From 4c88a97698d3eeccfffa50b19d312238bcd189ac Mon Sep 17 00:00:00 2001 From: Ivan Gnusarkov Date: Tue, 18 Jun 2024 14:10:35 +0700 Subject: [PATCH 001/132] added error message when db connection check fails --- scripts/check-db.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check-db.js b/scripts/check-db.js index 757843ac37..cdfeafa327 100644 --- a/scripts/check-db.js +++ b/scripts/check-db.js @@ -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); } } From 84e670858cb5d645cd5826159068391f92fca8ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=A2=E6=9D=A1?= <2959393+ccbikai@users.noreply.github.com> Date: Mon, 24 Jun 2024 19:40:04 +0800 Subject: [PATCH 002/132] Update Chinesi(zh-CN) translations --- src/lang/zh-CN.json | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/lang/zh-CN.json b/src/lang/zh-CN.json index bcdd3612b6..6eb49fcf4b 100644 --- a/src/lang/zh-CN.json +++ b/src/lang/zh-CN.json @@ -24,12 +24,12 @@ "label.cities": "市/县", "label.city": "市/县", "label.clear-all": "清除全部", - "label.compare": "Compare", + "label.compare": "对比", "label.confirm": "确认", "label.confirm-password": "确认密码", "label.contains": "包含", "label.continue": "继续", - "label.count": "Count", + "label.count": "数量", "label.countries": "国家/地区", "label.country": "国家/地区", "label.create": "创建", @@ -38,7 +38,7 @@ "label.create-user": "创建用户", "label.created": "已创建", "label.created-by": "创建者", - "label.current": "Current", + "label.current": "当前", "label.current-password": "目前密码", "label.custom-range": "自定义时间段", "label.dashboard": "仪表板", @@ -65,12 +65,12 @@ "label.edit-dashboard": "编辑仪表板", "label.edit-member": "编辑成员", "label.enable-share-url": "启用共享链接", - "label.end-step": "End Step", - "label.entry": "Entry URL", + "label.end-step": "最后一步", + "label.entry": "首次访问 URL", "label.event": "事件", "label.event-data": "事件数据", "label.events": "行为类别", - "label.exit": "Exit URL", + "label.exit": "退出 URL", "label.false": "否", "label.field": "字段", "label.fields": "字段", @@ -80,13 +80,13 @@ "label.filters": "筛选", "label.funnel": "分析", "label.funnel-description": "了解用户的转换率和退出率。", - "label.goal": "Goal", - "label.goals": "Goals", - "label.goals-description": "Track your goals for pageviews and events.", + "label.goal": "目标", + "label.goals": "目标", + "label.goals-description": "追踪您的页面浏览量和事件目标。", "label.greater-than": "大于", "label.greater-than-equals": "大于或等于", - "label.host": "Host", - "label.hosts": "Hosts", + "label.host": "主机", + "label.hosts": "主机", "label.insights": "见解", "label.insights-description": "通过使用筛选器和划分时间段来更深入地研究数据。", "label.is": "等于", @@ -95,8 +95,8 @@ "label.is-set": "已设置", "label.join": "加入", "label.join-team": "加入团队", - "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey": "轨迹", + "label.journey-description": "了解用户如何浏览您网站的哪些页面。", "label.language": "语言", "label.languages": "语言", "label.laptop": "笔记本", @@ -110,7 +110,7 @@ "label.login": "登录", "label.logout": "退出", "label.manage": "管理", - "label.manager": "Manager", + "label.manager": "管理者", "label.max": "最大", "label.member": "成员", "label.members": "成员", @@ -123,7 +123,7 @@ "label.new-password": "新密码", "label.none": "无", "label.number-of-records": "{x} {x, plural, one {record} other {records}}", - "label.ok": "OK", + "label.ok": "好的", "label.os": "操作系统", "label.overview": "概览", "label.owner": "所有者", @@ -133,11 +133,11 @@ "label.pages": "网页", "label.password": "密码", "label.powered-by": "由 {name} 提供支持", - "label.previous": "Previous", - "label.previous-period": "Previous period", - "label.previous-year": "Previous year", + "label.previous": "之前", + "label.previous-period": "前一周期", + "label.previous-year": "前一年", "label.profile": "个人资料", - "label.property": "Property", + "label.property": "属性", "label.queries": "查询", "label.query": "查询", "label.query-parameters": "查询参数", @@ -169,13 +169,13 @@ "label.settings": "设置", "label.share-url": "共享链接", "label.single-day": "单日", - "label.start-step": "Start Step", + "label.start-step": "第一步", "label.steps": "步骤", "label.sum": "总和", "label.tablet": "平板", "label.team": "团队", "label.team-id": "团队 ID", - "label.team-manager": "Team manager", + "label.team-manager": "团队管理", "label.team-member": "团队成员", "label.team-name": "团队名称", "label.team-owner": "团队所有者", @@ -225,7 +225,7 @@ "label.yesterday": "昨天", "message.action-confirmation": "在下面的框中输入 {confirmation} 以确认。", "message.active-users": "当前在线 {x} 人", - "message.collected-data": "Collected data", + "message.collected-data": "收集数据", "message.confirm-delete": "你确定要删除 {target} 吗?", "message.confirm-leave": "你确定要离开 {target} 吗?", "message.confirm-remove": "您确定要移除 {target} ?", From 43f231eca4d631fe22ea3e4e0b3a8e646d31da7f Mon Sep 17 00:00:00 2001 From: Yannick Decat <17177411+mho22@users.noreply.github.com> Date: Tue, 25 Jun 2024 15:33:19 +0200 Subject: [PATCH 003/132] Updated start-env with new NextStartOptions Corrected error with port and hostname argument and removed unneeded array. --- scripts/start-env.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/start-env.js b/scripts/start-env.js index e9fe2a4b4e..a2f2b43fc1 100644 --- a/scripts/start-env.js +++ b/scripts/start-env.js @@ -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' }); From e6d4c536aed693f0fa9c32d6cff7df2855a66a2a Mon Sep 17 00:00:00 2001 From: Gerard Nesta Date: Thu, 27 Jun 2024 12:35:14 +0200 Subject: [PATCH 004/132] Update ca-ES.json --- src/lang/ca-ES.json | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/lang/ca-ES.json b/src/lang/ca-ES.json index 5824492c3e..219fa9807e 100644 --- a/src/lang/ca-ES.json +++ b/src/lang/ca-ES.json @@ -24,12 +24,12 @@ "label.cities": "Ciutats", "label.city": "Ciutat", "label.clear-all": "Netejar tot", - "label.compare": "Compare", + "label.compare": "Comparar", "label.confirm": "Confirmar", "label.confirm-password": "Confirma la contrasenya", "label.contains": "Conté", "label.continue": "Continuar", - "label.count": "Count", + "label.count": "Recompte", "label.countries": "Països", "label.country": "País", "label.create": "Crear", @@ -38,7 +38,7 @@ "label.create-user": "Crear usuari", "label.created": "Creat", "label.created-by": "Creat Per", - "label.current": "Current", + "label.current": "Actual", "label.current-password": "Contrasenya actual", "label.custom-range": "Rang personalitzat", "label.dashboard": "Panell", @@ -65,12 +65,12 @@ "label.edit-dashboard": "Edita panell", "label.edit-member": "Edita membre", "label.enable-share-url": "Activa l'enllaç per compartir", - "label.end-step": "End Step", - "label.entry": "Entry URL", + "label.end-step": "Pas Final", + "label.entry": "URL d'entrada", "label.event": "Esdeveniment", "label.event-data": "Dades de l'esdeveniment", "label.events": "Esdeveniments", - "label.exit": "Exit URL", + "label.exit": "URL de sortida", "label.false": "Fals", "label.field": "Camp", "label.fields": "Camps", @@ -80,13 +80,13 @@ "label.filters": "Filtres", "label.funnel": "Embut", "label.funnel-description": "Entengui la taxa de conversió i abandonament dels usuaris.", - "label.goal": "Goal", - "label.goals": "Goals", - "label.goals-description": "Track your goals for pageviews and events.", + "label.goal": "Meta", + "label.goals": "Metes", + "label.goals-description": "Feu un seguiment de les seves metes per a pàgines vistes i esdeveniments.", "label.greater-than": "Més gran que", "label.greater-than-equals": "Més gran que o igual a", - "label.host": "Host", - "label.hosts": "Hosts", + "label.host": "Amfitrió", + "label.hosts": "Amfitrions", "label.insights": "Insights", "label.insights-description": "Aprofundeixi en les seves dades mitjançant l'ús de segments i filtres.", "label.is": "És igual a", @@ -95,8 +95,8 @@ "label.is-set": "Està establert", "label.join": "Unir", "label.join-team": "Unir-se al equip", - "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey": "Trajecte", + "label.journey-description": "Entengui com naveguen els usuaris pel seu lloc web.", "label.language": "Idioma", "label.languages": "Idiomes", "label.laptop": "Portàtil", @@ -122,7 +122,7 @@ "label.name": "Nom", "label.new-password": "Contrasenya nova", "label.none": "Cap", - "label.number-of-records": "{x} {x, plural, one {record} other {records}}", + "label.number-of-records": "{x} {x, plural, one {registre} other {registres}}", "label.ok": "OK", "label.os": "SO", "label.overview": "Resum", @@ -133,11 +133,11 @@ "label.pages": "Pàgines", "label.password": "Contrasenya", "label.powered-by": "Funciona amb {name}", - "label.previous": "Previous", - "label.previous-period": "Previous period", - "label.previous-year": "Previous year", + "label.previous": "Anterior", + "label.previous-period": "Període anterior", + "label.previous-year": "Any anterior", "label.profile": "Perfil", - "label.property": "Property", + "label.property": "Propietat", "label.queries": "Consultes", "label.query": "Consulta", "label.query-parameters": "Paràmetres de consulta", @@ -169,7 +169,7 @@ "label.settings": "Configuració", "label.share-url": "Enllaç per compartir", "label.single-day": "Un sol dia", - "label.start-step": "Start Step", + "label.start-step": "Pas inicial", "label.steps": "Pasos", "label.sum": "Suma", "label.tablet": "Tauleta", @@ -214,7 +214,7 @@ "label.view-details": "Veure els detalls", "label.view-only": "Només veure", "label.views": "Vistes", - "label.views-per-visit": "Views per visit", + "label.views-per-visit": "Vistes per visita", "label.visit-duration": "Temps mitjà de visita", "label.visitors": "Visitants", "label.visits": "Visites", @@ -225,7 +225,7 @@ "label.yesterday": "Ahir", "message.action-confirmation": "Escrigui {confirmation} al cuadre inferior per confirmar.", "message.active-users": "{x} {x, plural, one {visitant actual} other {visitants actuals}}", - "message.collected-data": "Collected data", + "message.collected-data": "Dades recol·lectades", "message.confirm-delete": "Segur que vol esborrar {target}?", "message.confirm-leave": "Segur que vol abandonar {target}?", "message.confirm-remove": "Segur que vol eliminar {target}?", From 5fc596ff411116491f4abbae93522d8881e159f4 Mon Sep 17 00:00:00 2001 From: Gerard Nesta Date: Thu, 27 Jun 2024 12:39:21 +0200 Subject: [PATCH 005/132] Update ca-ES.json --- src/lang/ca-ES.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/ca-ES.json b/src/lang/ca-ES.json index 219fa9807e..dae977fc2c 100644 --- a/src/lang/ca-ES.json +++ b/src/lang/ca-ES.json @@ -263,5 +263,5 @@ "message.user-deleted": "Usuari eliminat.", "message.viewed-page": "Pàgina vista", "message.visitor-log": "Visitant de {country} usant {browser} a {os} {device}", - "message.visitors-dropped-off": "Els visitants han sortit" + "message.visitors-dropped-off": "Visitants han sortit" } From caaa87c0a749474cb4cdb4f01d7991611ae1e31d Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Thu, 27 Jun 2024 20:12:32 -0700 Subject: [PATCH 006/132] Updated packages and lang files. --- package.json | 8 +- public/intl/messages/ca-ES.json | 46 ++-- public/intl/messages/ko-KR.json | 454 ++++++++++++++++---------------- public/intl/messages/zh-CN.json | 44 ++-- yarn.lock | 216 +++++++-------- 5 files changed, 379 insertions(+), 389 deletions(-) diff --git a/package.json b/package.json index d002388f8a..9ef6f738bc 100644 --- a/package.json +++ b/package.json @@ -64,9 +64,9 @@ ".next/cache" ], "dependencies": { - "@clickhouse/client": "^1.0.2", + "@clickhouse/client": "^1.2.0", "@fontsource/inter": "^4.5.15", - "@prisma/client": "5.14.0", + "@prisma/client": "5.16.1", "@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.1", "react": "^18.2.0", "react-basics": "^0.123.0", "react-beautiful-dnd": "^13.1.0", diff --git a/public/intl/messages/ca-ES.json b/public/intl/messages/ca-ES.json index f9b63535f0..dc7b27a87b 100644 --- a/public/intl/messages/ca-ES.json +++ b/public/intl/messages/ca-ES.json @@ -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" } ] } diff --git a/public/intl/messages/ko-KR.json b/public/intl/messages/ko-KR.json index da196a3c96..deab1408bc 100644 --- a/public/intl/messages/ko-KR.json +++ b/public/intl/messages/ko-KR.json @@ -2,7 +2,7 @@ "label.access-code": [ { "type": 0, - "value": "Access code" + "value": "액세스 코드" } ], "label.actions": [ @@ -14,31 +14,31 @@ "label.activity-log": [ { "type": 0, - "value": "Activity log" + "value": "활동 기록" } ], "label.add": [ { "type": 0, - "value": "Add" + "value": "추가" } ], "label.add-description": [ { "type": 0, - "value": "Add description" + "value": "설명 추가" } ], "label.add-member": [ { "type": 0, - "value": "Add member" + "value": "멤버 추가" } ], "label.add-step": [ { "type": 0, - "value": "Add step" + "value": "단계 추가" } ], "label.add-website": [ @@ -56,7 +56,7 @@ "label.after": [ { "type": 0, - "value": "After" + "value": "이후" } ], "label.all": [ @@ -68,19 +68,19 @@ "label.all-time": [ { "type": 0, - "value": "All time" + "value": "전체 시간" } ], "label.analytics": [ { "type": 0, - "value": "Analytics" + "value": "분석" } ], "label.average": [ { "type": 0, - "value": "Average" + "value": "평균" } ], "label.back": [ @@ -92,7 +92,7 @@ "label.before": [ { "type": 0, - "value": "Before" + "value": "이전" } ], "label.bounce-rate": [ @@ -104,13 +104,13 @@ "label.breakdown": [ { "type": 0, - "value": "Breakdown" + "value": "세부 사항" } ], "label.browser": [ { "type": 0, - "value": "Browser" + "value": "브라우저" } ], "label.browsers": [ @@ -128,37 +128,37 @@ "label.change-password": [ { "type": 0, - "value": "비밀번호 변경" + "value": "비밀번호 변경하기" } ], "label.cities": [ { "type": 0, - "value": "Cities" + "value": "도시" } ], "label.city": [ { "type": 0, - "value": "City" + "value": "도시" } ], "label.clear-all": [ { "type": 0, - "value": "Clear all" + "value": "모두 지우기" } ], "label.compare": [ { "type": 0, - "value": "Compare" + "value": "비교" } ], "label.confirm": [ { "type": 0, - "value": "Confirm" + "value": "확인" } ], "label.confirm-password": [ @@ -170,19 +170,19 @@ "label.contains": [ { "type": 0, - "value": "Contains" + "value": "포함" } ], "label.continue": [ { "type": 0, - "value": "Continue" + "value": "계속" } ], "label.count": [ { "type": 0, - "value": "Count" + "value": "수" } ], "label.countries": [ @@ -194,49 +194,49 @@ "label.country": [ { "type": 0, - "value": "Country" + "value": "국가" } ], "label.create": [ { "type": 0, - "value": "Create" + "value": "생성" } ], "label.create-report": [ { "type": 0, - "value": "Create report" + "value": "리포트 생성" } ], "label.create-team": [ { "type": 0, - "value": "Create team" + "value": "팀 생성" } ], "label.create-user": [ { "type": 0, - "value": "Create user" + "value": "사용자 생성" } ], "label.created": [ { "type": 0, - "value": "Created" + "value": "생성됨" } ], "label.created-by": [ { "type": 0, - "value": "Created By" + "value": "작성자" } ], "label.current": [ { "type": 0, - "value": "Current" + "value": "현재" } ], "label.current-password": [ @@ -260,13 +260,13 @@ "label.data": [ { "type": 0, - "value": "Data" + "value": "데이터" } ], "label.date": [ { "type": 0, - "value": "Date" + "value": "날짜" } ], "label.date-range": [ @@ -278,7 +278,7 @@ "label.day": [ { "type": 0, - "value": "Day" + "value": "일" } ], "label.default-date-range": [ @@ -296,19 +296,19 @@ "label.delete-report": [ { "type": 0, - "value": "Delete report" + "value": "리포트 삭제" } ], "label.delete-team": [ { "type": 0, - "value": "Delete team" + "value": "팀 삭제" } ], "label.delete-user": [ { "type": 0, - "value": "Delete user" + "value": "사용자 삭제" } ], "label.delete-website": [ @@ -320,7 +320,7 @@ "label.description": [ { "type": 0, - "value": "Description" + "value": "설명" } ], "label.desktop": [ @@ -332,13 +332,13 @@ "label.details": [ { "type": 0, - "value": "Details" + "value": "세부 사항" } ], "label.device": [ { "type": 0, - "value": "Device" + "value": "기기" } ], "label.devices": [ @@ -356,7 +356,7 @@ "label.does-not-contain": [ { "type": 0, - "value": "Does not contain" + "value": "포함하지 않음" } ], "label.domain": [ @@ -368,7 +368,7 @@ "label.dropoff": [ { "type": 0, - "value": "Dropoff" + "value": "이탈" } ], "label.edit": [ @@ -380,13 +380,13 @@ "label.edit-dashboard": [ { "type": 0, - "value": "Edit dashboard" + "value": "대시보드 편집" } ], "label.edit-member": [ { "type": 0, - "value": "Edit member" + "value": "회원 편집" } ], "label.enable-share-url": [ @@ -398,25 +398,25 @@ "label.end-step": [ { "type": 0, - "value": "End Step" + "value": "종료 단계" } ], "label.entry": [ { "type": 0, - "value": "Entry URL" + "value": "입장 URL" } ], "label.event": [ { "type": 0, - "value": "Event" + "value": "이벤트" } ], "label.event-data": [ { "type": 0, - "value": "Event data" + "value": "이벤트 데이터" } ], "label.events": [ @@ -428,31 +428,31 @@ "label.exit": [ { "type": 0, - "value": "Exit URL" + "value": "퇴장 URL" } ], "label.false": [ { "type": 0, - "value": "False" + "value": "거짓" } ], "label.field": [ { "type": 0, - "value": "Field" + "value": "필드" } ], "label.fields": [ { "type": 0, - "value": "Fields" + "value": "필드" } ], "label.filter": [ { "type": 0, - "value": "Filter" + "value": "필터" } ], "label.filter-combined": [ @@ -470,133 +470,133 @@ "label.filters": [ { "type": 0, - "value": "Filters" + "value": "필터" } ], "label.funnel": [ { "type": 0, - "value": "Funnel" + "value": "퍼널" } ], "label.funnel-description": [ { "type": 0, - "value": "Understand the conversion and drop-off rate of users." + "value": "사용자 전환율 및 이탈률을 살펴보세요." } ], "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": [ { "type": 0, - "value": "Greater than" + "value": "이상" } ], "label.greater-than-equals": [ { "type": 0, - "value": "Greater than or equals" + "value": "이상" } ], "label.host": [ { "type": 0, - "value": "Host" + "value": "호스트" } ], "label.hosts": [ { "type": 0, - "value": "Hosts" + "value": "호스트" } ], "label.insights": [ { "type": 0, - "value": "Insights" + "value": "인사이트" } ], "label.insights-description": [ { "type": 0, - "value": "Dive deeper into your data by using segments and filters." + "value": "세그먼트 및 필터를 사용하여 데이터를 더 자세히 살펴보세요." } ], "label.is": [ { "type": 0, - "value": "Is" + "value": "해당" } ], "label.is-not": [ { "type": 0, - "value": "Is not" + "value": "해당하지 않음" } ], "label.is-not-set": [ { "type": 0, - "value": "Is not set" + "value": "설정되지 않음" } ], "label.is-set": [ { "type": 0, - "value": "Is set" + "value": "설정됨" } ], "label.join": [ { "type": 0, - "value": "Join" + "value": "가입" } ], "label.join-team": [ { "type": 0, - "value": "Join team" + "value": "팀 가입" } ], "label.journey": [ { "type": 0, - "value": "Journey" + "value": "여정" } ], "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "사용자가 웹사이트를 탐색하는 경로를 살펴보세요." } ], "label.language": [ { "type": 0, - "value": "Language" + "value": "언어" } ], "label.languages": [ { "type": 0, - "value": "Languages" + "value": "언어" } ], "label.laptop": [ @@ -616,7 +616,7 @@ }, { "type": 0, - "value": " 일간" + "value": " 일" } ], "label.last-hours": [ @@ -636,7 +636,7 @@ "label.last-months": [ { "type": 0, - "value": "Last " + "value": "최근 " }, { "type": 1, @@ -644,31 +644,31 @@ }, { "type": 0, - "value": " months" + "value": " 개월" } ], "label.leave": [ { "type": 0, - "value": "Leave" + "value": "떠나기" } ], "label.leave-team": [ { "type": 0, - "value": "Leave team" + "value": "팀 떠나기" } ], "label.less-than": [ { "type": 0, - "value": "Less than" + "value": "미만" } ], "label.less-than-equals": [ { "type": 0, - "value": "Less than or equals" + "value": "이하" } ], "label.login": [ @@ -686,37 +686,37 @@ "label.manage": [ { "type": 0, - "value": "Manage" + "value": "관리" } ], "label.manager": [ { "type": 0, - "value": "Manager" + "value": "관리자" } ], "label.max": [ { "type": 0, - "value": "Max" + "value": "최대" } ], "label.member": [ { "type": 0, - "value": "Member" + "value": "멤버" } ], "label.members": [ { "type": 0, - "value": "Members" + "value": "멤버" } ], "label.min": [ { "type": 0, - "value": "Min" + "value": "최소" } ], "label.mobile": [ @@ -734,13 +734,13 @@ "label.my-account": [ { "type": 0, - "value": "My account" + "value": "내 계정" } ], "label.my-websites": [ { "type": 0, - "value": "My websites" + "value": "내 웹사이트" } ], "label.name": [ @@ -758,7 +758,7 @@ "label.none": [ { "type": 0, - "value": "None" + "value": "없음" } ], "label.number-of-records": [ @@ -785,7 +785,7 @@ "value": [ { "type": 0, - "value": "records" + "value": "레코드" } ] } @@ -798,31 +798,35 @@ "label.ok": [ { "type": 0, - "value": "OK" + "value": "확인" } ], "label.os": [ { "type": 0, - "value": "OS" + "value": "운영체제" } ], "label.overview": [ { "type": 0, - "value": "Overview" + "value": "개요" } ], "label.owner": [ { "type": 0, - "value": "Owner" + "value": "소유자" } ], "label.page-of": [ + { + "type": 1, + "value": "total" + }, { "type": 0, - "value": "Page " + "value": " 중 " }, { "type": 1, @@ -830,23 +834,19 @@ }, { "type": 0, - "value": " of " - }, - { - "type": 1, - "value": "total" + "value": " 페이지" } ], "label.page-views": [ { "type": 0, - "value": "페이지 뷰(PV)" + "value": "페이지 뷰" } ], "label.pageTitle": [ { "type": 0, - "value": "Page title" + "value": "페이지 제목" } ], "label.pages": [ @@ -878,19 +878,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": [ @@ -902,25 +902,25 @@ "label.property": [ { "type": 0, - "value": "Property" + "value": "속성" } ], "label.queries": [ { "type": 0, - "value": "Queries" + "value": "쿼리" } ], "label.query": [ { "type": 0, - "value": "Query" + "value": "쿼리" } ], "label.query-parameters": [ { "type": 0, - "value": "Query parameters" + "value": "쿼리 매개변수" } ], "label.realtime": [ @@ -932,7 +932,7 @@ "label.referrer": [ { "type": 0, - "value": "Referrer" + "value": "리퍼러" } ], "label.referrers": [ @@ -950,37 +950,37 @@ "label.regenerate": [ { "type": 0, - "value": "Regenerate" + "value": "다시 생성" } ], "label.region": [ { "type": 0, - "value": "Region" + "value": "지역" } ], "label.regions": [ { "type": 0, - "value": "Regions" + "value": "지역" } ], "label.remove": [ { "type": 0, - "value": "Remove" + "value": "제거" } ], "label.remove-member": [ { "type": 0, - "value": "Remove member" + "value": "멤버 제거" } ], "label.reports": [ { "type": 0, - "value": "Reports" + "value": "리포트" } ], "label.required": [ @@ -998,31 +998,31 @@ "label.reset-website": [ { "type": 0, - "value": "Reset statistics" + "value": "웹사이트 초기화" } ], "label.retention": [ { "type": 0, - "value": "Retention" + "value": "리텐션" } ], "label.retention-description": [ { "type": 0, - "value": "Measure your website stickiness by tracking how often users return." + "value": "사용자가 얼마나 자주 돌아오는지를 추적하여 웹사이트의 리텐션을 측정하십시오." } ], "label.role": [ { "type": 0, - "value": "Role" + "value": "역할" } ], "label.run-query": [ { "type": 0, - "value": "Run query" + "value": "쿼리 실행" } ], "label.save": [ @@ -1034,43 +1034,43 @@ "label.screens": [ { "type": 0, - "value": "Screens" + "value": "스크린" } ], "label.search": [ { "type": 0, - "value": "Search" + "value": "검색" } ], "label.select": [ { "type": 0, - "value": "Select" + "value": "선택" } ], "label.select-date": [ { "type": 0, - "value": "Select date" + "value": "날짜 선택" } ], "label.select-role": [ { "type": 0, - "value": "Select role" + "value": "역할 선택" } ], "label.select-website": [ { "type": 0, - "value": "Select website" + "value": "웹사이트 선택" } ], "label.sessions": [ { "type": 0, - "value": "Sessions" + "value": "세션" } ], "label.settings": [ @@ -1094,19 +1094,19 @@ "label.start-step": [ { "type": 0, - "value": "Start Step" + "value": "시작 단계" } ], "label.steps": [ { "type": 0, - "value": "Steps" + "value": "단계" } ], "label.sum": [ { "type": 0, - "value": "Sum" + "value": "합계" } ], "label.tablet": [ @@ -1118,61 +1118,61 @@ "label.team": [ { "type": 0, - "value": "Team" + "value": "팀" } ], "label.team-id": [ { "type": 0, - "value": "Team ID" + "value": "팀 ID" } ], "label.team-manager": [ { "type": 0, - "value": "Team manager" + "value": "팀 관리자" } ], "label.team-member": [ { "type": 0, - "value": "Team member" + "value": "팀 멤버" } ], "label.team-name": [ { "type": 0, - "value": "Team name" + "value": "팀 이름" } ], "label.team-owner": [ { "type": 0, - "value": "Team owner" + "value": "팀 소유자" } ], "label.team-view-only": [ { "type": 0, - "value": "Team view only" + "value": "팀 보기 전용" } ], "label.team-websites": [ { "type": 0, - "value": "Team websites" + "value": "팀 웹사이트" } ], "label.teams": [ { "type": 0, - "value": "Teams" + "value": "팀" } ], "label.theme": [ { "type": 0, - "value": "Theme" + "value": "테마" } ], "label.this-month": [ @@ -1202,7 +1202,7 @@ "label.title": [ { "type": 0, - "value": "Title" + "value": "제목" } ], "label.today": [ @@ -1214,19 +1214,19 @@ "label.toggle-charts": [ { "type": 0, - "value": "Toggle charts" + "value": "차트 전환" } ], "label.total": [ { "type": 0, - "value": "Total" + "value": "합계" } ], "label.total-records": [ { "type": 0, - "value": "Total records" + "value": "총 레코드" } ], "label.tracking-code": [ @@ -1238,31 +1238,31 @@ "label.transfer": [ { "type": 0, - "value": "Transfer" + "value": "전송" } ], "label.transfer-website": [ { "type": 0, - "value": "Transfer website" + "value": "웹사이트 전송" } ], "label.true": [ { "type": 0, - "value": "True" + "value": "참" } ], "label.type": [ { "type": 0, - "value": "Type" + "value": "유형" } ], "label.unique": [ { "type": 0, - "value": "Unique" + "value": "고유" } ], "label.unique-visitors": [ @@ -1280,13 +1280,13 @@ "label.untitled": [ { "type": 0, - "value": "Untitled" + "value": "제목 없음" } ], "label.update": [ { "type": 0, - "value": "Update" + "value": "업데이트" } ], "label.url": [ @@ -1298,13 +1298,13 @@ "label.urls": [ { "type": 0, - "value": "URLs" + "value": "URL" } ], "label.user": [ { "type": 0, - "value": "User" + "value": "사용자" } ], "label.username": [ @@ -1316,7 +1316,7 @@ "label.users": [ { "type": 0, - "value": "Users" + "value": "사용자" } ], "label.utm": [ @@ -1328,19 +1328,19 @@ "label.utm-description": [ { "type": 0, - "value": "Track your campaigns through UTM parameters." + "value": "UTM 매개변수를 통해 캠페인을 추적합니다." } ], "label.value": [ { "type": 0, - "value": "Value" + "value": "값" } ], "label.view": [ { "type": 0, - "value": "View" + "value": "보기" } ], "label.view-details": [ @@ -1352,7 +1352,7 @@ "label.view-only": [ { "type": 0, - "value": "View only" + "value": "보기 전용" } ], "label.views": [ @@ -1364,7 +1364,7 @@ "label.views-per-visit": [ { "type": 0, - "value": "Views per visit" + "value": "방문당 조회수" } ], "label.visit-duration": [ @@ -1382,19 +1382,19 @@ "label.visits": [ { "type": 0, - "value": "Visits" + "value": "방문" } ], "label.website": [ { "type": 0, - "value": "Website" + "value": "웹사이트" } ], "label.website-id": [ { "type": 0, - "value": "Website ID" + "value": "웹사이트 ID" } ], "label.websites": [ @@ -1406,19 +1406,19 @@ "label.window": [ { "type": 0, - "value": "Window" + "value": "창" } ], "label.yesterday": [ { "type": 0, - "value": "Yesterday" + "value": "어제" } ], "message.action-confirmation": [ { "type": 0, - "value": "Type " + "value": "확인을 위해 아래 상자에 " }, { "type": 1, @@ -1426,7 +1426,7 @@ }, { "type": 0, - "value": " in the box below to confirm." + "value": "을(를) 입력하십시오." } ], "message.active-users": [ @@ -1442,7 +1442,7 @@ "message.collected-data": [ { "type": 0, - "value": "Collected data" + "value": "수집된 데이터" } ], "message.confirm-delete": [ @@ -1456,57 +1456,45 @@ } ], "message.confirm-leave": [ - { - "type": 0, - "value": "Are you sure you want to leave " - }, { "type": 1, "value": "target" }, { "type": 0, - "value": "?" + "value": "을(를) 떠나시겠습니까?" } ], "message.confirm-remove": [ - { - "type": 0, - "value": "Are you sure you want to remove " - }, { "type": 1, "value": "target" }, { "type": 0, - "value": "?" + "value": "을(를) 제거하시겠습니까?" } ], "message.confirm-reset": [ - { - "type": 0, - "value": "Are your sure you want to reset " - }, { "type": 1, "value": "target" }, { "type": 0, - "value": "'s statistics?" + "value": "을(를) 초기화하시겠습니까?" } ], "message.delete-team-warning": [ { "type": 0, - "value": "Deleting a team will also delete all team websites." + "value": "팀을 삭제하면 팀에 등록된 모든 웹사이트도 삭제됩니다." } ], "message.delete-website-warning": [ { "type": 0, - "value": "관련된 모든 데이터도 삭제됩니다." + "value": "관련된 모든 데이터가 삭제됩니다." } ], "message.error": [ @@ -1522,7 +1510,7 @@ }, { "type": 0, - "value": " on " + "value": " - " }, { "type": 1, @@ -1538,7 +1526,7 @@ "message.incorrect-username-password": [ { "type": 0, - "value": "사용자 이름/비밀번호가 잘못되었습니다.." + "value": "사용자 이름/비밀번호가 잘못되었습니다." } ], "message.invalid-domain": [ @@ -1550,7 +1538,7 @@ "message.min-password-length": [ { "type": 0, - "value": "Minimum length of " + "value": "최소 길이는 " }, { "type": 1, @@ -1558,21 +1546,17 @@ }, { "type": 0, - "value": " characters" + "value": "자입니다" } ], "message.new-version-available": [ { "type": 0, - "value": "A new version of Umami " + "value": "새 버전이 사용 가능합니다! - Umami " }, { "type": 1, "value": "version" - }, - { - "type": 0, - "value": " is available!" } ], "message.no-data-available": [ @@ -1584,7 +1568,7 @@ "message.no-event-data": [ { "type": 0, - "value": "No event data is available." + "value": "사용 가능한 이벤트 데이터가 없습니다." } ], "message.no-match-password": [ @@ -1596,31 +1580,31 @@ "message.no-results-found": [ { "type": 0, - "value": "No results were found." + "value": "결과를 찾을 수 없습니다." } ], "message.no-team-websites": [ { "type": 0, - "value": "This team does not have any websites." + "value": "이 팀에는 웹사이트가 없습니다." } ], "message.no-teams": [ { "type": 0, - "value": "You have not created any teams." + "value": "생성된 팀이 없습니다." } ], "message.no-users": [ { "type": 0, - "value": "There are no users." + "value": "사용자가 없습니다." } ], "message.no-websites-configured": [ { "type": 0, - "value": "구성된 웹 사이트가 없습니다." + "value": "설정된 웹사이트가 없습니다." } ], "message.page-not-found": [ @@ -1632,7 +1616,7 @@ "message.reset-website": [ { "type": 0, - "value": "To reset this website, type " + "value": "이 웹사이트를 초기화하려면, 아래 상자에 " }, { "type": 1, @@ -1640,13 +1624,13 @@ }, { "type": 0, - "value": " in the box below to confirm." + "value": "을(를) 입력하십시오." } ], "message.reset-website-warning": [ { "type": 0, - "value": "All statistics for this website will be deleted, but your tracking code will remain intact." + "value": "이 웹사이트의 모든 통계가 삭제되지만 설정은 그대로 유지됩니다." } ], "message.saved": [ @@ -1658,115 +1642,121 @@ "message.share-url": [ { "type": 0, - "value": "이것은 " - }, - { - "type": 1, - "value": "target" - }, - { - "type": 0, - "value": "의 공개적으로 공유된 URL입니다." + "value": "아래 링크를 통해 웹사이트의 통계를 누구나 볼 수 있습니다." } ], "message.team-already-member": [ { "type": 0, - "value": "You are already a member of the team." + "value": "이미 팀의 회원입니다." } ], "message.team-not-found": [ { "type": 0, - "value": "Team not found." + "value": "팀을 찾을 수 없습니다." } ], "message.team-websites-info": [ { "type": 0, - "value": "Websites can be viewed by anyone on the team." + "value": "웹사이트는 팀의 누구나 볼 수 있습니다." } ], "message.tracking-code": [ { "type": 0, - "value": "추적 코드" + "value": "이 웹사이트의 통계를 추적하려면, 다음 코드를 HTML의 " + }, + { + "children": [ + { + "type": 0, + "value": "..." + } + ], + "type": 8, + "value": "head" + }, + { + "type": 0, + "value": " 섹션에 추가하십시오." } ], "message.transfer-team-website-to-user": [ { "type": 0, - "value": "Transfer this website to your account?" + "value": "이 웹사이트를 당신의 계정으로 전송하시겠습니까?" } ], "message.transfer-user-website-to-team": [ { "type": 0, - "value": "Select the team to transfer this website to." + "value": "이 웹사이트를 전송받을 팀을 선택하십시오." } ], "message.transfer-website": [ { "type": 0, - "value": "Transfer website ownership to your account or another team." + "value": "웹사이트 소유권을 계정이나 다른 팀으로 전송합니다." } ], "message.triggered-event": [ { "type": 0, - "value": "Triggered event" + "value": "트리거된 이벤트" } ], "message.user-deleted": [ { "type": 0, - "value": "User deleted." + "value": "사용자가 삭제되었습니다." } ], "message.viewed-page": [ { "type": 0, - "value": "Viewed page" + "value": "페이지 조회" } ], "message.visitor-log": [ { "type": 1, - "value": "os" + "value": "country" }, { "type": 0, - "value": " " + "value": "의 " }, { "type": 1, - "value": "device" + "value": "browser" }, { "type": 0, - "value": "에서 " + "value": " 브라우저를 사용하는 " }, { "type": 1, - "value": "browser" + "value": "os" }, { "type": 0, - "value": "을(를) 사용하는 " + "value": " " }, { "type": 1, - "value": "country" + "value": "device" }, { "type": 0, - "value": "의 방문자" + "value": " 방문자" } ], "message.visitors-dropped-off": [ { "type": 0, - "value": "Visitors dropped off" + "value": "방문자가 이탈했습니다" } ] } diff --git a/public/intl/messages/zh-CN.json b/public/intl/messages/zh-CN.json index e66a8eb158..0b7da8a52e 100644 --- a/public/intl/messages/zh-CN.json +++ b/public/intl/messages/zh-CN.json @@ -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": [ diff --git a/yarn.lock b/yarn.lock index 9b29fa7467..90f3da53ec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1206,17 +1206,17 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@clickhouse/client-common@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@clickhouse/client-common/-/client-common-1.0.2.tgz#0fe0a4b33101c08d85c1279e4d74b2a92d42d996" - integrity sha512-5oI2URFsXlzoysv5lAxoTUAnAHjXnaJ+1Jz3HUARR06Hkbr1sN0pGxfGwgjEd8E/lI4+UNdNEZicG2rlFnWSaA== +"@clickhouse/client-common@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@clickhouse/client-common/-/client-common-1.2.0.tgz#8845d373a14c6076d59118a0cb58afad84687372" + integrity sha512-VfA/C/tVJ2eNe72CaQ7eXmai+yqFEvZjQZiNtvJoOMLP+Vtb6DzqH9nfkgsiHHMhUhhclvt2mFh6+euk1Ea5wA== -"@clickhouse/client@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@clickhouse/client/-/client-1.0.2.tgz#7d9675e697ce697f1e6777f4c66ca6d3384e7325" - integrity sha512-PaK0GLjIrlCpXevrp9gliOrurna6MjMMFBgZhDh6Zup8IuJCjQru4LkNsWUl3hJ2nua6+Ygag14iB8ILbeaIjg== +"@clickhouse/client@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@clickhouse/client/-/client-1.2.0.tgz#83aa3b69635e19c94a975ee7ec9a68843915b94b" + integrity sha512-zMp2EhMfp1IrFKr/NjDwNiLsf7nq68nW8lGKszwFe7Iglc6Z5PY9ZA9Hd0XqAk75Q1NmFrkGCP1r3JCM1Nm1Bw== dependencies: - "@clickhouse/client-common" "1.0.2" + "@clickhouse/client-common" "1.2.0" "@colors/colors@1.5.0": version "1.5.0" @@ -2006,10 +2006,10 @@ resolved "https://registry.yarnpkg.com/@netlify/plugin-nextjs/-/plugin-nextjs-5.2.2.tgz#3c283d335001f9e0fbcb4db75557e5fe1660db72" integrity sha512-jV/P7o8+v1XaEGb7wvFfkF1fSLggAxjg7WYoBPkD3R93bsI6xmCDKBcUJ/6g7lqECRXt4dGKApSFtGk/pUmAHw== -"@next/env@14.2.3": - version "14.2.3" - resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.3.tgz#d6def29d1c763c0afb397343a15a82e7d92353a0" - integrity sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA== +"@next/env@14.2.4": + version "14.2.4" + resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.4.tgz#5546813dc4f809884a37d257b254a5ce1b0248d7" + integrity sha512-3EtkY5VDkuV2+lNmKlbkibIJxcO4oIHEhBWne6PaAp+76J9KoSsGvNikp6ivzAT8dhhBMYrm6op2pS1ApG0Hzg== "@next/eslint-plugin-next@14.2.3": version "14.2.3" @@ -2018,50 +2018,50 @@ dependencies: glob "10.3.10" -"@next/swc-darwin-arm64@14.2.3": - version "14.2.3" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.3.tgz#db1a05eb88c0224089b815ad10ac128ec79c2cdb" - integrity sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A== - -"@next/swc-darwin-x64@14.2.3": - version "14.2.3" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.3.tgz#a3f8af05b5f9a52ac3082e66ac29e125ab1d7b9c" - integrity sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA== - -"@next/swc-linux-arm64-gnu@14.2.3": - version "14.2.3" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.3.tgz#4e63f43879285b52554bfd39e6e0cc78a9b27bbf" - integrity sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA== - -"@next/swc-linux-arm64-musl@14.2.3": - version "14.2.3" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.3.tgz#ebdaed26214448b1e6f2c3e8b3cd29bfba387990" - integrity sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw== - -"@next/swc-linux-x64-gnu@14.2.3": - version "14.2.3" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.3.tgz#19e3bcc137c3b582a1ab867106817e5c90a20593" - integrity sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w== - -"@next/swc-linux-x64-musl@14.2.3": - version "14.2.3" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.3.tgz#794a539b98e064169cf0ff7741b2a4fb16adec7d" - integrity sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ== - -"@next/swc-win32-arm64-msvc@14.2.3": - version "14.2.3" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.3.tgz#eda9fa0fbf1ff9113e87ac2668ee67ce9e5add5a" - integrity sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A== - -"@next/swc-win32-ia32-msvc@14.2.3": - version "14.2.3" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.3.tgz#7c1190e3f640ab16580c6bdbd7d0e766b9920457" - integrity sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw== - -"@next/swc-win32-x64-msvc@14.2.3": - version "14.2.3" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.3.tgz#2be4e39ee25bfbd85be78eea17c0e7751dc4323c" - integrity sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA== +"@next/swc-darwin-arm64@14.2.4": + version "14.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.4.tgz#da9f04c34a3d5f0b8401ed745768420e4a604036" + integrity sha512-AH3mO4JlFUqsYcwFUHb1wAKlebHU/Hv2u2kb1pAuRanDZ7pD/A/KPD98RHZmwsJpdHQwfEc/06mgpSzwrJYnNg== + +"@next/swc-darwin-x64@14.2.4": + version "14.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.4.tgz#46dedb29ec5503bf171a72a3ecb8aac6e738e9d6" + integrity sha512-QVadW73sWIO6E2VroyUjuAxhWLZWEpiFqHdZdoQ/AMpN9YWGuHV8t2rChr0ahy+irKX5mlDU7OY68k3n4tAZTg== + +"@next/swc-linux-arm64-gnu@14.2.4": + version "14.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.4.tgz#c9697ab9eb422bd1d7ffd0eb0779cc2aefa9d4a1" + integrity sha512-KT6GUrb3oyCfcfJ+WliXuJnD6pCpZiosx2X3k66HLR+DMoilRb76LpWPGb4tZprawTtcnyrv75ElD6VncVamUQ== + +"@next/swc-linux-arm64-musl@14.2.4": + version "14.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.4.tgz#cbbceb2008571c743b5a310a488d2e166d200a75" + integrity sha512-Alv8/XGSs/ytwQcbCHwze1HmiIkIVhDHYLjczSVrf0Wi2MvKn/blt7+S6FJitj3yTlMwMxII1gIJ9WepI4aZ/A== + +"@next/swc-linux-x64-gnu@14.2.4": + version "14.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.4.tgz#d79184223f857bacffb92f643cb2943a43632568" + integrity sha512-ze0ShQDBPCqxLImzw4sCdfnB3lRmN3qGMB2GWDRlq5Wqy4G36pxtNOo2usu/Nm9+V2Rh/QQnrRc2l94kYFXO6Q== + +"@next/swc-linux-x64-musl@14.2.4": + version "14.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.4.tgz#6b6c3e5ac02ca5e63394d280ec8ee607491902df" + integrity sha512-8dwC0UJoc6fC7PX70csdaznVMNr16hQrTDAMPvLPloazlcaWfdPogq+UpZX6Drqb1OBlwowz8iG7WR0Tzk/diQ== + +"@next/swc-win32-arm64-msvc@14.2.4": + version "14.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.4.tgz#dbad3906e870dba84c5883d9d4c4838472e0697f" + integrity sha512-jxyg67NbEWkDyvM+O8UDbPAyYRZqGLQDTPwvrBBeOSyVWW/jFQkQKQ70JDqDSYg1ZDdl+E3nkbFbq8xM8E9x8A== + +"@next/swc-win32-ia32-msvc@14.2.4": + version "14.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.4.tgz#6074529b91ba49132922ce89a2e16d25d2ec235d" + integrity sha512-twrmN753hjXRdcrZmZttb/m5xaCBFa48Dt3FbeEItpJArxriYDunWxJn+QFXdJ3hPkm4u7CKxncVvnmgQMY1ag== + +"@next/swc-win32-x64-msvc@14.2.4": + version "14.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.4.tgz#e65a1c6539a671f97bb86d5183d6e3a1733c29c7" + integrity sha512-tkLrjBzqFTP8DVrAAQmZelEahfR9OxWpFR++vAI9FBhCiIxtwHwBHC23SBHCTURBtwB4kc/x44imVOnkKGNVGg== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -2089,51 +2089,51 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@prisma/client@5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.14.0.tgz#dadca5bb1137ddcebb454bbdaf89423823d3363f" - integrity sha512-akMSuyvLKeoU4LeyBAUdThP/uhVP3GuLygFE3MlYzaCb3/J8SfsYBE5PkaFuLuVpLyA6sFoW+16z/aPhNAESqg== +"@prisma/client@5.16.1": + version "5.16.1" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.16.1.tgz#65c5649b4701c097e7fa943c91a3140ce8bf053d" + integrity sha512-wM9SKQjF0qLxdnOZIVAIMKiz6Hu7vDt4FFAih85K1dk/Rr2mdahy6d3QP41K62N9O0DJJA//gUDA3Mp49xsKIg== -"@prisma/debug@5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-5.14.0.tgz#1227c705893c38284f7c63d72441480ebaa12605" - integrity sha512-iq56qBZuFfX3fCxoxT8gBX33lQzomBU0qIUaEj1RebsKVz1ob/BVH1XSBwwwvRVtZEV1b7Fxx2eVu34Ge/mg3w== +"@prisma/debug@5.16.1": + version "5.16.1" + resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-5.16.1.tgz#4887a57a0973fb732a60c30dc48de97bf1eefd7e" + integrity sha512-JsNgZAg6BD9RInLSrg7ZYzo11N7cVvYArq3fHGSD89HSgtN0VDdjV6bib7YddbcO6snzjchTiLfjeTqBjtArVQ== -"@prisma/engines-version@5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48": - version "5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48" - resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48.tgz#019c3c75a5c3276e580685fe48cdbfd181176858" - integrity sha512-ip6pNkRo1UxWv+6toxNcYvItNYaqQjXdFNGJ+Nuk2eYtRoEdoF13wxo7/jsClJFFenMPVNVqXQDV0oveXnR1cA== +"@prisma/engines-version@5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303": + version "5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303.tgz#63ceebefb7daa1eb17f250cad75d35999a50ee1b" + integrity sha512-HkT2WbfmFZ9WUPyuJHhkiADxazHg8Y4gByrTSVeb3OikP6tjQ7txtSUGu9OBOBH0C13dPKN2qqH12xKtHu/Hiw== -"@prisma/engines@5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.14.0.tgz#2ee91dd2220a726c27c906fbea788bbb3efdac6e" - integrity sha512-lgxkKZ6IEygVcw6IZZUlPIfLQ9hjSYAtHjZ5r64sCLDgVzsPFCi2XBBJgzPMkOQ5RHzUD4E/dVdpn9+ez8tk1A== +"@prisma/engines@5.16.1": + version "5.16.1" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.16.1.tgz#a14e5d08d34241ed1f1bb7d11ed44eacb37b6fc6" + integrity sha512-KkyF3eIUtBIyp5A/rJHCtwQO18OjpGgx18PzjyGcJDY/+vNgaVyuVd+TgwBgeq6NLdd1XMwRCI+58vinHsAdfA== dependencies: - "@prisma/debug" "5.14.0" - "@prisma/engines-version" "5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48" - "@prisma/fetch-engine" "5.14.0" - "@prisma/get-platform" "5.14.0" + "@prisma/debug" "5.16.1" + "@prisma/engines-version" "5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303" + "@prisma/fetch-engine" "5.16.1" + "@prisma/get-platform" "5.16.1" "@prisma/extension-read-replicas@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@prisma/extension-read-replicas/-/extension-read-replicas-0.3.0.tgz#2842a7c928f957c1dd58a6256104797596d43426" integrity sha512-F9+rSmYday6GT2qjhJtkZcBOpLO5LtpvFcMGqrBDHf+78LEdSuxfFjOxYlNuqk4B+th62yxpbhfpmB9/Mca14Q== -"@prisma/fetch-engine@5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-5.14.0.tgz#45297c118d4ec3fea55129886edd5a429da1f6da" - integrity sha512-VrheA9y9DMURK5vu8OJoOgQpxOhas3qF0IBHJ8G/0X44k82kc8E0w98HCn2nhnbOOMwbWsJWXfLC2/F8n5u0gQ== +"@prisma/fetch-engine@5.16.1": + version "5.16.1" + resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-5.16.1.tgz#506a034eb23222af27ba635eb48c63df0ba7fc14" + integrity sha512-oOkjaPU1lhcA/Rvr4GVfd1NLJBwExgNBE36Ueq7dr71kTMwy++a3U3oLd2ZwrV9dj9xoP6LjCcky799D9nEt4w== dependencies: - "@prisma/debug" "5.14.0" - "@prisma/engines-version" "5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48" - "@prisma/get-platform" "5.14.0" + "@prisma/debug" "5.16.1" + "@prisma/engines-version" "5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303" + "@prisma/get-platform" "5.16.1" -"@prisma/get-platform@5.14.0": - version "5.14.0" - resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-5.14.0.tgz#69112d3dde61905f59a65ed818f153e153ca40f0" - integrity sha512-/yAyBvcEjRv41ynZrhdrPtHgk47xLRRq/o5eWGcUpBJ1YrUZTYB8EoPiopnP7iQrMATK8stXQdPOoVlrzuTQZw== +"@prisma/get-platform@5.16.1": + version "5.16.1" + resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-5.16.1.tgz#613197c58acaafd5142d48a11f4df45a8f26a9e9" + integrity sha512-R4IKnWnMkR2nUAbU5gjrPehdQYUUd7RENFD2/D+xXTNhcqczp0N+WEGQ3ViyI3+6mtVcjjNIMdnUTNyu3GxIgA== dependencies: - "@prisma/debug" "5.14.0" + "@prisma/debug" "5.16.1" "@react-spring/animated@~9.7.3": version "9.7.3" @@ -7601,12 +7601,12 @@ next-basics@^0.39.0: jsonwebtoken "^9.0.0" pure-rand "^6.0.2" -next@14.2.3: - version "14.2.3" - resolved "https://registry.yarnpkg.com/next/-/next-14.2.3.tgz#f117dd5d5f20c307e7b8e4f9c1c97d961008925d" - integrity sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A== +next@14.2.4: + version "14.2.4" + resolved "https://registry.yarnpkg.com/next/-/next-14.2.4.tgz#ef66c39c71e2d8ad0a3caa0383c8933f4663e4d1" + integrity sha512-R8/V7vugY+822rsQGQCjoLhMuC9oFj9SOi4Cl4b2wjDrseD0LRZ10W7R6Czo4w9ZznVSshKjuIomsRjvm9EKJQ== dependencies: - "@next/env" "14.2.3" + "@next/env" "14.2.4" "@swc/helpers" "0.5.5" busboy "1.6.0" caniuse-lite "^1.0.30001579" @@ -7614,15 +7614,15 @@ next@14.2.3: postcss "8.4.31" styled-jsx "5.1.1" optionalDependencies: - "@next/swc-darwin-arm64" "14.2.3" - "@next/swc-darwin-x64" "14.2.3" - "@next/swc-linux-arm64-gnu" "14.2.3" - "@next/swc-linux-arm64-musl" "14.2.3" - "@next/swc-linux-x64-gnu" "14.2.3" - "@next/swc-linux-x64-musl" "14.2.3" - "@next/swc-win32-arm64-msvc" "14.2.3" - "@next/swc-win32-ia32-msvc" "14.2.3" - "@next/swc-win32-x64-msvc" "14.2.3" + "@next/swc-darwin-arm64" "14.2.4" + "@next/swc-darwin-x64" "14.2.4" + "@next/swc-linux-arm64-gnu" "14.2.4" + "@next/swc-linux-arm64-musl" "14.2.4" + "@next/swc-linux-x64-gnu" "14.2.4" + "@next/swc-linux-x64-musl" "14.2.4" + "@next/swc-win32-arm64-msvc" "14.2.4" + "@next/swc-win32-ia32-msvc" "14.2.4" + "@next/swc-win32-x64-msvc" "14.2.4" nice-try@^1.0.4: version "1.0.5" @@ -8659,12 +8659,12 @@ pretty-format@^29.0.0, pretty-format@^29.7.0: ansi-styles "^5.0.0" react-is "^18.0.0" -prisma@5.14.0: - version "5.14.0" - resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.14.0.tgz#ffc4696a43b044b636c3303b7aa98c13c2ade4dd" - integrity sha512-gCNZco7y5XtjrnQYeDJTiVZmT/ncqCr5RY1/Cf8X2wgLRmyh9ayPAGBNziI4qEE4S6SxCH5omQLVo9lmURaJ/Q== +prisma@5.16.1: + version "5.16.1" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.16.1.tgz#6dfd1e27e6534741326f4a231f04c16b3fbb7ba9" + integrity sha512-Z1Uqodk44diztImxALgJJfNl2Uisl9xDRvqybMKEBYJLNKNhDfAHf+ZIJbZyYiBhLMbKU9cYGdDVG5IIXEnL2Q== dependencies: - "@prisma/engines" "5.14.0" + "@prisma/engines" "5.16.1" process@^0.11.10: version "0.11.10" From 0142e0ff2f5e31d2b008355f4f45e7363f02921b Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 28 Jun 2024 20:20:12 -0700 Subject: [PATCH 007/132] Fixed typo. --- public/intl/messages/am-ET.json | 2 +- public/intl/messages/ar-SA.json | 2 +- public/intl/messages/be-BY.json | 2 +- public/intl/messages/bg-BG.json | 2 +- public/intl/messages/bn-BD.json | 2 +- public/intl/messages/bs-BA.json | 2 +- public/intl/messages/cs-CZ.json | 2 +- public/intl/messages/da-DK.json | 2 +- public/intl/messages/de-CH.json | 2 +- public/intl/messages/de-DE.json | 2 +- public/intl/messages/el-GR.json | 2 +- public/intl/messages/en-GB.json | 2 +- public/intl/messages/en-US.json | 2 +- public/intl/messages/es-ES.json | 2 +- public/intl/messages/fa-IR.json | 2 +- public/intl/messages/fi-FI.json | 2 +- public/intl/messages/fo-FO.json | 2 +- public/intl/messages/fr-FR.json | 2 +- public/intl/messages/ga-ES.json | 2 +- public/intl/messages/he-IL.json | 2 +- public/intl/messages/hi-IN.json | 2 +- public/intl/messages/hr-HR.json | 2 +- public/intl/messages/hu-HU.json | 2 +- public/intl/messages/id-ID.json | 2 +- public/intl/messages/it-IT.json | 2 +- public/intl/messages/ja-JP.json | 2 +- public/intl/messages/km-KH.json | 2 +- public/intl/messages/lt-LT.json | 2 +- public/intl/messages/mn-MN.json | 2 +- public/intl/messages/ms-MY.json | 2 +- public/intl/messages/my-MM.json | 2 +- public/intl/messages/nb-NO.json | 2 +- public/intl/messages/nl-NL.json | 2 +- public/intl/messages/pl-PL.json | 2 +- public/intl/messages/pt-BR.json | 2 +- public/intl/messages/pt-PT.json | 2 +- public/intl/messages/ro-RO.json | 2 +- public/intl/messages/ru-RU.json | 2 +- public/intl/messages/si-LK.json | 2 +- public/intl/messages/sk-SK.json | 2 +- public/intl/messages/sl-SI.json | 2 +- public/intl/messages/sv-SE.json | 2 +- public/intl/messages/ta-IN.json | 2 +- public/intl/messages/th-TH.json | 2 +- public/intl/messages/tr-TR.json | 2 +- public/intl/messages/uk-UA.json | 2 +- public/intl/messages/ur-PK.json | 2 +- public/intl/messages/vi-VN.json | 2 +- public/intl/messages/zh-TW.json | 2 +- src/components/messages.ts | 2 +- src/lang/am-ET.json | 2 +- src/lang/ar-SA.json | 2 +- src/lang/be-BY.json | 2 +- src/lang/bg-BG.json | 2 +- src/lang/bn-BD.json | 2 +- src/lang/bs-BA.json | 2 +- src/lang/cs-CZ.json | 2 +- src/lang/da-DK.json | 2 +- src/lang/de-CH.json | 2 +- src/lang/de-DE.json | 2 +- src/lang/el-GR.json | 2 +- src/lang/en-GB.json | 2 +- src/lang/en-US.json | 2 +- src/lang/es-ES.json | 2 +- src/lang/fa-IR.json | 2 +- src/lang/fi-FI.json | 2 +- src/lang/fo-FO.json | 2 +- src/lang/fr-FR.json | 2 +- src/lang/ga-ES.json | 2 +- src/lang/he-IL.json | 2 +- src/lang/hi-IN.json | 2 +- src/lang/hr-HR.json | 2 +- src/lang/hu-HU.json | 2 +- src/lang/id-ID.json | 2 +- src/lang/it-IT.json | 2 +- src/lang/ja-JP.json | 2 +- src/lang/km-KH.json | 2 +- src/lang/lt-LT.json | 2 +- src/lang/mn-MN.json | 2 +- src/lang/ms-MY.json | 2 +- src/lang/my-MM.json | 2 +- src/lang/nb-NO.json | 2 +- src/lang/nl-NL.json | 2 +- src/lang/pl-PL.json | 2 +- src/lang/pt-BR.json | 2 +- src/lang/pt-PT.json | 2 +- src/lang/ro-RO.json | 2 +- src/lang/ru-RU.json | 2 +- src/lang/si-LK.json | 2 +- src/lang/sk-SK.json | 2 +- src/lang/sl-SI.json | 2 +- src/lang/sv-SE.json | 2 +- src/lang/ta-IN.json | 2 +- src/lang/th-TH.json | 2 +- src/lang/tr-TR.json | 2 +- src/lang/uk-UA.json | 2 +- src/lang/ur-PK.json | 2 +- src/lang/vi-VN.json | 2 +- src/lang/zh-TW.json | 2 +- 99 files changed, 99 insertions(+), 99 deletions(-) diff --git a/public/intl/messages/am-ET.json b/public/intl/messages/am-ET.json index 6565c98b20..65427a024d 100644 --- a/public/intl/messages/am-ET.json +++ b/public/intl/messages/am-ET.json @@ -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": [ diff --git a/public/intl/messages/ar-SA.json b/public/intl/messages/ar-SA.json index 721584d5a1..d6a1855e1f 100644 --- a/public/intl/messages/ar-SA.json +++ b/public/intl/messages/ar-SA.json @@ -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": [ diff --git a/public/intl/messages/be-BY.json b/public/intl/messages/be-BY.json index 9af4e89523..e6afc2ca85 100644 --- a/public/intl/messages/be-BY.json +++ b/public/intl/messages/be-BY.json @@ -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": [ diff --git a/public/intl/messages/bg-BG.json b/public/intl/messages/bg-BG.json index bf5f15dcd5..e970ec52e5 100644 --- a/public/intl/messages/bg-BG.json +++ b/public/intl/messages/bg-BG.json @@ -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": [ diff --git a/public/intl/messages/bn-BD.json b/public/intl/messages/bn-BD.json index 827271e3b6..31e04cfa49 100644 --- a/public/intl/messages/bn-BD.json +++ b/public/intl/messages/bn-BD.json @@ -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": [ diff --git a/public/intl/messages/bs-BA.json b/public/intl/messages/bs-BA.json index f3edb77352..3b5c10ff53 100644 --- a/public/intl/messages/bs-BA.json +++ b/public/intl/messages/bs-BA.json @@ -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": [ diff --git a/public/intl/messages/cs-CZ.json b/public/intl/messages/cs-CZ.json index a2500688a0..eaef42457d 100644 --- a/public/intl/messages/cs-CZ.json +++ b/public/intl/messages/cs-CZ.json @@ -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": [ diff --git a/public/intl/messages/da-DK.json b/public/intl/messages/da-DK.json index d9a6903f80..aac48bbc9b 100644 --- a/public/intl/messages/da-DK.json +++ b/public/intl/messages/da-DK.json @@ -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": [ diff --git a/public/intl/messages/de-CH.json b/public/intl/messages/de-CH.json index 32d3b4434c..0d06a0e03e 100644 --- a/public/intl/messages/de-CH.json +++ b/public/intl/messages/de-CH.json @@ -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": [ diff --git a/public/intl/messages/de-DE.json b/public/intl/messages/de-DE.json index 9a9b6d14f7..2482b208b5 100644 --- a/public/intl/messages/de-DE.json +++ b/public/intl/messages/de-DE.json @@ -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": [ diff --git a/public/intl/messages/el-GR.json b/public/intl/messages/el-GR.json index f44bd026a0..af0f11be3e 100644 --- a/public/intl/messages/el-GR.json +++ b/public/intl/messages/el-GR.json @@ -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": [ diff --git a/public/intl/messages/en-GB.json b/public/intl/messages/en-GB.json index efb8f1be4d..38a7857ccb 100644 --- a/public/intl/messages/en-GB.json +++ b/public/intl/messages/en-GB.json @@ -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": [ diff --git a/public/intl/messages/en-US.json b/public/intl/messages/en-US.json index bf911728a8..52b968ddf1 100644 --- a/public/intl/messages/en-US.json +++ b/public/intl/messages/en-US.json @@ -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": [ diff --git a/public/intl/messages/es-ES.json b/public/intl/messages/es-ES.json index 40ddded568..496fabc664 100644 --- a/public/intl/messages/es-ES.json +++ b/public/intl/messages/es-ES.json @@ -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": [ diff --git a/public/intl/messages/fa-IR.json b/public/intl/messages/fa-IR.json index cb10df4b1e..d5408e6458 100644 --- a/public/intl/messages/fa-IR.json +++ b/public/intl/messages/fa-IR.json @@ -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": [ diff --git a/public/intl/messages/fi-FI.json b/public/intl/messages/fi-FI.json index e43a185500..0af4dc6f4f 100644 --- a/public/intl/messages/fi-FI.json +++ b/public/intl/messages/fi-FI.json @@ -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": [ diff --git a/public/intl/messages/fo-FO.json b/public/intl/messages/fo-FO.json index 1b2d4f0721..c9df65e1e2 100644 --- a/public/intl/messages/fo-FO.json +++ b/public/intl/messages/fo-FO.json @@ -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": [ diff --git a/public/intl/messages/fr-FR.json b/public/intl/messages/fr-FR.json index 44fd708bee..bc4c51bb3f 100644 --- a/public/intl/messages/fr-FR.json +++ b/public/intl/messages/fr-FR.json @@ -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": [ diff --git a/public/intl/messages/ga-ES.json b/public/intl/messages/ga-ES.json index 690083ae05..7e182d73c7 100644 --- a/public/intl/messages/ga-ES.json +++ b/public/intl/messages/ga-ES.json @@ -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": [ diff --git a/public/intl/messages/he-IL.json b/public/intl/messages/he-IL.json index 740dfffaa5..ee8599f349 100644 --- a/public/intl/messages/he-IL.json +++ b/public/intl/messages/he-IL.json @@ -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": [ diff --git a/public/intl/messages/hi-IN.json b/public/intl/messages/hi-IN.json index 2c178669b5..5fdffe3755 100644 --- a/public/intl/messages/hi-IN.json +++ b/public/intl/messages/hi-IN.json @@ -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": [ diff --git a/public/intl/messages/hr-HR.json b/public/intl/messages/hr-HR.json index 974bdebd71..87f37e73ad 100644 --- a/public/intl/messages/hr-HR.json +++ b/public/intl/messages/hr-HR.json @@ -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": [ diff --git a/public/intl/messages/hu-HU.json b/public/intl/messages/hu-HU.json index 227ef208a0..2cc464e990 100644 --- a/public/intl/messages/hu-HU.json +++ b/public/intl/messages/hu-HU.json @@ -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": [ diff --git a/public/intl/messages/id-ID.json b/public/intl/messages/id-ID.json index dbe78a5018..8123d03b3a 100644 --- a/public/intl/messages/id-ID.json +++ b/public/intl/messages/id-ID.json @@ -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": [ diff --git a/public/intl/messages/it-IT.json b/public/intl/messages/it-IT.json index 21b70650dc..d755b277a6 100644 --- a/public/intl/messages/it-IT.json +++ b/public/intl/messages/it-IT.json @@ -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": [ diff --git a/public/intl/messages/ja-JP.json b/public/intl/messages/ja-JP.json index c11311c455..b2bc66a09a 100644 --- a/public/intl/messages/ja-JP.json +++ b/public/intl/messages/ja-JP.json @@ -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": [ diff --git a/public/intl/messages/km-KH.json b/public/intl/messages/km-KH.json index d8f7d50123..841dabdd33 100644 --- a/public/intl/messages/km-KH.json +++ b/public/intl/messages/km-KH.json @@ -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": [ diff --git a/public/intl/messages/lt-LT.json b/public/intl/messages/lt-LT.json index 891aed6ffa..e6c6cd1857 100644 --- a/public/intl/messages/lt-LT.json +++ b/public/intl/messages/lt-LT.json @@ -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": [ diff --git a/public/intl/messages/mn-MN.json b/public/intl/messages/mn-MN.json index b11d88c12e..d9461929e1 100644 --- a/public/intl/messages/mn-MN.json +++ b/public/intl/messages/mn-MN.json @@ -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": [ diff --git a/public/intl/messages/ms-MY.json b/public/intl/messages/ms-MY.json index 996af333db..8f47f3cd7a 100644 --- a/public/intl/messages/ms-MY.json +++ b/public/intl/messages/ms-MY.json @@ -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": [ diff --git a/public/intl/messages/my-MM.json b/public/intl/messages/my-MM.json index 34f83c029f..9451a0febb 100644 --- a/public/intl/messages/my-MM.json +++ b/public/intl/messages/my-MM.json @@ -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": [ diff --git a/public/intl/messages/nb-NO.json b/public/intl/messages/nb-NO.json index eeb648c482..3e73b45ee8 100644 --- a/public/intl/messages/nb-NO.json +++ b/public/intl/messages/nb-NO.json @@ -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": [ diff --git a/public/intl/messages/nl-NL.json b/public/intl/messages/nl-NL.json index 16f2de07f9..a186de8625 100644 --- a/public/intl/messages/nl-NL.json +++ b/public/intl/messages/nl-NL.json @@ -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": [ diff --git a/public/intl/messages/pl-PL.json b/public/intl/messages/pl-PL.json index 29e99d4fe6..608c0b3a80 100644 --- a/public/intl/messages/pl-PL.json +++ b/public/intl/messages/pl-PL.json @@ -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": [ diff --git a/public/intl/messages/pt-BR.json b/public/intl/messages/pt-BR.json index 149af775da..ae5b0de4ba 100644 --- a/public/intl/messages/pt-BR.json +++ b/public/intl/messages/pt-BR.json @@ -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": [ diff --git a/public/intl/messages/pt-PT.json b/public/intl/messages/pt-PT.json index 340ebb8852..02704e9e0f 100644 --- a/public/intl/messages/pt-PT.json +++ b/public/intl/messages/pt-PT.json @@ -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": [ diff --git a/public/intl/messages/ro-RO.json b/public/intl/messages/ro-RO.json index 67d0929bcf..311199cf39 100644 --- a/public/intl/messages/ro-RO.json +++ b/public/intl/messages/ro-RO.json @@ -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": [ diff --git a/public/intl/messages/ru-RU.json b/public/intl/messages/ru-RU.json index 1d0a86a1e7..30974dd64e 100644 --- a/public/intl/messages/ru-RU.json +++ b/public/intl/messages/ru-RU.json @@ -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": [ diff --git a/public/intl/messages/si-LK.json b/public/intl/messages/si-LK.json index d7cfd7bf43..e6b06c6ba7 100644 --- a/public/intl/messages/si-LK.json +++ b/public/intl/messages/si-LK.json @@ -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": [ diff --git a/public/intl/messages/sk-SK.json b/public/intl/messages/sk-SK.json index 2be1f01521..738ead435d 100644 --- a/public/intl/messages/sk-SK.json +++ b/public/intl/messages/sk-SK.json @@ -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": [ diff --git a/public/intl/messages/sl-SI.json b/public/intl/messages/sl-SI.json index 83c383c1aa..67f65ad407 100644 --- a/public/intl/messages/sl-SI.json +++ b/public/intl/messages/sl-SI.json @@ -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": [ diff --git a/public/intl/messages/sv-SE.json b/public/intl/messages/sv-SE.json index a6336a3044..c2b2c5e06f 100644 --- a/public/intl/messages/sv-SE.json +++ b/public/intl/messages/sv-SE.json @@ -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": [ diff --git a/public/intl/messages/ta-IN.json b/public/intl/messages/ta-IN.json index 4afcb8db74..13ab67980f 100644 --- a/public/intl/messages/ta-IN.json +++ b/public/intl/messages/ta-IN.json @@ -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": [ diff --git a/public/intl/messages/th-TH.json b/public/intl/messages/th-TH.json index 32d5e677d2..43158951dc 100644 --- a/public/intl/messages/th-TH.json +++ b/public/intl/messages/th-TH.json @@ -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": [ diff --git a/public/intl/messages/tr-TR.json b/public/intl/messages/tr-TR.json index ac49dd47d5..1edfdbbc4e 100644 --- a/public/intl/messages/tr-TR.json +++ b/public/intl/messages/tr-TR.json @@ -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": [ diff --git a/public/intl/messages/uk-UA.json b/public/intl/messages/uk-UA.json index 6ea471a6b3..33579a3373 100644 --- a/public/intl/messages/uk-UA.json +++ b/public/intl/messages/uk-UA.json @@ -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": [ diff --git a/public/intl/messages/ur-PK.json b/public/intl/messages/ur-PK.json index 18fce7ebb7..9d3c932b6c 100644 --- a/public/intl/messages/ur-PK.json +++ b/public/intl/messages/ur-PK.json @@ -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": [ diff --git a/public/intl/messages/vi-VN.json b/public/intl/messages/vi-VN.json index 50409fab26..424d30a691 100644 --- a/public/intl/messages/vi-VN.json +++ b/public/intl/messages/vi-VN.json @@ -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": [ diff --git a/public/intl/messages/zh-TW.json b/public/intl/messages/zh-TW.json index d6514ab4df..37c490d7c4 100644 --- a/public/intl/messages/zh-TW.json +++ b/public/intl/messages/zh-TW.json @@ -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": [ diff --git a/src/components/messages.ts b/src/components/messages.ts index fa515c665f..6609076235 100644 --- a/src/components/messages.ts +++ b/src/components/messages.ts @@ -265,7 +265,7 @@ export const labels = defineMessages({ journey: { id: 'label.journey', defaultMessage: 'Journey' }, journeyDescription: { id: 'label.journey-description', - defaultMessage: 'Understand how users nagivate through your website.', + defaultMessage: 'Understand how users navigate through your website.', }, compare: { id: 'label.compare', defaultMessage: 'Compare' }, current: { id: 'label.current', defaultMessage: 'Current' }, diff --git a/src/lang/am-ET.json b/src/lang/am-ET.json index 323fe4e88b..7c533031ab 100644 --- a/src/lang/am-ET.json +++ b/src/lang/am-ET.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "Laptop", diff --git a/src/lang/ar-SA.json b/src/lang/ar-SA.json index a9fd02cfa4..f764f29e0a 100644 --- a/src/lang/ar-SA.json +++ b/src/lang/ar-SA.json @@ -96,7 +96,7 @@ "label.join": "انضم", "label.join-team": "انضم للفريق", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "اللغة", "label.languages": "اللغات", "label.laptop": "لابتوب", diff --git a/src/lang/be-BY.json b/src/lang/be-BY.json index 3a4a033581..53533023fd 100644 --- a/src/lang/be-BY.json +++ b/src/lang/be-BY.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Мова", "label.languages": "Мовы", "label.laptop": "Ноўтбук", diff --git a/src/lang/bg-BG.json b/src/lang/bg-BG.json index 9b22b466f4..5095b38799 100644 --- a/src/lang/bg-BG.json +++ b/src/lang/bg-BG.json @@ -96,7 +96,7 @@ "label.join": "Присъедини се", "label.join-team": "Присъедини се към екип", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Език", "label.languages": "Езици", "label.laptop": "Лаптоп", diff --git a/src/lang/bn-BD.json b/src/lang/bn-BD.json index 43b41420c8..18eb1d896e 100644 --- a/src/lang/bn-BD.json +++ b/src/lang/bn-BD.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "ভাষা", "label.languages": "ভাষা", "label.laptop": "ল্যাপটপ", diff --git a/src/lang/bs-BA.json b/src/lang/bs-BA.json index 04fb89ee4b..000af0dc20 100644 --- a/src/lang/bs-BA.json +++ b/src/lang/bs-BA.json @@ -96,7 +96,7 @@ "label.join": "Učlani se", "label.join-team": "Učlani se u tim", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Jezik", "label.languages": "Jezici", "label.laptop": "Laptop", diff --git a/src/lang/cs-CZ.json b/src/lang/cs-CZ.json index 9c6d3ba197..3a57f6b28c 100644 --- a/src/lang/cs-CZ.json +++ b/src/lang/cs-CZ.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "Přenosný počítač", diff --git a/src/lang/da-DK.json b/src/lang/da-DK.json index a0cbe5e650..715df99e21 100644 --- a/src/lang/da-DK.json +++ b/src/lang/da-DK.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Sprog", "label.languages": "Sprog", "label.laptop": "Laptop", diff --git a/src/lang/de-CH.json b/src/lang/de-CH.json index bdd37cdc8d..6891480fa9 100644 --- a/src/lang/de-CH.json +++ b/src/lang/de-CH.json @@ -96,7 +96,7 @@ "label.join": "Biträte", "label.join-team": "Team biträte", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Sprach", "label.languages": "Sprache", "label.laptop": "Laptop", diff --git a/src/lang/de-DE.json b/src/lang/de-DE.json index aca4b863f9..10b633263f 100644 --- a/src/lang/de-DE.json +++ b/src/lang/de-DE.json @@ -96,7 +96,7 @@ "label.join": "Beitreten", "label.join-team": "Team beitreten", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Sprache", "label.languages": "Sprachen", "label.laptop": "Laptop", diff --git a/src/lang/el-GR.json b/src/lang/el-GR.json index 8155b97af7..ceb0812bdd 100644 --- a/src/lang/el-GR.json +++ b/src/lang/el-GR.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "Λάπτοπ", diff --git a/src/lang/en-GB.json b/src/lang/en-GB.json index aeefe43997..9bb61811b4 100644 --- a/src/lang/en-GB.json +++ b/src/lang/en-GB.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "Laptop", diff --git a/src/lang/en-US.json b/src/lang/en-US.json index 7487e220a2..e895036cc1 100644 --- a/src/lang/en-US.json +++ b/src/lang/en-US.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "Laptop", diff --git a/src/lang/es-ES.json b/src/lang/es-ES.json index bdcb3e0ad0..d06bd19c2b 100644 --- a/src/lang/es-ES.json +++ b/src/lang/es-ES.json @@ -96,7 +96,7 @@ "label.join": "Unir", "label.join-team": "Unirse al equipo", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Idioma", "label.languages": "Idiomas", "label.laptop": "Portátil", diff --git a/src/lang/fa-IR.json b/src/lang/fa-IR.json index 8eb2df241d..fe1705e30a 100644 --- a/src/lang/fa-IR.json +++ b/src/lang/fa-IR.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "زبان", "label.languages": "زبان‌ها", "label.laptop": "لپ‌تاپ", diff --git a/src/lang/fi-FI.json b/src/lang/fi-FI.json index f6d18b35e5..02561dcb23 100644 --- a/src/lang/fi-FI.json +++ b/src/lang/fi-FI.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Kieli", "label.languages": "Kielet", "label.laptop": "Kannettava tietokone", diff --git a/src/lang/fo-FO.json b/src/lang/fo-FO.json index 98646d2832..e9ca2aae91 100644 --- a/src/lang/fo-FO.json +++ b/src/lang/fo-FO.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "Fartelda", diff --git a/src/lang/fr-FR.json b/src/lang/fr-FR.json index 23c6652588..edb2abd36b 100644 --- a/src/lang/fr-FR.json +++ b/src/lang/fr-FR.json @@ -96,7 +96,7 @@ "label.join": "Rejoindre", "label.join-team": "Rejoindre une équipe", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Langue", "label.languages": "Langues", "label.laptop": "Portable", diff --git a/src/lang/ga-ES.json b/src/lang/ga-ES.json index 9e04806129..d267137f1a 100644 --- a/src/lang/ga-ES.json +++ b/src/lang/ga-ES.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Idioma", "label.languages": "Idiomas", "label.laptop": "Portátil", diff --git a/src/lang/he-IL.json b/src/lang/he-IL.json index 584d5f5d43..d7ebf875b6 100644 --- a/src/lang/he-IL.json +++ b/src/lang/he-IL.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "לפטופ", diff --git a/src/lang/hi-IN.json b/src/lang/hi-IN.json index 0cf024e332..1fbee2c02e 100644 --- a/src/lang/hi-IN.json +++ b/src/lang/hi-IN.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "लैपटॉप", diff --git a/src/lang/hr-HR.json b/src/lang/hr-HR.json index bc69742664..39d4d9d1dd 100644 --- a/src/lang/hr-HR.json +++ b/src/lang/hr-HR.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Jezik", "label.languages": "Languages", "label.laptop": "Laptop", diff --git a/src/lang/hu-HU.json b/src/lang/hu-HU.json index 111ce00fa1..6762181bd7 100644 --- a/src/lang/hu-HU.json +++ b/src/lang/hu-HU.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "Laptop", diff --git a/src/lang/id-ID.json b/src/lang/id-ID.json index 0f2c705aa0..930b02da5c 100644 --- a/src/lang/id-ID.json +++ b/src/lang/id-ID.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Bahasa", "label.languages": "Bahasa", "label.laptop": "Laptop", diff --git a/src/lang/it-IT.json b/src/lang/it-IT.json index 3e73781894..229254d56a 100644 --- a/src/lang/it-IT.json +++ b/src/lang/it-IT.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Lingua", "label.languages": "Lingue", "label.laptop": "Portatile", diff --git a/src/lang/ja-JP.json b/src/lang/ja-JP.json index ef0cce1dd6..02e300bbb8 100644 --- a/src/lang/ja-JP.json +++ b/src/lang/ja-JP.json @@ -96,7 +96,7 @@ "label.join": "参加", "label.join-team": "チームに参加", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "言語", "label.languages": "言語", "label.laptop": "ノートPC", diff --git a/src/lang/km-KH.json b/src/lang/km-KH.json index 9a374a0673..f3189ac2bd 100644 --- a/src/lang/km-KH.json +++ b/src/lang/km-KH.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "ភាសា", "label.languages": "ភាសា", "label.laptop": "កុំព្យូទ័រយួរដៃ", diff --git a/src/lang/lt-LT.json b/src/lang/lt-LT.json index f96e643e64..424c06ce3e 100644 --- a/src/lang/lt-LT.json +++ b/src/lang/lt-LT.json @@ -96,7 +96,7 @@ "label.join": "Prisijungti", "label.join-team": "Prisijungti į komandą", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Kalba", "label.languages": "Kalbos", "label.laptop": "Laptop", diff --git a/src/lang/mn-MN.json b/src/lang/mn-MN.json index 47e08bca3f..0e99817063 100644 --- a/src/lang/mn-MN.json +++ b/src/lang/mn-MN.json @@ -96,7 +96,7 @@ "label.join": "Нэгдэх", "label.join-team": "Багт нэгдэх", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Хэл", "label.languages": "Хэл", "label.laptop": "Зөөврийн компьютер", diff --git a/src/lang/ms-MY.json b/src/lang/ms-MY.json index bf28b05cfe..82cfbb6b42 100644 --- a/src/lang/ms-MY.json +++ b/src/lang/ms-MY.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "Laptop", diff --git a/src/lang/my-MM.json b/src/lang/my-MM.json index dffbe43e48..8a71cd4974 100644 --- a/src/lang/my-MM.json +++ b/src/lang/my-MM.json @@ -96,7 +96,7 @@ "label.join": "ဝင်မည်", "label.join-team": "အသင်းဝင်မည်", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "ဘာသာစကား", "label.languages": "ဘာသာစကားများ", "label.laptop": "လက်တော့ပ်", diff --git a/src/lang/nb-NO.json b/src/lang/nb-NO.json index ca360d0950..4a6e0409c1 100644 --- a/src/lang/nb-NO.json +++ b/src/lang/nb-NO.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Språk", "label.languages": "Språk", "label.laptop": "Bærbar", diff --git a/src/lang/nl-NL.json b/src/lang/nl-NL.json index 6208147309..9a1c852bc1 100644 --- a/src/lang/nl-NL.json +++ b/src/lang/nl-NL.json @@ -96,7 +96,7 @@ "label.join": "Lid worden", "label.join-team": "Word lid van een team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Taal", "label.languages": "Talen", "label.laptop": "Laptop", diff --git a/src/lang/pl-PL.json b/src/lang/pl-PL.json index 8068d9b360..e663d44598 100644 --- a/src/lang/pl-PL.json +++ b/src/lang/pl-PL.json @@ -96,7 +96,7 @@ "label.join": "Dołącz", "label.join-team": "Dołącz do zespołu", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Język", "label.languages": "Języki", "label.laptop": "Laptop", diff --git a/src/lang/pt-BR.json b/src/lang/pt-BR.json index c1f9c708ff..be96d09772 100644 --- a/src/lang/pt-BR.json +++ b/src/lang/pt-BR.json @@ -96,7 +96,7 @@ "label.join": "Participar", "label.join-team": "Participar da equipe", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Idioma", "label.languages": "Idiomas", "label.laptop": "Notebook", diff --git a/src/lang/pt-PT.json b/src/lang/pt-PT.json index d497bb3a8f..252457f833 100644 --- a/src/lang/pt-PT.json +++ b/src/lang/pt-PT.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Língua", "label.languages": "Línguas", "label.laptop": "Portátil", diff --git a/src/lang/ro-RO.json b/src/lang/ro-RO.json index 107303ae94..c06690c23d 100644 --- a/src/lang/ro-RO.json +++ b/src/lang/ro-RO.json @@ -96,7 +96,7 @@ "label.join": "Alătură-te", "label.join-team": "Alătură-te echipei", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Limbă", "label.languages": "Limbi", "label.laptop": "Laptop", diff --git a/src/lang/ru-RU.json b/src/lang/ru-RU.json index 535d22921d..257c0491aa 100644 --- a/src/lang/ru-RU.json +++ b/src/lang/ru-RU.json @@ -96,7 +96,7 @@ "label.join": "Присоединиться", "label.join-team": "Присоединиться к команде", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Язык", "label.languages": "Языки", "label.laptop": "Ноутбук", diff --git a/src/lang/si-LK.json b/src/lang/si-LK.json index 03b42df782..6263f91a96 100644 --- a/src/lang/si-LK.json +++ b/src/lang/si-LK.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "භාෂාව", "label.languages": "Languages", "label.laptop": "Laptop", diff --git a/src/lang/sk-SK.json b/src/lang/sk-SK.json index 3d5b8fbb13..4447ada298 100644 --- a/src/lang/sk-SK.json +++ b/src/lang/sk-SK.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "Prenosný počítač", diff --git a/src/lang/sl-SI.json b/src/lang/sl-SI.json index 01fc7f30b4..3c684dec03 100644 --- a/src/lang/sl-SI.json +++ b/src/lang/sl-SI.json @@ -96,7 +96,7 @@ "label.join": "Pridruži se", "label.join-team": "Pridruži se ekipi", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Jezik", "label.languages": "Jeziki", "label.laptop": "Prenosni računalnik", diff --git a/src/lang/sv-SE.json b/src/lang/sv-SE.json index 92c9df03c6..c60b6cf904 100644 --- a/src/lang/sv-SE.json +++ b/src/lang/sv-SE.json @@ -96,7 +96,7 @@ "label.join": "Gå med", "label.join-team": "Gå med i team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Språk", "label.languages": "Språk", "label.laptop": "Bärbar", diff --git a/src/lang/ta-IN.json b/src/lang/ta-IN.json index 99558b11d8..0f95bc1861 100644 --- a/src/lang/ta-IN.json +++ b/src/lang/ta-IN.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "மடிக்கணினி", diff --git a/src/lang/th-TH.json b/src/lang/th-TH.json index 253f420c82..a594bb3120 100644 --- a/src/lang/th-TH.json +++ b/src/lang/th-TH.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "ภาษา", "label.languages": "ภาษา", "label.laptop": "แล็ปท็อป", diff --git a/src/lang/tr-TR.json b/src/lang/tr-TR.json index 49e71b9a31..b8271ed219 100644 --- a/src/lang/tr-TR.json +++ b/src/lang/tr-TR.json @@ -96,7 +96,7 @@ "label.join": "Katıl", "label.join-team": "Takıma katıl", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Dil", "label.languages": "Diller", "label.laptop": "Dizüstü", diff --git a/src/lang/uk-UA.json b/src/lang/uk-UA.json index ec658b7ef6..ecfd27f50a 100644 --- a/src/lang/uk-UA.json +++ b/src/lang/uk-UA.json @@ -93,7 +93,7 @@ "label.join": "Приєднатись", "label.join-team": "Приєднатись до команди", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Мова", "label.languages": "Мови", "label.laptop": "Ноутбук", diff --git a/src/lang/ur-PK.json b/src/lang/ur-PK.json index 7d649148ab..97de5d6940 100644 --- a/src/lang/ur-PK.json +++ b/src/lang/ur-PK.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "زبانیں", "label.laptop": "لیپ ٹاپ", diff --git a/src/lang/vi-VN.json b/src/lang/vi-VN.json index 1be8684bdb..054f449a8a 100644 --- a/src/lang/vi-VN.json +++ b/src/lang/vi-VN.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Ngôn ngữ", "label.laptop": "Laptop", diff --git a/src/lang/zh-TW.json b/src/lang/zh-TW.json index 9d8329e9af..10069181e3 100644 --- a/src/lang/zh-TW.json +++ b/src/lang/zh-TW.json @@ -96,7 +96,7 @@ "label.join": "加入", "label.join-team": "加入團隊", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "語言", "label.languages": "語言", "label.laptop": "筆記型電腦", From 490d51bde12693b2e5936a2d206353f0a57563da Mon Sep 17 00:00:00 2001 From: Jacek Maciejak <18230443+RikoDEV@users.noreply.github.com> Date: Sat, 29 Jun 2024 18:57:21 +0200 Subject: [PATCH 008/132] Update pl-PL.json --- src/lang/pl-PL.json | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/lang/pl-PL.json b/src/lang/pl-PL.json index 8068d9b360..064f67abc8 100644 --- a/src/lang/pl-PL.json +++ b/src/lang/pl-PL.json @@ -24,12 +24,12 @@ "label.cities": "Miasta", "label.city": "Miasto", "label.clear-all": "Wyczyść wszystko", - "label.compare": "Compare", + "label.compare": "Porównaj", "label.confirm": "Potwierdź", "label.confirm-password": "Potwierdź hasło", "label.contains": "Zawiera", "label.continue": "Kontynuuj", - "label.count": "Count", + "label.count": "Liczba", "label.countries": "Kraje", "label.country": "Państwo", "label.create": "Utwórz", @@ -38,7 +38,7 @@ "label.create-user": "Utwórz użytkownika", "label.created": "Utworzony", "label.created-by": "Utworzony przez", - "label.current": "Current", + "label.current": "Aktualny", "label.current-password": "Aktualne hasło", "label.custom-range": "Zakres niestandardowy", "label.dashboard": "Panel", @@ -65,12 +65,12 @@ "label.edit-dashboard": "Edytuj panel", "label.edit-member": "Edytuj członka", "label.enable-share-url": "Włącz udostępnianie adresu URL", - "label.end-step": "End Step", + "label.end-step": "Krok końcowy", "label.entry": "Entry URL", "label.event": "Zdarzenie", "label.event-data": "Dane zdarzenia", "label.events": "Zdarzenia", - "label.exit": "Exit URL", + "label.exit": "URL wyjściowy", "label.false": "Fałsz", "label.field": "Pole", "label.fields": "Pola", @@ -80,13 +80,13 @@ "label.filters": "Filtry", "label.funnel": "Lejek", "label.funnel-description": "Zrozum wskaźniki konwersji i odpływu użytkowników.", - "label.goal": "Goal", - "label.goals": "Goals", + "label.goal": "Cel", + "label.goals": "Cele", "label.goals-description": "Track your goals for pageviews and events.", "label.greater-than": "Większe niż", "label.greater-than-equals": "Większe niż lub równe", "label.host": "Host", - "label.hosts": "Hosts", + "label.hosts": "Hosty", "label.insights": "Analiza", "label.insights-description": "Poznaj lepiej swoje dane, korzystając z segmentów i filtrów.", "label.is": "Równe", @@ -95,8 +95,8 @@ "label.is-set": "Ustawione", "label.join": "Dołącz", "label.join-team": "Dołącz do zespołu", - "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey": "Droga", + "label.journey-description": "Zrozum, w jaki sposób użytkownicy poruszają się po Twojej witrynie.", "label.language": "Język", "label.languages": "Języki", "label.laptop": "Laptop", @@ -134,22 +134,22 @@ "label.password": "Hasło", "label.powered-by": "Obsługiwane przez {name}", "label.previous": "Previous", - "label.previous-period": "Previous period", - "label.previous-year": "Previous year", + "label.previous-period": "Poprzedni okres", + "label.previous-year": "Poprzedni rok", "label.profile": "Profil", "label.property": "Property", "label.queries": "Zapytania", "label.query": "Zapytanie", "label.query-parameters": "Parametry zapytania", "label.realtime": "Czas rzeczywisty", - "label.referrer": "Referrer", + "label.referrer": "Źródło odsyłające", "label.referrers": "Źródła odsyłające", "label.refresh": "Odśwież", "label.regenerate": "Wygeneruj ponownie", "label.region": "Region", "label.regions": "Regiony", "label.remove": "Usuń", - "label.remove-member": "Remove member", + "label.remove-member": "Usuń członka", "label.reports": "Raporty", "label.required": "Wymagany", "label.reset": "Zresetuj", @@ -169,7 +169,7 @@ "label.settings": "Ustawienia", "label.share-url": "Udostępnij adres URL", "label.single-day": "W tym dniu", - "label.start-step": "Start Step", + "label.start-step": "Krok startowy", "label.steps": "Kroki", "label.sum": "Suma", "label.tablet": "Tablet", @@ -217,7 +217,7 @@ "label.views-per-visit": "Widoków na wizytę", "label.visit-duration": "Średni czas wizyty", "label.visitors": "Odwiedzający", - "label.visits": "Odwiedząjący", + "label.visits": "Wizyty", "label.website": "Witryna", "label.website-id": "ID witryny", "label.websites": "Witryny", @@ -225,7 +225,7 @@ "label.yesterday": "Wczoraj", "message.action-confirmation": "Wpisz {confirmation}, aby potwierdzić.", "message.active-users": "{x} aktualnie {x, plural, one {odwiedzający} other {odwiedzających}}", - "message.collected-data": "Collected data", + "message.collected-data": "Zebrane dane", "message.confirm-delete": "Czy na pewno chcesz usunąć {target}?", "message.confirm-leave": "Czy na pewno chcesz opuścić {target}?", "message.confirm-remove": "Czy na pewno chcesz usunąć {target}?", From 512603d79b3d40e6da29091644f9b3501d4f5eb5 Mon Sep 17 00:00:00 2001 From: Jacek Maciejak <18230443+RikoDEV@users.noreply.github.com> Date: Sat, 29 Jun 2024 19:00:33 +0200 Subject: [PATCH 009/132] Whoops --- src/lang/pl-PL.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/pl-PL.json b/src/lang/pl-PL.json index 064f67abc8..953438be23 100644 --- a/src/lang/pl-PL.json +++ b/src/lang/pl-PL.json @@ -133,7 +133,7 @@ "label.pages": "Strony", "label.password": "Hasło", "label.powered-by": "Obsługiwane przez {name}", - "label.previous": "Previous", + "label.previous": "Poprzedni", "label.previous-period": "Poprzedni okres", "label.previous-year": "Poprzedni rok", "label.profile": "Profil", From 87779202f9c01d6a39a6dfd4ad03c524a0c2d0f1 Mon Sep 17 00:00:00 2001 From: mobeicanyue <81098819+mobeicanyue@users.noreply.github.com> Date: Sun, 30 Jun 2024 11:26:21 +0800 Subject: [PATCH 010/132] Update zh-CN.json --- src/lang/zh-CN.json | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/lang/zh-CN.json b/src/lang/zh-CN.json index bcdd3612b6..552c242369 100644 --- a/src/lang/zh-CN.json +++ b/src/lang/zh-CN.json @@ -24,12 +24,12 @@ "label.cities": "市/县", "label.city": "市/县", "label.clear-all": "清除全部", - "label.compare": "Compare", + "label.compare": "比较", "label.confirm": "确认", "label.confirm-password": "确认密码", "label.contains": "包含", "label.continue": "继续", - "label.count": "Count", + "label.count": "统计", "label.countries": "国家/地区", "label.country": "国家/地区", "label.create": "创建", @@ -38,7 +38,7 @@ "label.create-user": "创建用户", "label.created": "已创建", "label.created-by": "创建者", - "label.current": "Current", + "label.current": "目前", "label.current-password": "目前密码", "label.custom-range": "自定义时间段", "label.dashboard": "仪表板", @@ -65,12 +65,12 @@ "label.edit-dashboard": "编辑仪表板", "label.edit-member": "编辑成员", "label.enable-share-url": "启用共享链接", - "label.end-step": "End Step", - "label.entry": "Entry URL", + "label.end-step": "结束步骤", + "label.entry": "入口 URL", "label.event": "事件", "label.event-data": "事件数据", "label.events": "行为类别", - "label.exit": "Exit URL", + "label.exit": "退出 URL", "label.false": "否", "label.field": "字段", "label.fields": "字段", @@ -80,13 +80,13 @@ "label.filters": "筛选", "label.funnel": "分析", "label.funnel-description": "了解用户的转换率和退出率。", - "label.goal": "Goal", - "label.goals": "Goals", - "label.goals-description": "Track your goals for pageviews and events.", + "label.goal": "目标", + "label.goals": "目标", + "label.goals-description": "跟踪页面浏览量和事件的目标。", "label.greater-than": "大于", "label.greater-than-equals": "大于或等于", - "label.host": "Host", - "label.hosts": "Hosts", + "label.host": "主机", + "label.hosts": "主机", "label.insights": "见解", "label.insights-description": "通过使用筛选器和划分时间段来更深入地研究数据。", "label.is": "等于", @@ -95,8 +95,8 @@ "label.is-set": "已设置", "label.join": "加入", "label.join-team": "加入团队", - "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey": "用户浏览轨迹", + "label.journey-description": "了解用户如何浏览网站。", "label.language": "语言", "label.languages": "语言", "label.laptop": "笔记本", @@ -110,7 +110,7 @@ "label.login": "登录", "label.logout": "退出", "label.manage": "管理", - "label.manager": "Manager", + "label.manager": "管理者", "label.max": "最大", "label.member": "成员", "label.members": "成员", @@ -133,11 +133,11 @@ "label.pages": "网页", "label.password": "密码", "label.powered-by": "由 {name} 提供支持", - "label.previous": "Previous", - "label.previous-period": "Previous period", - "label.previous-year": "Previous year", + "label.previous": "先前", + "label.previous-period": "上一时期", + "label.previous-year": "上一年", "label.profile": "个人资料", - "label.property": "Property", + "label.property": "属性", "label.queries": "查询", "label.query": "查询", "label.query-parameters": "查询参数", @@ -169,13 +169,13 @@ "label.settings": "设置", "label.share-url": "共享链接", "label.single-day": "单日", - "label.start-step": "Start Step", + "label.start-step": "开始步骤", "label.steps": "步骤", "label.sum": "总和", "label.tablet": "平板", "label.team": "团队", "label.team-id": "团队 ID", - "label.team-manager": "Team manager", + "label.team-manager": "团队管理者", "label.team-member": "团队成员", "label.team-name": "团队名称", "label.team-owner": "团队所有者", @@ -225,7 +225,7 @@ "label.yesterday": "昨天", "message.action-confirmation": "在下面的框中输入 {confirmation} 以确认。", "message.active-users": "当前在线 {x} 人", - "message.collected-data": "Collected data", + "message.collected-data": "已收集的数据", "message.confirm-delete": "你确定要删除 {target} 吗?", "message.confirm-leave": "你确定要离开 {target} 吗?", "message.confirm-remove": "您确定要移除 {target} ?", From d95e21299076b46e11b481e2daf4e14de52102cf Mon Sep 17 00:00:00 2001 From: Konstantin Tutsch Date: Sun, 30 Jun 2024 13:39:37 +0200 Subject: [PATCH 011/132] Add German translation for v2.12.0 Report categories, Compare, Transfers, Team features --- public/intl/messages/de-DE.json | 108 ++++++++++++++++---------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/public/intl/messages/de-DE.json b/public/intl/messages/de-DE.json index 9a9b6d14f7..a75a7ee90c 100644 --- a/public/intl/messages/de-DE.json +++ b/public/intl/messages/de-DE.json @@ -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": [ @@ -74,7 +74,7 @@ "label.analytics": [ { "type": 0, - "value": "Analytics" + "value": "Analytiken" } ], "label.average": [ @@ -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": [ @@ -476,31 +476,31 @@ "label.funnel": [ { "type": 0, - "value": "Funnel" + "value": "Trichter" } ], "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": [ @@ -636,7 +636,7 @@ "label.last-months": [ { "type": 0, - "value": "Last " + "value": "Letzte " }, { "type": 1, @@ -644,7 +644,7 @@ }, { "type": 0, - "value": " months" + "value": " Monate" } ], "label.leave": [ @@ -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": [ @@ -777,7 +777,7 @@ "value": [ { "type": 0, - "value": "record" + "value": "Eintrag" } ] }, @@ -785,7 +785,7 @@ "value": [ { "type": 0, - "value": "records" + "value": "Einträge" } ] } @@ -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": "Tippe " }, { "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": "Wird ein Team gelöscht werden auch alle Websites des Teams 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ähle 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": [ From 001dbbad9f10e93bcdea3e8858d439b56dcb5dcc Mon Sep 17 00:00:00 2001 From: Konstantin Tutsch Date: Mon, 1 Jul 2024 17:04:56 +0200 Subject: [PATCH 012/132] Revert "Add German translation for v2.12.0" This reverts commit d95e21299076b46e11b481e2daf4e14de52102cf. --- public/intl/messages/de-DE.json | 108 ++++++++++++++++---------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/public/intl/messages/de-DE.json b/public/intl/messages/de-DE.json index a75a7ee90c..9a9b6d14f7 100644 --- a/public/intl/messages/de-DE.json +++ b/public/intl/messages/de-DE.json @@ -32,13 +32,13 @@ "label.add-member": [ { "type": 0, - "value": "Mitglied hinzufügen" + "value": "Add member" } ], "label.add-step": [ { "type": 0, - "value": "Schritt hinzufügen" + "value": "Add step" } ], "label.add-website": [ @@ -74,7 +74,7 @@ "label.analytics": [ { "type": 0, - "value": "Analytiken" + "value": "Analytics" } ], "label.average": [ @@ -104,7 +104,7 @@ "label.breakdown": [ { "type": 0, - "value": "Aufschlüsselung" + "value": "Breakdown" } ], "label.browser": [ @@ -152,7 +152,7 @@ "label.compare": [ { "type": 0, - "value": "Vergleich" + "value": "Compare" } ], "label.confirm": [ @@ -182,7 +182,7 @@ "label.count": [ { "type": 0, - "value": "Anzahl" + "value": "Count" } ], "label.countries": [ @@ -200,7 +200,7 @@ "label.create": [ { "type": 0, - "value": "Erstellen" + "value": "Create" } ], "label.create-report": [ @@ -230,13 +230,13 @@ "label.created-by": [ { "type": 0, - "value": "Erstellt von" + "value": "Created By" } ], "label.current": [ { "type": 0, - "value": "Aktuell" + "value": "Current" } ], "label.current-password": [ @@ -296,7 +296,7 @@ "label.delete-report": [ { "type": 0, - "value": "Bericht löschen" + "value": "Delete report" } ], "label.delete-team": [ @@ -386,7 +386,7 @@ "label.edit-member": [ { "type": 0, - "value": "Mitglied bearbeiten" + "value": "Edit member" } ], "label.enable-share-url": [ @@ -398,7 +398,7 @@ "label.end-step": [ { "type": 0, - "value": "Schritt beenden" + "value": "End Step" } ], "label.entry": [ @@ -476,31 +476,31 @@ "label.funnel": [ { "type": 0, - "value": "Trichter" + "value": "Funnel" } ], "label.funnel-description": [ { "type": 0, - "value": "Verstehe die Konversions- und Dropoffrate von Nutzern." + "value": "Understand the conversion and drop-off rate of users." } ], "label.goal": [ { "type": 0, - "value": "Ziel" + "value": "Goal" } ], "label.goals": [ { "type": 0, - "value": "Ziele" + "value": "Goals" } ], "label.goals-description": [ { "type": 0, - "value": "Verfolgen Sie Ihre Ziele für Aufrufe und Events." + "value": "Track your goals for pageviews and events." } ], "label.greater-than": [ @@ -536,7 +536,7 @@ "label.insights-description": [ { "type": 0, - "value": "Tauchen Sie tiefer in Ihre Daten mit Filtern und Segmenten ein." + "value": "Dive deeper into your data by using segments and filters." } ], "label.is": [ @@ -584,7 +584,7 @@ "label.journey-description": [ { "type": 0, - "value": "Verstehen Sie, wie Nutzer Ihre Website navigieren." + "value": "Understand how users nagivate through your website." } ], "label.language": [ @@ -636,7 +636,7 @@ "label.last-months": [ { "type": 0, - "value": "Letzte " + "value": "Last " }, { "type": 1, @@ -644,7 +644,7 @@ }, { "type": 0, - "value": " Monate" + "value": " months" } ], "label.leave": [ @@ -686,7 +686,7 @@ "label.manage": [ { "type": 0, - "value": "Verwalten" + "value": "Manage" } ], "label.manager": [ @@ -704,7 +704,7 @@ "label.member": [ { "type": 0, - "value": "Mitglied" + "value": "Member" } ], "label.members": [ @@ -734,7 +734,7 @@ "label.my-account": [ { "type": 0, - "value": "Mein Konto" + "value": "My account" } ], "label.my-websites": [ @@ -777,7 +777,7 @@ "value": [ { "type": 0, - "value": "Eintrag" + "value": "record" } ] }, @@ -785,7 +785,7 @@ "value": [ { "type": 0, - "value": "Einträge" + "value": "records" } ] } @@ -874,19 +874,19 @@ "label.previous": [ { "type": 0, - "value": "Vorherige" + "value": "Previous" } ], "label.previous-period": [ { "type": 0, - "value": "Vorheriger Zeitraum" + "value": "Previous period" } ], "label.previous-year": [ { "type": 0, - "value": "Vorheriges Jahr" + "value": "Previous year" } ], "label.profile": [ @@ -898,7 +898,7 @@ "label.property": [ { "type": 0, - "value": "Besitz" + "value": "Property" } ], "label.queries": [ @@ -970,7 +970,7 @@ "label.remove-member": [ { "type": 0, - "value": "Mitglied entfernen" + "value": "Remove member" } ], "label.reports": [ @@ -1006,7 +1006,7 @@ "label.retention-description": [ { "type": 0, - "value": "Messen Sie die Presenz Ihrer Website, indem Sie tracken wie oft Nutzer zurückkehren." + "value": "Measure your website stickiness by tracking how often users return." } ], "label.role": [ @@ -1036,13 +1036,13 @@ "label.search": [ { "type": 0, - "value": "Suche" + "value": "Search" } ], "label.select": [ { "type": 0, - "value": "Auswählen" + "value": "Select" } ], "label.select-date": [ @@ -1054,7 +1054,7 @@ "label.select-role": [ { "type": 0, - "value": "Rolle auswählen" + "value": "Select role" } ], "label.select-website": [ @@ -1090,13 +1090,13 @@ "label.start-step": [ { "type": 0, - "value": "Schritt starten" + "value": "Start Step" } ], "label.steps": [ { "type": 0, - "value": "Schritte" + "value": "Steps" } ], "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": "Übertragung" + "value": "Transfer" } ], "label.transfer-website": [ { "type": 0, - "value": "Website übertragen" + "value": "Transfer website" } ], "label.true": [ @@ -1324,7 +1324,7 @@ "label.utm-description": [ { "type": 0, - "value": "Tracken Sie Ihre Kampagnen mit Hilfe von UTM Parametern." + "value": "Track your campaigns through UTM parameters." } ], "label.value": [ @@ -1360,7 +1360,7 @@ "label.views-per-visit": [ { "type": 0, - "value": "Aufrufe pro Besuch" + "value": "Views per visit" } ], "label.visit-duration": [ @@ -1378,7 +1378,7 @@ "label.visits": [ { "type": 0, - "value": "Besuche" + "value": "Visits" } ], "label.website": [ @@ -1414,7 +1414,7 @@ "message.action-confirmation": [ { "type": 0, - "value": "Tippe " + "value": "Type " }, { "type": 1, @@ -1422,7 +1422,7 @@ }, { "type": 0, - "value": " in das untenliegende Feld, um zu bestätigen." + "value": " in the box below to confirm." } ], "message.active-users": [ @@ -1462,7 +1462,7 @@ "message.collected-data": [ { "type": 0, - "value": "Gesammelte Daten" + "value": "Collected data" } ], "message.confirm-delete": [ @@ -1496,7 +1496,7 @@ "message.confirm-remove": [ { "type": 0, - "value": "Sind Sie sicher, dass Sie " + "value": "Are you sure you want to remove " }, { "type": 1, @@ -1504,7 +1504,7 @@ }, { "type": 0, - "value": " entfernen möchten?" + "value": "?" } ], "message.confirm-reset": [ @@ -1524,7 +1524,7 @@ "message.delete-team-warning": [ { "type": 0, - "value": "Wird ein Team gelöscht werden auch alle Websites des Teams gelöscht." + "value": "Deleting a team will also delete all team websites." } ], "message.delete-website-warning": [ @@ -1708,25 +1708,25 @@ "message.transfer-team-website-to-user": [ { "type": 0, - "value": "Möchten Sie diese Website auf Ihr Konto übertragen?" + "value": "Transfer this website to your account?" } ], "message.transfer-user-website-to-team": [ { "type": 0, - "value": "Wähle das Team, auf das die Website übertragen wird." + "value": "Select the team to transfer this website to." } ], "message.transfer-website": [ { "type": 0, - "value": "Übertragen Sie den Besitz der Website auf Ihren Account oder ein anderes Team." + "value": "Transfer website ownership to your account or another team." } ], "message.triggered-event": [ { "type": 0, - "value": "Event ausgelöst." + "value": "Triggered event" } ], "message.user-deleted": [ @@ -1738,7 +1738,7 @@ "message.viewed-page": [ { "type": 0, - "value": "Seite besucht." + "value": "Viewed page" } ], "message.visitor-log": [ From 9bb06d86105bd40bbc6c487c9ae08fc86bac26ea Mon Sep 17 00:00:00 2001 From: Konstantin Tutsch Date: Mon, 1 Jul 2024 17:05:48 +0200 Subject: [PATCH 013/132] Update German translation for v2.12 Report categories, Compare, Transfers, Team features --- src/lang/de-DE.json | 94 ++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/src/lang/de-DE.json b/src/lang/de-DE.json index aca4b863f9..aed2655a66 100644 --- a/src/lang/de-DE.json +++ b/src/lang/de-DE.json @@ -4,8 +4,8 @@ "label.activity-log": "Aktivitätsverlauf", "label.add": "Hinzufügen", "label.add-description": "Beschreibung hinzufügen", - "label.add-member": "Add member", - "label.add-step": "Add step", + "label.add-member": "Mitglied hinzufügen", + "label.add-step": "Schritt hinzufügen", "label.add-website": "Website hinzufügen", "label.admin": "Administrator", "label.after": "Nach", @@ -16,7 +16,7 @@ "label.back": "Zurück", "label.before": "Vor", "label.bounce-rate": "Absprungrate", - "label.breakdown": "Breakdown", + "label.breakdown": "Aufschlüsselung", "label.browser": "Browser", "label.browsers": "Browser", "label.cancel": "Abbrechen", @@ -24,21 +24,21 @@ "label.cities": "Städte", "label.city": "Stadt", "label.clear-all": "Alles löschen", - "label.compare": "Compare", + "label.compare": "Vergleich", "label.confirm": "Bestätigen", "label.confirm-password": "Passwort wiederholen", "label.contains": "Enthält", "label.continue": "Weiter", - "label.count": "Count", + "label.count": "Anzahl", "label.countries": "Länder", "label.country": "Land", - "label.create": "Create", + "label.create": "Erstellen", "label.create-report": "Bericht erstellen", "label.create-team": "Team erstellen", "label.create-user": "Benutzer erstellen", "label.created": "Erstellt", - "label.created-by": "Created By", - "label.current": "Current", + "label.created-by": "Erstellt von", + "label.current": "Aktuell", "label.current-password": "Derzeitiges Passwort", "label.custom-range": "Benutzerdefinierter Bereich", "label.dashboard": "Übersicht", @@ -48,7 +48,7 @@ "label.day": "Tag", "label.default-date-range": "Voreingestellter Datumsbereich", "label.delete": "Löschen", - "label.delete-report": "Delete report", + "label.delete-report": "Bericht löschen", "label.delete-team": "Team löschen", "label.delete-user": "Benutzer löschen", "label.delete-website": "Website löschen", @@ -63,9 +63,9 @@ "label.dropoff": "Dropoff", "label.edit": "Bearbeiten", "label.edit-dashboard": "Dashboard bearbeiten", - "label.edit-member": "Edit member", + "label.edit-member": "Mitglied bearbeiten", "label.enable-share-url": "Freigabe-URL aktivieren", - "label.end-step": "End Step", + "label.end-step": "Schritt beenden", "label.entry": "Entry URL", "label.event": "Event", "label.event-data": "Eventdaten", @@ -79,16 +79,16 @@ "label.filter-raw": "Rohdaten", "label.filters": "Filter", "label.funnel": "Funnel", - "label.funnel-description": "Understand the conversion and drop-off rate of users.", - "label.goal": "Goal", - "label.goals": "Goals", - "label.goals-description": "Track your goals for pageviews and events.", + "label.funnel-description": "Verstehe die Konversions- und Dropoffrate von Nutzern.", + "label.goal": "Ziel", + "label.goals": "Ziele", + "label.goals-description": "Verfolgen Sie Ihre Ziele für Aufrufe und Events.", "label.greater-than": "Größer als", "label.greater-than-equals": "Größer oder gleich", "label.host": "Host", "label.hosts": "Hosts", "label.insights": "Insights", - "label.insights-description": "Dive deeper into your data by using segments and filters.", + "label.insights-description": "Tauchen Sie tiefer in Ihre Daten mit Filtern und Segmenten ein.", "label.is": "Ist", "label.is-not": "Ist nicht", "label.is-not-set": "Ist nicht gesetzt", @@ -96,7 +96,7 @@ "label.join": "Beitreten", "label.join-team": "Team beitreten", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Verstehen Sie, wie Nutzer Ihre Website navigieren.", "label.language": "Sprache", "label.languages": "Sprachen", "label.laptop": "Laptop", @@ -109,15 +109,15 @@ "label.less-than-equals": "Kleiner oder gleich", "label.login": "Anmelden", "label.logout": "Abmelden", - "label.manage": "Manage", + "label.manage": "Verwalten", "label.manager": "Manager", "label.max": "Max", - "label.member": "Member", + "label.member": "Mitglied", "label.members": "Mitglieder", "label.min": "Min", "label.mobile": "Handy", "label.more": "Mehr", - "label.my-account": "My account", + "label.my-account": "Mein Konto", "label.my-websites": "Meine Websites", "label.name": "Name", "label.new-password": "Neues Passwort", @@ -133,11 +133,11 @@ "label.pages": "Seiten", "label.password": "Passwort", "label.powered-by": "Betrieben durch {name}", - "label.previous": "Previous", - "label.previous-period": "Previous period", - "label.previous-year": "Previous year", + "label.previous": "Vorherige", + "label.previous-period": "Vorheriger Zeitraum", + "label.previous-year": "Vorheriges Jahr", "label.profile": "Profil", - "label.property": "Property", + "label.property": "Besitz", "label.queries": "Abfragen", "label.query": "Abfrage", "label.query-parameters": "Abfrageparameter", @@ -149,33 +149,33 @@ "label.region": "Region", "label.regions": "Regionen", "label.remove": "Entfernen", - "label.remove-member": "Remove member", + "label.remove-member": "Mitglied entfernen", "label.reports": "Berichte", "label.required": "Erforderlich", "label.reset": "Zurücksetzen", "label.reset-website": "Statistik zurücksetzen", "label.retention": "Retention", - "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.retention-description": "Messen Sie die Presenz Ihrer Website, indem Sie tracken wie oft Nutzer zurückkehren.", "label.role": "Rolle", "label.run-query": "Abfrage starten", "label.save": "Speichern", "label.screens": "Bildschirmauflösungen", - "label.search": "Search", - "label.select": "Select", + "label.search": "Suche", + "label.select": "Auswählen", "label.select-date": "Datum auswählen", - "label.select-role": "Select role", + "label.select-role": "Rolle auswählen", "label.select-website": "Website auswählen", "label.sessions": "Sitzungen", "label.settings": "Einstellungen", "label.share-url": "Freigabe-URL", "label.single-day": "Ein Tag", - "label.start-step": "Start Step", - "label.steps": "Steps", + "label.start-step": "Schritt starten", + "label.steps": "Schritte", "label.sum": "Summe", "label.tablet": "Tablet", "label.team": "Team", "label.team-id": "Team-ID", - "label.team-manager": "Team manager", + "label.team-manager": "Team-Manager", "label.team-member": "Team-Mitglied", "label.team-name": "Name des Teams", "label.team-owner": "Team-Eigentümer", @@ -193,8 +193,8 @@ "label.total": "Gesamt", "label.total-records": "Datensätze insgesamt", "label.tracking-code": "Tracking Code", - "label.transfer": "Transfer", - "label.transfer-website": "Transfer website", + "label.transfer": "Übertragung", + "label.transfer-website": "Website übertragen", "label.true": "Wahr", "label.type": "Typ", "label.unique": "Eindeutig", @@ -208,29 +208,29 @@ "label.username": "Benutzername", "label.users": "Benutzer", "label.utm": "UTM", - "label.utm-description": "Track your campaigns through UTM parameters.", + "label.utm-description": "Tracken Sie Ihre Kampagnen mit Hilfe von UTM Parametern.", "label.value": "Wert", "label.view": "Anzeigen", "label.view-details": "Details anzeigen", "label.view-only": "Nur ansehen", "label.views": "Aufrufe", - "label.views-per-visit": "Views per visit", + "label.views-per-visit": "Aufrufe pro Besuch", "label.visit-duration": "Durchschn. Besuchszeit", "label.visitors": "Besucher", - "label.visits": "Visits", + "label.visits": "Besuche", "label.website": "Website", "label.website-id": "Website ID", "label.websites": "Websites", "label.window": "Fenster", "label.yesterday": "Gestern", - "message.action-confirmation": "Type {confirmation} in the box below to confirm.", + "message.action-confirmation": "Tippen Sie {confirmation} in das untenliegende Feld, um zu bestätigen.", "message.active-users": "{x} {x, plural, one {aktiver Besucher} other {aktive Besucher}}", - "message.collected-data": "Collected data", + "message.collected-data": "Gesammelte Daten", "message.confirm-delete": "Sind Sie sich sicher, {target} zu löschen?", "message.confirm-leave": "Sind Sie sicher, dass die {target} verlassen möchten?", - "message.confirm-remove": "Are you sure you want to remove {target}?", + "message.confirm-remove": "Sind Sie sicher, dass Sie {target} entfernen möchten?", "message.confirm-reset": "Sind Sie sicher, dass Sie die Statistiken von {target} zurücksetzen wollen?", - "message.delete-team-warning": "Deleting a team will also delete all team websites.", + "message.delete-team-warning": "Alle zugehörigen Websiten werden ebenfalls gelöscht.", "message.delete-website-warning": "Alle zugehörigen Daten werden ebenfalls gelöscht.", "message.error": "Es ist ein Fehler aufgetreten.", "message.event-log": "{event} auf {url}", @@ -256,12 +256,12 @@ "message.team-not-found": "Team nicht gefunden.", "message.team-websites-info": "Websites können von jedem im Team eingesehen werden.", "message.tracking-code": "Tracking Code", - "message.transfer-team-website-to-user": "Transfer this website to your account?", - "message.transfer-user-website-to-team": "Select the team to transfer this website to.", - "message.transfer-website": "Transfer website ownership to your account or another team.", - "message.triggered-event": "Triggered event", + "message.transfer-team-website-to-user": "Möchten Sie diese Website auf Ihr Konto übertragen?", + "message.transfer-user-website-to-team": "Wählen Sie das Team, auf das die Website übertragen wird.", + "message.transfer-website": "Übertragen Sie den Besitz der Website auf Ihren Account oder ein anderes Team.", + "message.triggered-event": "Event ausgelöst", "message.user-deleted": "Benutzer gelöscht.", - "message.viewed-page": "Viewed page", + "message.viewed-page": "Seite besucht", "message.visitor-log": "Besucher aus {country} benutzt {browser} auf {os} {device}", - "message.visitors-dropped-off": "Visitors dropped off" + "message.visitors-dropped-off": "Besucher haben die Seite verlassen" } From 05894df58fc61d1096b846703f02ab7f9ab3fdf8 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Mon, 1 Jul 2024 12:18:43 -0700 Subject: [PATCH 014/132] replace count(distinct with uniq for session / visits --- src/queries/analytics/getWebsiteStats.ts | 4 ++-- src/queries/analytics/sessions/getSessionMetrics.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/queries/analytics/getWebsiteStats.ts b/src/queries/analytics/getWebsiteStats.ts index 6257e166c9..2f3c82e865 100644 --- a/src/queries/analytics/getWebsiteStats.ts +++ b/src/queries/analytics/getWebsiteStats.ts @@ -71,8 +71,8 @@ async function clickhouseQuery( ` select sum(t.c) as "pageviews", - count(distinct t.session_id) as "visitors", - count(distinct t.visit_id) as "visits", + uniq(t.session_id) as "visitors", + uniq(t.visit_id) as "visits", sum(if(t.c = 1, 1, 0)) as "bounces", sum(max_time-min_time) as "totaltime" from ( diff --git a/src/queries/analytics/sessions/getSessionMetrics.ts b/src/queries/analytics/sessions/getSessionMetrics.ts index e28f1fb2e7..e522a7effd 100644 --- a/src/queries/analytics/sessions/getSessionMetrics.ts +++ b/src/queries/analytics/sessions/getSessionMetrics.ts @@ -75,7 +75,7 @@ async function clickhouseQuery( ` select ${column} x, - count(distinct session_id) y + uniq(session_id) y ${includeCountry ? ', country' : ''} from website_event where website_id = {websiteId:UUID} From 20b3a3e6c2aa2d221f838295ba68e719ca849c41 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Mon, 1 Jul 2024 12:18:43 -0700 Subject: [PATCH 015/132] replace count(distinct with uniq for session / visits --- src/queries/analytics/getWebsiteStats.ts | 4 ++-- src/queries/analytics/sessions/getSessionMetrics.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/queries/analytics/getWebsiteStats.ts b/src/queries/analytics/getWebsiteStats.ts index 6257e166c9..2f3c82e865 100644 --- a/src/queries/analytics/getWebsiteStats.ts +++ b/src/queries/analytics/getWebsiteStats.ts @@ -71,8 +71,8 @@ async function clickhouseQuery( ` select sum(t.c) as "pageviews", - count(distinct t.session_id) as "visitors", - count(distinct t.visit_id) as "visits", + uniq(t.session_id) as "visitors", + uniq(t.visit_id) as "visits", sum(if(t.c = 1, 1, 0)) as "bounces", sum(max_time-min_time) as "totaltime" from ( diff --git a/src/queries/analytics/sessions/getSessionMetrics.ts b/src/queries/analytics/sessions/getSessionMetrics.ts index e28f1fb2e7..e522a7effd 100644 --- a/src/queries/analytics/sessions/getSessionMetrics.ts +++ b/src/queries/analytics/sessions/getSessionMetrics.ts @@ -75,7 +75,7 @@ async function clickhouseQuery( ` select ${column} x, - count(distinct session_id) y + uniq(session_id) y ${includeCountry ? ', country' : ''} from website_event where website_id = {websiteId:UUID} From 5289c277fb314ed183f582031783b263775ef2dd Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Mon, 1 Jul 2024 12:18:43 -0700 Subject: [PATCH 016/132] replace count(distinct with uniq for session / visits --- src/queries/analytics/getWebsiteStats.ts | 4 ++-- src/queries/analytics/sessions/getSessionMetrics.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/queries/analytics/getWebsiteStats.ts b/src/queries/analytics/getWebsiteStats.ts index 6257e166c9..2f3c82e865 100644 --- a/src/queries/analytics/getWebsiteStats.ts +++ b/src/queries/analytics/getWebsiteStats.ts @@ -71,8 +71,8 @@ async function clickhouseQuery( ` select sum(t.c) as "pageviews", - count(distinct t.session_id) as "visitors", - count(distinct t.visit_id) as "visits", + uniq(t.session_id) as "visitors", + uniq(t.visit_id) as "visits", sum(if(t.c = 1, 1, 0)) as "bounces", sum(max_time-min_time) as "totaltime" from ( diff --git a/src/queries/analytics/sessions/getSessionMetrics.ts b/src/queries/analytics/sessions/getSessionMetrics.ts index e28f1fb2e7..e522a7effd 100644 --- a/src/queries/analytics/sessions/getSessionMetrics.ts +++ b/src/queries/analytics/sessions/getSessionMetrics.ts @@ -75,7 +75,7 @@ async function clickhouseQuery( ` select ${column} x, - count(distinct session_id) y + uniq(session_id) y ${includeCountry ? ', country' : ''} from website_event where website_id = {websiteId:UUID} From d501410a63ac151000356274f22a1e3403c8936d Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Thu, 4 Jul 2024 15:55:06 -0700 Subject: [PATCH 017/132] Fixed console command. Updated packages. --- package.json | 2 +- src/app/(main)/console/TestConsole.tsx | 14 +- src/lib/detect.ts | 30 +- src/pages/api/send.ts | 31 +- yarn.lock | 577 ++++++++++++++----------- 5 files changed, 372 insertions(+), 282 deletions(-) diff --git a/package.json b/package.json index 9ef6f738bc..a7a3aba1e2 100644 --- a/package.json +++ b/package.json @@ -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" } } diff --git a/src/app/(main)/console/TestConsole.tsx b/src/app/(main)/console/TestConsole.tsx index d0afb14cec..7461454f9e 100644 --- a/src/app/(main)/console/TestConsole.tsx +++ b/src/app/(main)/console/TestConsole.tsx @@ -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 }) {
Javascript events
- -
diff --git a/src/lib/detect.ts b/src/lib/detect.ts index 64f70e6ca5..56a037ec49 100644 --- a/src/lib/detect.ts +++ b/src/lib/detect.ts @@ -2,9 +2,9 @@ import path from 'path'; import { getClientIp } from 'request-ip'; import { browserName, detectOS } from 'detect-browser'; import isLocalhost from 'is-localhost-ip'; +import ipaddr from 'ipaddr.js'; import maxmind from 'maxmind'; import { safeDecodeURIComponent } from 'next-basics'; - import { DESKTOP_OS, MOBILE_OS, @@ -137,3 +137,31 @@ export async function getClientInfo(req: NextApiRequestCollect) { return { userAgent, browser, os, ip, country, subdivision1, subdivision2, city, device }; } + +export function hasBlockedIp(req: NextApiRequestCollect) { + const ignoreIps = process.env.IGNORE_IP; + + if (ignoreIps) { + const ips = []; + + if (ignoreIps) { + ips.push(...ignoreIps.split(',').map(n => n.trim())); + } + + const clientIp = getIpAddress(req); + + return ips.find(ip => { + if (ip === clientIp) return true; + + // CIDR notation + if (ip.indexOf('/') > 0) { + const addr = ipaddr.parse(clientIp); + const range = ipaddr.parseCIDR(ip); + + if (addr.kind() === range[0].kind() && addr.match(range)) return true; + } + }); + } + + return false; +} diff --git a/src/pages/api/send.ts b/src/pages/api/send.ts index ab565e6e70..3431795edd 100644 --- a/src/pages/api/send.ts +++ b/src/pages/api/send.ts @@ -1,4 +1,3 @@ -import ipaddr from 'ipaddr.js'; import { isbot } from 'isbot'; import { NextApiRequest, NextApiResponse } from 'next'; import { @@ -12,7 +11,7 @@ import { } from 'next-basics'; import { COLLECTION_TYPE, HOSTNAME_REGEX, IP_REGEX } from 'lib/constants'; import { secret, visitSalt, uuid } from 'lib/crypto'; -import { getIpAddress } from 'lib/detect'; +import { hasBlockedIp } from 'lib/detect'; import { useCors, useSession, useValidate } from 'lib/middleware'; import { CollectionType, YupRequest } from 'lib/types'; import { saveEvent, saveSessionData } from 'queries'; @@ -166,31 +165,3 @@ export default async (req: NextApiRequestCollect, res: NextApiResponse) => { return methodNotAllowed(res); }; - -function hasBlockedIp(req: NextApiRequestCollect) { - const ignoreIps = process.env.IGNORE_IP; - - if (ignoreIps) { - const ips = []; - - if (ignoreIps) { - ips.push(...ignoreIps.split(',').map(n => n.trim())); - } - - const clientIp = getIpAddress(req); - - return ips.find(ip => { - if (ip === clientIp) return true; - - // CIDR notation - if (ip.indexOf('/') > 0) { - const addr = ipaddr.parse(clientIp); - const range = ipaddr.parseCIDR(ip); - - if (addr.kind() === range[0].kind() && addr.match(range)) return true; - } - }); - } - - return false; -} diff --git a/yarn.lock b/yarn.lock index 90f3da53ec..7944a0c6f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1126,13 +1126,6 @@ dependencies: regenerator-runtime "^0.14.0" -"@babel/runtime@^7.23.2": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.5.tgz#230946857c053a36ccc66e1dd03b17dd0c4ed02c" - integrity sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g== - dependencies: - regenerator-runtime "^0.14.0" - "@babel/template@^7.22.15", "@babel/template@^7.22.5": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" @@ -1565,14 +1558,6 @@ "@formatjs/intl-localematcher" "0.2.25" tslib "^2.1.0" -"@formatjs/ecma402-abstract@1.18.2": - version "1.18.2" - resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.18.2.tgz#bf103712a406874eb1e387858d5be2371ab3aa14" - integrity sha512-+QoPW4csYALsQIl8GbN14igZzDbuwzcpWrku9nyMXlaqAlwRBgl5V+p0vWMGFqHOw37czNXaP/lEk4wbLgcmtA== - dependencies: - "@formatjs/intl-localematcher" "0.5.4" - tslib "^2.4.0" - "@formatjs/ecma402-abstract@1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.4.0.tgz#ac6c17a8fffac43c6d68c849a7b732626d32654c" @@ -1587,6 +1572,14 @@ dependencies: tslib "^2.0.1" +"@formatjs/ecma402-abstract@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz#39197ab90b1c78b7342b129a56a7acdb8f512e17" + integrity sha512-rRqXOqdFmk7RYvj4khklyqzcfQl9vEL/usogncBHRZfZBDOwMGuSRNFl02fu5KGHXdbinju+YXyuR+Nk8xlr/g== + dependencies: + "@formatjs/intl-localematcher" "0.5.4" + tslib "^2.4.0" + "@formatjs/fast-memoize@2.2.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz#33bd616d2e486c3e8ef4e68c99648c196887802b" @@ -1603,13 +1596,13 @@ "@formatjs/icu-skeleton-parser" "1.3.6" tslib "^2.1.0" -"@formatjs/icu-messageformat-parser@2.7.6": - version "2.7.6" - resolved "https://registry.yarnpkg.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.6.tgz#3d69806de056d2919d53dad895a5ff4851e4e9ff" - integrity sha512-etVau26po9+eewJKYoiBKP6743I1br0/Ie00Pb/S/PtmYfmjTcOn2YCh2yNkSZI12h6Rg+BOgQYborXk46BvkA== +"@formatjs/icu-messageformat-parser@2.7.8": + version "2.7.8" + resolved "https://registry.yarnpkg.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.8.tgz#f6d7643001e9bb5930d812f1f9a9856f30fa0343" + integrity sha512-nBZJYmhpcSX0WeJ5SDYUkZ42AgR3xiyhNCsQweFx3cz/ULJjym8bHAzWKvG5e2+1XO98dBYC0fWeeAECAVSwLA== dependencies: - "@formatjs/ecma402-abstract" "1.18.2" - "@formatjs/icu-skeleton-parser" "1.8.0" + "@formatjs/ecma402-abstract" "2.0.0" + "@formatjs/icu-skeleton-parser" "1.8.2" tslib "^2.4.0" "@formatjs/icu-skeleton-parser@1.3.6": @@ -1620,29 +1613,29 @@ "@formatjs/ecma402-abstract" "1.11.4" tslib "^2.1.0" -"@formatjs/icu-skeleton-parser@1.8.0": - version "1.8.0" - resolved "https://registry.yarnpkg.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.0.tgz#5f3d3a620c687d6f8c180d80d1241e8f213acf79" - integrity sha512-QWLAYvM0n8hv7Nq5BEs4LKIjevpVpbGLAJgOaYzg9wABEoX1j0JO1q2/jVkO6CVlq0dbsxZCngS5aXbysYueqA== +"@formatjs/icu-skeleton-parser@1.8.2": + version "1.8.2" + resolved "https://registry.yarnpkg.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.2.tgz#2252c949ae84ee66930e726130ea66731a123c9f" + integrity sha512-k4ERKgw7aKGWJZgTarIcNEmvyTVD9FYh0mTrrBMHZ1b8hUu6iOJ4SzsZlo3UNAvHYa+PnvntIwRPt1/vy4nA9Q== dependencies: - "@formatjs/ecma402-abstract" "1.18.2" + "@formatjs/ecma402-abstract" "2.0.0" tslib "^2.4.0" -"@formatjs/intl-displaynames@6.6.6": - version "6.6.6" - resolved "https://registry.yarnpkg.com/@formatjs/intl-displaynames/-/intl-displaynames-6.6.6.tgz#be9fea4d24f577bb1a9d0f3ef4f2dcdabb4fe42d" - integrity sha512-Dg5URSjx0uzF8VZXtHb6KYZ6LFEEhCbAbKoYChYHEOnMFTw/ZU3jIo/NrujzQD2EfKPgQzIq73LOUvW6Z/LpFA== +"@formatjs/intl-displaynames@6.6.8": + version "6.6.8" + resolved "https://registry.yarnpkg.com/@formatjs/intl-displaynames/-/intl-displaynames-6.6.8.tgz#2f5afac8df83167f5a6ef8543600eaf1ef99c885" + integrity sha512-Lgx6n5KxN16B3Pb05z3NLEBQkGoXnGjkTBNCZI+Cn17YjHJ3fhCeEJJUqRlIZmJdmaXQhjcQVDp6WIiNeRYT5g== dependencies: - "@formatjs/ecma402-abstract" "1.18.2" + "@formatjs/ecma402-abstract" "2.0.0" "@formatjs/intl-localematcher" "0.5.4" tslib "^2.4.0" -"@formatjs/intl-listformat@7.5.5": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@formatjs/intl-listformat/-/intl-listformat-7.5.5.tgz#e4c7d741f2201c65e7da71326726e61332c7161e" - integrity sha512-XoI52qrU6aBGJC9KJddqnacuBbPlb/bXFN+lIFVFhQ1RnFHpzuFrlFdjD9am2O7ZSYsyqzYRpkVcXeT1GHkwDQ== +"@formatjs/intl-listformat@7.5.7": + version "7.5.7" + resolved "https://registry.yarnpkg.com/@formatjs/intl-listformat/-/intl-listformat-7.5.7.tgz#125e05105fabd1ae5f11881d6ab74484f2098ee4" + integrity sha512-MG2TSChQJQT9f7Rlv+eXwUFiG24mKSzmF144PLb8m8OixyXqn4+YWU+5wZracZGCgVTVmx8viCf7IH3QXoiB2g== dependencies: - "@formatjs/ecma402-abstract" "1.18.2" + "@formatjs/ecma402-abstract" "2.0.0" "@formatjs/intl-localematcher" "0.5.4" tslib "^2.4.0" @@ -1668,17 +1661,17 @@ "@formatjs/ecma402-abstract" "1.4.0" tslib "^2.0.1" -"@formatjs/intl@2.10.2": - version "2.10.2" - resolved "https://registry.yarnpkg.com/@formatjs/intl/-/intl-2.10.2.tgz#c074439ac2dbde4c2b3768b8108dfc3932b7fb30" - integrity sha512-raPGWr3JRv3neXV78SqPFrGC05fIbhhNzVghHNxFde27ls2KkXiMhtP7HBybjGpikVSjjhdhaZto+4p1vmm9bQ== +"@formatjs/intl@2.10.4": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@formatjs/intl/-/intl-2.10.4.tgz#e1819e0858fb05ca65923a020f346bc74e894e92" + integrity sha512-56483O+HVcL0c7VucAS2tyH020mt9XTozZO67cwtGg0a7KWDukS/FzW3OnvaHmTHDuYsoPIzO+ZHVfU6fT/bJw== dependencies: - "@formatjs/ecma402-abstract" "1.18.2" + "@formatjs/ecma402-abstract" "2.0.0" "@formatjs/fast-memoize" "2.2.0" - "@formatjs/icu-messageformat-parser" "2.7.6" - "@formatjs/intl-displaynames" "6.6.6" - "@formatjs/intl-listformat" "7.5.5" - intl-messageformat "10.5.12" + "@formatjs/icu-messageformat-parser" "2.7.8" + "@formatjs/intl-displaynames" "6.6.8" + "@formatjs/intl-listformat" "7.5.7" + intl-messageformat "10.5.14" tslib "^2.4.0" "@formatjs/ts-transformer@3.9.4": @@ -2002,19 +1995,19 @@ integrity sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw== "@netlify/plugin-nextjs@^5.1.0": - version "5.2.2" - resolved "https://registry.yarnpkg.com/@netlify/plugin-nextjs/-/plugin-nextjs-5.2.2.tgz#3c283d335001f9e0fbcb4db75557e5fe1660db72" - integrity sha512-jV/P7o8+v1XaEGb7wvFfkF1fSLggAxjg7WYoBPkD3R93bsI6xmCDKBcUJ/6g7lqECRXt4dGKApSFtGk/pUmAHw== + version "5.3.3" + resolved "https://registry.yarnpkg.com/@netlify/plugin-nextjs/-/plugin-nextjs-5.3.3.tgz#414cb0f4c21e6b80d6127b824efb00ae2f732a89" + integrity sha512-QhvZLOHhPuTnh6TZ5G0/jtjAJ1Y52A67b39eygKv6znQUPti8p+8y2WulcntpCRzVp2stzEULMNPlnptna1ikg== "@next/env@14.2.4": version "14.2.4" resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.4.tgz#5546813dc4f809884a37d257b254a5ce1b0248d7" integrity sha512-3EtkY5VDkuV2+lNmKlbkibIJxcO4oIHEhBWne6PaAp+76J9KoSsGvNikp6ivzAT8dhhBMYrm6op2pS1ApG0Hzg== -"@next/eslint-plugin-next@14.2.3": - version "14.2.3" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.3.tgz#287ad8620e7061ba01e8d3313d464db6d217b6df" - integrity sha512-L3oDricIIjgj1AVnRdRor21gI7mShlSwU/1ZGHmqM3LzHhXXhdkrfeNY5zif25Bi5Dd7fiJHsbhoZCHfXYvlAw== +"@next/eslint-plugin-next@14.2.4": + version "14.2.4" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.4.tgz#c7f965cb76f0b454e726ef0f69157c4fb4e28f53" + integrity sha512-svSFxW9f3xDaZA3idQmlFw7SusOuWTpDTAeBlO3AEPDltrraV+lqs7mAc6A27YdnpQVVIA3sODqUAAHdWhVWsA== dependencies: glob "10.3.10" @@ -2216,9 +2209,9 @@ slash "^4.0.0" "@rollup/plugin-commonjs@^25.0.4": - version "25.0.7" - resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.7.tgz#145cec7589ad952171aeb6a585bbeabd0fd3b4cf" - integrity sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ== + version "25.0.8" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.8.tgz#c77e608ab112a666b7f2a6bea625c73224f7dd34" + integrity sha512-ZEZWTK5n6Qde0to4vS9Mr5x/0UZoqCxPVR9KRUjU4kA2sO7GEUn1fop0DAwpO6z0Nw/kJON9bDmSxdWxO/TT1A== dependencies: "@rollup/pluginutils" "^5.0.1" commondir "^1.0.1" @@ -2247,9 +2240,9 @@ resolve "^1.22.1" "@rollup/plugin-replace@^5.0.2": - version "5.0.5" - resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-5.0.5.tgz#33d5653dce6d03cb24ef98bef7f6d25b57faefdf" - integrity sha512-rYO4fOi8lMaTg/z5Jb+hKnrHHVn8j2lwkqwyS4kTRhKyWOLf2wST2sWXr4WzWiTcoHTp2sTjqUbqIj2E39slKQ== + version "5.0.7" + resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-5.0.7.tgz#150c9ee9db8031d9e4580a61a0edeaaed3d37687" + integrity sha512-PqxSfuorkHz/SPpyngLyg5GCEkOcee9M1bkxiVDr41Pd61mqP1PLOoDPbpl44SB2mQGKwV/In74gqQmGITOhEQ== dependencies: "@rollup/pluginutils" "^5.0.1" magic-string "^0.30.3" @@ -2273,9 +2266,9 @@ picomatch "^2.3.1" "@rushstack/eslint-patch@^1.3.3": - version "1.10.2" - resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.10.2.tgz#053f1540703faa81dea2966b768ee5581c66aeda" - integrity sha512-hw437iINopmQuxWPSUEvqE56NCPsiU8N4AYtfHmJFckclktzK9YQJieD3XkDCDH4OjL+C7zgPUh73R/nrcHrqw== + version "1.10.3" + resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.10.3.tgz#391d528054f758f81e53210f1a1eebcf1a8b1d20" + integrity sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg== "@sinclair/typebox@^0.27.8": version "0.27.8" @@ -2430,17 +2423,17 @@ "@swc/counter" "^0.1.3" tslib "^2.4.0" -"@tanstack/query-core@5.35.5": - version "5.35.5" - resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.35.5.tgz#7b4100dc9cc7fee314b8a1bcbf502a236d43ffe3" - integrity sha512-OMWvlEqG01RfGj+XZb/piDzPp0eZkkHWSDHt2LvE/fd1zWburP/xwm0ghk6Iv8cuPlP+ACFkZviKXK0OVt6lhg== +"@tanstack/query-core@5.48.0": + version "5.48.0" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.48.0.tgz#a3308ec925d8c16d64c789899d6c084c2fe30cbc" + integrity sha512-lZAfPPeVIqXCswE9SSbG33B6/91XOWt/Iq41bFeWb/mnHwQSIfFRbkS4bfs+WhIk9abRArF9Id2fp0Mgo+hq6Q== "@tanstack/react-query@^5.28.6": - version "5.35.5" - resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.35.5.tgz#d41a087d58f42418824fa04aaca00ba93c99075c" - integrity sha512-sppX7L+PVn5GBV3In6zzj0zcKfnZRKhXbX1MfIfKo1OjIq2GMaopvAFOP0x1bRYTUk2ikrdYcQYOozX7PWkb8A== + version "5.48.0" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.48.0.tgz#7890620272b48aeb278498dfe082f27518f3ac6d" + integrity sha512-GDExbjYWzvDokyRqMSWXdrPiYpp95Aig0oeMIrxTaruOJJgWiWfUP//OAaowm2RrRkGVsavSZdko/XmIrrV2Nw== dependencies: - "@tanstack/query-core" "5.35.5" + "@tanstack/query-core" "5.48.0" "@trysound/sax@0.2.0": version "0.2.0" @@ -2525,21 +2518,16 @@ dependencies: cypress "*" -"@types/estree@*": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.3.tgz#2be19e759a3dd18c79f9f436bd7363556c1a73dd" - integrity sha512-CS2rOaoQ/eAgAfcTfq6amKG7bsN+EMcgGY4FAFQdvSj2y1ixvOZTUA9mOtCai7E1SYu283XNw7urKK30nP3wkQ== +"@types/estree@*", "@types/estree@^1.0.0": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== "@types/estree@^0.0.50": version "0.0.50" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== -"@types/estree@^1.0.0": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" - integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== - "@types/fs-extra@^8.0.1": version "8.1.4" resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-8.1.4.tgz#8171df1d16a80d69fc9c3aab07d7961349a5cb8b" @@ -2653,9 +2641,9 @@ integrity sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag== "@types/node@*", "@types/node@^20.9.0": - version "20.12.11" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.11.tgz#c4ef00d3507000d17690643278a60dc55a9dc9be" - integrity sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw== + version "20.14.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.9.tgz#12e8e765ab27f8c421a1820c99f5f313a933b420" + integrity sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg== dependencies: undici-types "~5.26.4" @@ -2704,9 +2692,9 @@ "@types/react" "*" "@types/react@*", "@types/react@16 || 17 || 18", "@types/react@^18.2.41": - version "18.3.1" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.1.tgz#fed43985caa834a2084d002e4771e15dfcbdbe8e" - integrity sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw== + version "18.3.3" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.3.tgz#9679020895318b0915d7a3ab004d92d33375c45f" + integrity sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw== dependencies: "@types/prop-types" "*" csstype "^3.0.2" @@ -3209,14 +3197,14 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -aria-query@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" - integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== +aria-query@~5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" + integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== dependencies: - dequal "^2.0.3" + deep-equal "^2.0.5" -array-buffer-byte-length@^1.0.1: +array-buffer-byte-length@^1.0.0, array-buffer-byte-length@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== @@ -3229,7 +3217,7 @@ array-find-index@^1.0.1: resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" integrity sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw== -array-includes@^3.1.6, array-includes@^3.1.7: +array-includes@^3.1.6, array-includes@^3.1.7, array-includes@^3.1.8: version "3.1.8" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.8.tgz#5e370cbe172fdd5dd6530c1d4aadda25281ba97d" integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== @@ -3246,7 +3234,7 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array.prototype.findlast@^1.2.4: +array.prototype.findlast@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz#3e4fbcb30a15a7f5bf64cf2faae22d139c2e4904" integrity sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ== @@ -3300,15 +3288,15 @@ array.prototype.toreversed@^1.1.2: es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" -array.prototype.tosorted@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz#c8c89348337e51b8a3c48a9227f9ce93ceedcba8" - integrity sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg== +array.prototype.tosorted@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz#fe954678ff53034e717ea3352a03f0b0b86f7ffc" + integrity sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA== dependencies: - call-bind "^1.0.5" + call-bind "^1.0.7" define-properties "^1.2.1" - es-abstract "^1.22.3" - es-errors "^1.1.0" + es-abstract "^1.23.3" + es-errors "^1.3.0" es-shim-unscopables "^1.0.2" arraybuffer.prototype.slice@^1.0.3: @@ -3392,21 +3380,21 @@ aws-sign2@~0.7.0: integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== aws4@^1.8.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3" - integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== + version "1.13.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.0.tgz#d9b802e9bb9c248d7be5f7f5ef178dc3684e9dcc" + integrity sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g== -axe-core@=4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.0.tgz#34ba5a48a8b564f67e103f0aa5768d76e15bbbbf" - integrity sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ== +axe-core@^4.9.1: + version "4.9.1" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.9.1.tgz#fcd0f4496dad09e0c899b44f6c4bb7848da912ae" + integrity sha512-QbUdXJVTpvUTHU7871ppZkdOLBeGUKBQWHkHrvN2V9IQWGMt61zf3B45BtzjxEJzYuj0JBjBZP/hmYS/R9pmAw== -axobject-query@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a" - integrity sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg== +axobject-query@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.1.1.tgz#3b6e5c6d4e43ca7ba51c5babf99d22a9c68485e1" + integrity sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg== dependencies: - dequal "^2.0.3" + deep-equal "^2.0.5" babel-jest@^29.7.0: version "29.7.0" @@ -3565,7 +3553,7 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^3.0.2: +braces@^3.0.2, braces@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== @@ -3754,9 +3742,9 @@ charenc@0.0.2: integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== chart.js@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.4.2.tgz#95962fa6430828ed325a480cc2d5f2b4e385ac31" - integrity sha512-6GD7iKwFpP5kbSD4MeRRRlTnQvxfQREy36uEtm1hzHzcOqwWx0YEHuspuoNlslu+nciLIB7fjjsHkUv/FzFcOg== + version "4.4.3" + resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.4.3.tgz#3b2e11e7010fefa99b07d0349236f5098e5226ad" + integrity sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw== dependencies: "@kurkle/color" "^0.3.0" @@ -3810,9 +3798,9 @@ cli-cursor@^4.0.0: restore-cursor "^4.0.0" cli-table3@~0.6.1: - version "0.6.4" - resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.4.tgz#d1c536b8a3f2e7bec58f67ac9e5769b1b30088b0" - integrity sha512-Lm3L0p+/npIQWNIiyF/nAn7T5dnOwR3xNTHXYEBFBFVPXzCVNZ5lqEC/1eo/EVfpDsQ1I+TX4ORPQgp+UI0CRw== + version "0.6.5" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.5.tgz#013b91351762739c16a9567c21a04632e449bf2f" + integrity sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ== dependencies: string-width "^4.2.0" optionalDependencies: @@ -4267,9 +4255,9 @@ cypress@*: yauzl "^2.10.0" cypress@^13.6.6: - version "13.9.0" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.9.0.tgz#b529cfa8f8c39ba163ed0501a25bb5b09c143652" - integrity sha512-atNjmYfHsvTuCaxTxLZr9xGoHz53LLui3266WWxXJHY7+N6OdwJdg/feEa3T+buez9dmUXHT1izCOklqG82uCQ== + version "13.12.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.12.0.tgz#1a4ea89b7fa6855e32bc02eaf5e25fc45b9e273f" + integrity sha512-udzS2JilmI9ApO/UuqurEwOvThclin5ntz7K0BtnHBs+tg2Bl9QShLISXpSEMDv/u8b6mqdoAdyKeZiSqKWL8g== dependencies: "@cypress/request" "^3.0.0" "@cypress/xvfb" "^1.2.4" @@ -4461,7 +4449,7 @@ debounce@^1.2.1: resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== -debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: +debug@4.3.4, debug@^4.1.0, debug@^4.3.1, debug@^4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -4475,6 +4463,13 @@ debug@^3.1.0, debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4.1.1, debug@^4.3.4: + version "4.3.5" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== + dependencies: + ms "2.1.2" + decamelize-keys@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" @@ -4498,6 +4493,30 @@ dedent@^1.0.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== +deep-equal@^2.0.5: + version "2.2.3" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.3.tgz#af89dafb23a396c7da3e862abc0be27cf51d56e1" + integrity sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.5" + es-get-iterator "^1.1.3" + get-intrinsic "^1.2.2" + is-arguments "^1.1.1" + is-array-buffer "^3.0.2" + is-date-object "^1.0.5" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + isarray "^2.0.5" + object-is "^1.1.5" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + side-channel "^1.0.4" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.13" + deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -4517,7 +4536,7 @@ define-data-property@^1.0.1, define-data-property@^1.1.4: es-errors "^1.3.0" gopd "^1.0.1" -define-properties@^1.2.0, define-properties@^1.2.1: +define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== @@ -4559,11 +4578,6 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -dequal@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" - integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== - detect-browser@^5.2.0: version "5.3.0" resolved "https://registry.yarnpkg.com/detect-browser/-/detect-browser-5.3.0.tgz#9705ef2bddf46072d0f7265a1fe300e36fe7ceca" @@ -4731,9 +4745,9 @@ end-of-stream@^1.1.0: once "^1.4.0" enhanced-resolve@^5.12.0: - version "5.16.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz#e8bc63d51b826d6f1cbc0a150ecb5a8b0c62e567" - integrity sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw== + version "5.17.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz#d037603789dd9555b89aaec7eb78845c49089bc5" + integrity sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -4763,7 +4777,7 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2, es-abstract@^1.23.3: +es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2, es-abstract@^1.23.3: version "1.23.3" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== @@ -4822,12 +4836,27 @@ es-define-property@^1.0.0: dependencies: get-intrinsic "^1.2.4" -es-errors@^1.1.0, es-errors@^1.2.1, es-errors@^1.3.0: +es-errors@^1.2.1, es-errors@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== -es-iterator-helpers@^1.0.15, es-iterator-helpers@^1.0.17: +es-get-iterator@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" + integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.7" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" + +es-iterator-helpers@^1.0.19: version "1.0.19" resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz#117003d0e5fec237b4b5c08aded722e0c6d50ca8" integrity sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw== @@ -4933,11 +4962,11 @@ escape-string-regexp@^4.0.0: integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== eslint-config-next@^14.0.4: - version "14.2.3" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-14.2.3.tgz#2fb0f7c4eccda530a4b5054438162b2303786d4f" - integrity sha512-ZkNztm3Q7hjqvB1rRlOX8P9E/cXRL9ajRcs8jufEtwMfTVYRqnmtnaSu57QqHyBlovMuiB8LEzfLBkh5RYV6Fg== + version "14.2.4" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-14.2.4.tgz#eb0bedfe4a894bc2aea918214bb5243ee4fa7d4b" + integrity sha512-Qr0wMgG9m6m4uYy2jrYJmyuNlYZzPRQq5Kvb9IDlYwn+7yq6W6sfMNFgb+9guM1KYwuIo6TIaiFhZJ6SnQ/Efw== dependencies: - "@next/eslint-plugin-next" "14.2.3" + "@next/eslint-plugin-next" "14.2.4" "@rushstack/eslint-patch" "^1.3.3" "@typescript-eslint/parser" "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0" eslint-import-resolver-node "^0.3.6" @@ -5032,26 +5061,26 @@ eslint-plugin-jest@^27.9.0: "@typescript-eslint/utils" "^5.10.0" eslint-plugin-jsx-a11y@^6.7.1: - version "6.8.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz#2fa9c701d44fcd722b7c771ec322432857fcbad2" - integrity sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA== + version "6.9.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.9.0.tgz#67ab8ff460d4d3d6a0b4a570e9c1670a0a8245c8" + integrity sha512-nOFOCaJG2pYqORjK19lqPqxMO/JpvdCZdPtNdxY3kvom3jTvkAbOvQvD8wuD0G8BYR0IGAGYDlzqWJOh/ybn2g== dependencies: - "@babel/runtime" "^7.23.2" - aria-query "^5.3.0" - array-includes "^3.1.7" + aria-query "~5.1.3" + array-includes "^3.1.8" array.prototype.flatmap "^1.3.2" ast-types-flow "^0.0.8" - axe-core "=4.7.0" - axobject-query "^3.2.1" + axe-core "^4.9.1" + axobject-query "~3.1.1" damerau-levenshtein "^1.0.8" emoji-regex "^9.2.2" - es-iterator-helpers "^1.0.15" - hasown "^2.0.0" + es-iterator-helpers "^1.0.19" + hasown "^2.0.2" jsx-ast-utils "^3.3.5" language-tags "^1.0.9" minimatch "^3.1.2" - object.entries "^1.1.7" - object.fromentries "^2.0.7" + object.fromentries "^2.0.8" + safe-regex-test "^1.0.3" + string.prototype.includes "^2.0.0" eslint-plugin-prettier@^4.0.0: version "4.2.1" @@ -5061,9 +5090,9 @@ eslint-plugin-prettier@^4.0.0: prettier-linter-helpers "^1.0.0" eslint-plugin-promise@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz#269a3e2772f62875661220631bd4dafcb4083816" - integrity sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig== + version "6.2.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.2.0.tgz#e24ab0e3c0a25fa227d98d9ff612156b5af15945" + integrity sha512-QmAqwizauvnKOlifxyDj2ObfULpHQawlg/zQdgEixur9vl0CvZGv/LCJV2rtj3210QCoeGBzVMfMXqGAOr/4fA== "eslint-plugin-react-hooks@^4.5.0 || 5.0.0-canary-7118f5dd7-20230705": version "4.6.2" @@ -5071,28 +5100,28 @@ eslint-plugin-promise@^6.1.1: integrity sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ== eslint-plugin-react@^7.33.2: - version "7.34.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz#6806b70c97796f5bbfb235a5d3379ece5f4da997" - integrity sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw== + version "7.34.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.34.3.tgz#9965f27bd1250a787b5d4cfcc765e5a5d58dcb7b" + integrity sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA== dependencies: - array-includes "^3.1.7" - array.prototype.findlast "^1.2.4" + array-includes "^3.1.8" + array.prototype.findlast "^1.2.5" array.prototype.flatmap "^1.3.2" array.prototype.toreversed "^1.1.2" - array.prototype.tosorted "^1.1.3" + array.prototype.tosorted "^1.1.4" doctrine "^2.1.0" - es-iterator-helpers "^1.0.17" + es-iterator-helpers "^1.0.19" estraverse "^5.3.0" jsx-ast-utils "^2.4.1 || ^3.0.0" minimatch "^3.1.2" - object.entries "^1.1.7" - object.fromentries "^2.0.7" - object.hasown "^1.1.3" - object.values "^1.1.7" + object.entries "^1.1.8" + object.fromentries "^2.0.8" + object.hasown "^1.1.4" + object.values "^1.2.0" prop-types "^15.8.1" resolve "^2.0.0-next.5" semver "^6.3.1" - string.prototype.matchall "^4.0.10" + string.prototype.matchall "^4.0.11" eslint-scope@^5.1.1: version "5.1.1" @@ -5489,9 +5518,9 @@ for-each@^0.3.3: is-callable "^1.1.3" foreground-child@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" - integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + version "3.2.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.2.1.tgz#767004ccf3a5b30df39bed90718bab43fe0a59f7" + integrity sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA== dependencies: cross-spawn "^7.0.0" signal-exit "^4.0.1" @@ -5609,7 +5638,7 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: +get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== @@ -6039,7 +6068,7 @@ ini@^1.3.5: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== -internal-slot@^1.0.7: +internal-slot@^1.0.4, internal-slot@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== @@ -6068,14 +6097,14 @@ intl-messageformat-parser@^5.3.7: dependencies: "@formatjs/intl-numberformat" "^5.5.2" -intl-messageformat@10.5.12: - version "10.5.12" - resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-10.5.12.tgz#a0c1a20da896b7a1f4ba1b59c8ba5d9943c29c3f" - integrity sha512-izl0uxhy/melhw8gP2r8pGiVieviZmM4v5Oqx3c1/R7g9cwER2smmGfSjcIsp8Y3Q53bfciL/gkxacJRx/dUvg== +intl-messageformat@10.5.14: + version "10.5.14" + resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-10.5.14.tgz#e5bb373f8a37b88fbe647d7b941f3ab2a37ed00a" + integrity sha512-IjC6sI0X7YRjjyVH9aUgdftcmZK7WXdHeil4KwbjDnRWjnVitKpAx3rr6t6di1joFp5188VqKcobOPA6mCLG/w== dependencies: - "@formatjs/ecma402-abstract" "1.18.2" + "@formatjs/ecma402-abstract" "2.0.0" "@formatjs/fast-memoize" "2.2.0" - "@formatjs/icu-messageformat-parser" "2.7.6" + "@formatjs/icu-messageformat-parser" "2.7.8" tslib "^2.4.0" ipaddr.js@^2.0.1: @@ -6083,7 +6112,15 @@ ipaddr.js@^2.0.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== -is-array-buffer@^3.0.4: +is-arguments@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-array-buffer@^3.0.2, is-array-buffer@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== @@ -6142,7 +6179,14 @@ is-ci@^3.0.1: dependencies: ci-info "^3.2.0" -is-core-module@^2.11.0, is-core-module@^2.13.0, is-core-module@^2.13.1, is-core-module@^2.5.0: +is-core-module@^2.11.0, is-core-module@^2.13.0, is-core-module@^2.13.1: + version "2.14.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.14.0.tgz#43b8ef9f46a6a08888db67b1ffd4ec9e3dfd59d1" + integrity sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A== + dependencies: + hasown "^2.0.2" + +is-core-module@^2.5.0: version "2.13.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== @@ -6222,7 +6266,7 @@ is-localhost-ip@^1.4.0: resolved "https://registry.yarnpkg.com/is-localhost-ip/-/is-localhost-ip-1.4.0.tgz#dd66aaabcbb5dbbc943e00adad5f715d2c3b3a1d" integrity sha512-cN7SzlY7BVxSeoJu5equjsZaKSgD4HCfXrTwu0Jgbq5BbT1BU+D7Lyi/l1KO8H0un0JTlxcQaT/GWVapu+DIDg== -is-map@^2.0.3: +is-map@^2.0.2, is-map@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== @@ -6294,7 +6338,7 @@ is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-set@^2.0.3: +is-set@^2.0.2, is-set@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== @@ -6373,9 +6417,9 @@ isarray@^2.0.5: integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== isbot@^5.1.1: - version "5.1.6" - resolved "https://registry.yarnpkg.com/isbot/-/isbot-5.1.6.tgz#579a48515e92a3e064da63a41709815d8e641a30" - integrity sha512-Phksj1A0dBP/M/5xeOx0zWemKlZRQvrbNzI19/HWso0uodiOcR8YYCXN60IdzwbKsGj5LnxPkMy6FuBtgckMNw== + version "5.1.11" + resolved "https://registry.yarnpkg.com/isbot/-/isbot-5.1.11.tgz#55e91e887c806a5f07d18ce04c2efe194215ba36" + integrity sha512-Asuou7OsKVbATgXlrlqdeSRw4fYnD6CNcc0IXy1f5m1AImL2S6IP0xRcRRzjGjoARbegvnBnju9kk5z/Itf70Q== isexe@^2.0.0: version "2.0.0" @@ -7033,9 +7077,9 @@ known-css-properties@^0.29.0: integrity sha512-Ne7wqW7/9Cz54PDt4I3tcV+hAyat8ypyOGzYRJQfdxnnjeWsTxt1cy8pjvvKeI5kfXuyvULyeeAvwvvtAX3ayQ== language-subtag-registry@^0.3.20: - version "0.3.22" - resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d" - integrity sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w== + version "0.3.23" + resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz#23529e04d9e3b74679d70142df3fd2eb6ec572e7" + integrity sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ== language-tags@^1.0.9: version "1.0.9" @@ -7290,9 +7334,9 @@ lower-case@^2.0.2: tslib "^2.0.3" lru-cache@^10.2.0: - version "10.2.2" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.2.tgz#48206bc114c1252940c41b25b41af5b545aca878" - integrity sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ== + version "10.3.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.3.0.tgz#4a4aaf10c84658ab70f79a85a9a3f1e1fb11196b" + integrity sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ== lru-cache@^5.1.1: version "5.1.1" @@ -7308,13 +7352,20 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -magic-string@^0.30.0, magic-string@^0.30.2, magic-string@^0.30.3: +magic-string@^0.30.0, magic-string@^0.30.2: version "0.30.5" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.5.tgz#1994d980bd1c8835dc6e78db7cbd4ae4f24746f9" integrity sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA== dependencies: "@jridgewell/sourcemap-codec" "^1.4.15" +magic-string@^0.30.3: + version "0.30.10" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.10.tgz#123d9c41a0cb5640c892b041d4cfb3bd0aa4b39e" + integrity sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.15" + make-dir@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -7357,11 +7408,11 @@ mathml-tag-names@^2.1.3: integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== maxmind@^4.3.6: - version "4.3.19" - resolved "https://registry.yarnpkg.com/maxmind/-/maxmind-4.3.19.tgz#da97391185b41373961685419f0f12dfd7b97ff9" - integrity sha512-Bu/VEN7ZWAOCjifdZaXJQuN6/yO7+OK35pnJsqmz8sOndK3KQFvJoY+6HX09/MmLLqtCfa+sMK0iaQOaTejGNA== + version "4.3.20" + resolved "https://registry.yarnpkg.com/maxmind/-/maxmind-4.3.20.tgz#1b85c4b0d9a8df072c5cbe50dc5c54c130a02889" + integrity sha512-xwnLGghs96DyNK3ANs1jzW8+JwS6pBQ3eTh3kNsi23n5wySZb9FgqCghmde5JZFOv23bxGehcyh6lT522llNKw== dependencies: - mmdb-lib "2.1.0" + mmdb-lib "2.1.1" tiny-lru "11.2.6" md5@^2.3.0: @@ -7443,7 +7494,7 @@ merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -micromatch@4.0.5, micromatch@^4.0.4, micromatch@^4.0.5: +micromatch@4.0.5, micromatch@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== @@ -7451,6 +7502,14 @@ micromatch@4.0.5, micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.2" picomatch "^2.3.1" +micromatch@^4.0.4: + version "4.0.7" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5" + integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" @@ -7500,9 +7559,9 @@ minimatch@^5.0.1: brace-expansion "^2.0.1" minimatch@^9.0.1: - version "9.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" - integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== dependencies: brace-expansion "^2.0.1" @@ -7533,9 +7592,9 @@ minipass@^5.0.0: integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== "minipass@^5.0.0 || ^6.0.2 || ^7.0.0": - version "7.1.1" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.1.tgz#f7f85aff59aa22f110b20e27692465cf3bf89481" - integrity sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA== + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== minizlib@^2.1.1: version "2.1.2" @@ -7550,10 +7609,10 @@ mkdirp@^1.0.3: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mmdb-lib@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mmdb-lib/-/mmdb-lib-2.1.0.tgz#c2456caaf4c7ffa056f77575da6d40988e9e878b" - integrity sha512-tdDTZmnI5G4UoSctv2KxM/3VQt2XRj4CmR5R4VsAWsOUcS3LysHR34wtixWm/pXxXdkBDuN92auxkC0T2+qd1Q== +mmdb-lib@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/mmdb-lib/-/mmdb-lib-2.1.1.tgz#c0d0bd35dc1fca41f0ebd043e43227ab04eb1792" + integrity sha512-yx8H/1H5AfnufiLnzzPqPf4yr/dKU9IFT1rPVwSkrKWHsQEeVVd6+X+L0nUbXhlEFTu3y/7hu38CFmEVgzvyeg== moment-timezone@^0.5.35: version "0.5.45" @@ -7743,9 +7802,17 @@ object-assign@^4, object-assign@^4.1.1: integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== object-inspect@^1.13.1: - version "1.13.1" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" - integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + version "1.13.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" + integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== + +object-is@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" + integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" object-keys@^1.1.1: version "1.1.1" @@ -7762,7 +7829,7 @@ object.assign@^4.1.4, object.assign@^4.1.5: has-symbols "^1.0.3" object-keys "^1.1.1" -object.entries@^1.1.7: +object.entries@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.8.tgz#bffe6f282e01f4d17807204a24f8edd823599c41" integrity sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ== @@ -7771,7 +7838,7 @@ object.entries@^1.1.7: define-properties "^1.2.1" es-object-atoms "^1.0.0" -object.fromentries@^2.0.7: +object.fromentries@^2.0.7, object.fromentries@^2.0.8: version "2.0.8" resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== @@ -7790,7 +7857,7 @@ object.groupby@^1.0.1: define-properties "^1.2.1" es-abstract "^1.23.2" -object.hasown@^1.1.3: +object.hasown@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.4.tgz#e270ae377e4c120cdcb7656ce66884a6218283dc" integrity sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg== @@ -7799,7 +7866,7 @@ object.hasown@^1.1.3: es-abstract "^1.23.2" es-object-atoms "^1.0.0" -object.values@^1.1.6, object.values@^1.1.7: +object.values@^1.1.6, object.values@^1.1.7, object.values@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.0.tgz#65405a9d92cee68ac2d303002e0b8470a4d9ab1b" integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== @@ -7969,9 +8036,9 @@ path-parse@^1.0.7: integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-scurry@^1.10.1: - version "1.11.0" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.0.tgz#332d64e9726bf667fb348e5a1c71005c09ad741a" - integrity sha512-LNHTaVkzaYaLGlO+0u3rQTz7QrHTFOuKyba9JMTQutkmtNew8dw8wOD7mTU/5fCPZzCWpfW0XnQKzY61P0aTaw== + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== dependencies: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -8810,19 +8877,19 @@ react-hook-form@^7.34.2: integrity sha512-F/TroLjTICipmHeFlMrLtNLceO2xr1jU3CyiNla5zdwsGUGu2UOxxR4UyJgLlhMwLW/Wzp4cpJ7CPfgJIeKdSg== react-intl@^6.5.5: - version "6.6.6" - resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-6.6.6.tgz#67979f790263c5ebd95b6ea581110eea3e7b550f" - integrity sha512-dKXQNUrhZTlCp8uelYW8PHiM4saNKyLmHCfsJYWK0N/kZ/Ien35wjPHB8x9yQcTJbeN/hBOmb4x16iKUrdL9MA== - dependencies: - "@formatjs/ecma402-abstract" "1.18.2" - "@formatjs/icu-messageformat-parser" "2.7.6" - "@formatjs/intl" "2.10.2" - "@formatjs/intl-displaynames" "6.6.6" - "@formatjs/intl-listformat" "7.5.5" + version "6.6.8" + resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-6.6.8.tgz#cb60c90502d0025caf9f86ec298cdc4348da17c2" + integrity sha512-M0pkhzcgV31h++2901BiRXWl69hp2zPyLxRrSwRjd1ErXbNoubz/f4M6DrRTd4OiSUrT4ajRQzrmtS5plG4FtA== + dependencies: + "@formatjs/ecma402-abstract" "2.0.0" + "@formatjs/icu-messageformat-parser" "2.7.8" + "@formatjs/intl" "2.10.4" + "@formatjs/intl-displaynames" "6.6.8" + "@formatjs/intl-listformat" "7.5.7" "@types/hoist-non-react-statics" "^3.3.1" "@types/react" "16 || 17 || 18" hoist-non-react-statics "^3.3.2" - intl-messageformat "10.5.12" + intl-messageformat "10.5.14" tslib "^2.4.0" react-is@^16.13.1, react-is@^16.7.0: @@ -9018,7 +9085,7 @@ regenerator-transform@^0.15.2: dependencies: "@babel/runtime" "^7.8.4" -regexp.prototype.flags@^1.5.2: +regexp.prototype.flags@^1.5.1, regexp.prototype.flags@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== @@ -9141,9 +9208,9 @@ reusify@^1.0.4: integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== rfdc@^1.3.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.1.tgz#2b6d4df52dffe8bb346992a10ea9451f24373a8f" - integrity sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg== + version "1.4.1" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" @@ -9567,6 +9634,13 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" +stop-iteration-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" + integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== + dependencies: + internal-slot "^1.0.4" + streamsearch@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" @@ -9617,7 +9691,15 @@ string-width@^5.0.0, string-width@^5.0.1, string-width@^5.1.2: emoji-regex "^9.2.2" strip-ansi "^7.0.1" -string.prototype.matchall@^4.0.10: +string.prototype.includes@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/string.prototype.includes/-/string.prototype.includes-2.0.0.tgz#8986d57aee66d5460c144620a6d873778ad7289f" + integrity sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +string.prototype.matchall@^4.0.11: version "4.0.11" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz#1092a72c59268d2abaad76582dccc687c0297e0a" integrity sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg== @@ -9770,9 +9852,9 @@ stylelint-config-prettier@^9.0.3: integrity sha512-U44lELgLZhbAD/xy/vncZ2Pq8sh2TnpiPvo38Ifg9+zeioR+LAkHu0i6YORIOxFafZoVg0xqQwex6e6F25S5XA== stylelint-config-recommended@^14.0.0: - version "14.0.0" - resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-14.0.0.tgz#b395c7014838d2aaca1755eebd914d0bb5274994" - integrity sha512-jSkx290CglS8StmrLp2TxAppIajzIBZKYm3IxT89Kg6fGlxbPiTiyH9PS5YUuVAFwaJLl1ikiXX0QWjI0jmgZQ== + version "14.0.1" + resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-14.0.1.tgz#d25e86409aaf79ee6c6085c2c14b33c7e23c90c6" + integrity sha512-bLvc1WOz/14aPImu/cufKAZYfXs/A/owZfSMZ4N+16WGXLoX5lOir53M6odBxvhgmgdxCVnNySJmZKx73T93cg== stylelint-scss@^6.0.0: version "6.1.0" @@ -10037,9 +10119,9 @@ ts-api-utils@^1.0.1: integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== ts-jest@^29.1.2: - version "29.1.2" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.2.tgz#7613d8c81c43c8cb312c6904027257e814c40e09" - integrity sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g== + version "29.1.5" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.5.tgz#d6c0471cc78bffa2cb4664a0a6741ef36cfe8f69" + integrity sha512-UuClSYxM7byvvYfyWdFI+/2UxMmwNyJb0NPkZPQE2hew3RurV7l7zURgOHAd/1I1ZdPpe3GUsXNXAcN8TFKSIg== dependencies: bs-logger "0.x" fast-json-stable-stringify "2.x" @@ -10084,11 +10166,16 @@ tslib@^1.8.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0: +tslib@^2.0.1, tslib@^2.0.3: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== +tslib@^2.1.0, tslib@^2.4.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" + integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -10206,10 +10293,10 @@ typescript@^4.0, typescript@^4.5: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== -typescript@^5.4.3: - version "5.4.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611" - integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== +typescript@^5.5.3: + version "5.5.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.3.tgz#e1b0a3c394190838a0b168e771b0ad56a0af0faa" + integrity sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ== unbox-primitive@^1.0.2: version "1.0.2" @@ -10415,7 +10502,7 @@ which-collection@^1.0.1: is-weakmap "^2.0.2" is-weakset "^2.0.3" -which-typed-array@^1.1.14, which-typed-array@^1.1.15, which-typed-array@^1.1.9: +which-typed-array@^1.1.13, which-typed-array@^1.1.14, which-typed-array@^1.1.15, which-typed-array@^1.1.9: version "1.1.15" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== @@ -10607,8 +10694,8 @@ yup@^0.32.11: toposort "^2.0.2" zustand@^4.3.8: - version "4.5.2" - resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.2.tgz#fddbe7cac1e71d45413b3682cdb47b48034c3848" - integrity sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g== + version "4.5.4" + resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.4.tgz#63abdd81edfb190bc61e0bbae045cc4d52158a05" + integrity sha512-/BPMyLKJPtFEvVL0E9E9BTUM63MNyhPGlvxk1XjrfWTUlV+BR8jufjsovHzrtR6YNcBEcL7cMHovL1n9xHawEg== dependencies: use-sync-external-store "1.2.0" From 9bf34bd5e48a86d7ee3038203b865022d75514e3 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Thu, 4 Jul 2024 20:30:15 -0700 Subject: [PATCH 018/132] Support Clickhouse insert without Kafka. --- src/lib/clickhouse.ts | 11 +++++++++-- src/queries/analytics/eventData/saveEventData.ts | 8 +++++++- src/queries/analytics/events/saveEvent.ts | 8 +++++++- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/lib/clickhouse.ts b/src/lib/clickhouse.ts index b6d0fc3f36..cd057cd70a 100644 --- a/src/lib/clickhouse.ts +++ b/src/lib/clickhouse.ts @@ -32,7 +32,7 @@ function getClient() { } = new URL(process.env.CLICKHOUSE_URL); const client = createClient({ - host: `${protocol}//${hostname}:${port}`, + url: `${protocol}//${hostname}:${port}`, database: pathname.replace('/', ''), username: username, password, @@ -136,7 +136,13 @@ async function rawQuery( format: 'JSONEachRow', }); - return resultSet.json(); + return resultSet.json() as T; +} + +async function insert(table: string, values: any[]) { + await connect(); + + return clickhouse.insert({ table, values, format: 'JSONEachRow' }); } async function findUnique(data: any[]) { @@ -172,4 +178,5 @@ export default { findUnique, findFirst, rawQuery, + insert, }; diff --git a/src/queries/analytics/eventData/saveEventData.ts b/src/queries/analytics/eventData/saveEventData.ts index 0ed3c8b016..222e8b7326 100644 --- a/src/queries/analytics/eventData/saveEventData.ts +++ b/src/queries/analytics/eventData/saveEventData.ts @@ -3,6 +3,7 @@ import { DATA_TYPE } from 'lib/constants'; import { uuid } from 'lib/crypto'; import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; import { flattenJSON, getStringValue } from 'lib/data'; +import clickhouse from 'lib/clickhouse'; import kafka from 'lib/kafka'; import prisma from 'lib/prisma'; import { DynamicData } from 'lib/types'; @@ -59,6 +60,7 @@ async function clickhouseQuery(data: { }) { const { websiteId, sessionId, eventId, urlPath, eventName, eventData, createdAt } = data; + const { insert } = clickhouse; const { getDateFormat, sendMessages } = kafka; const jsonKeys = flattenJSON(eventData); @@ -79,7 +81,11 @@ async function clickhouseQuery(data: { }; }); - await sendMessages(messages, 'event_data'); + if (kafka.enabled) { + await sendMessages(messages, 'event_data'); + } else { + await insert('event_data', messages); + } return data; } diff --git a/src/queries/analytics/events/saveEvent.ts b/src/queries/analytics/events/saveEvent.ts index 25bcf9e7af..9a5787a38f 100644 --- a/src/queries/analytics/events/saveEvent.ts +++ b/src/queries/analytics/events/saveEvent.ts @@ -1,5 +1,6 @@ import { EVENT_NAME_LENGTH, URL_LENGTH, EVENT_TYPE, PAGE_TITLE_LENGTH } from 'lib/constants'; import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; +import clickhouse from 'lib/clickhouse'; import kafka from 'lib/kafka'; import prisma from 'lib/prisma'; import { uuid } from 'lib/crypto'; @@ -134,6 +135,7 @@ async function clickhouseQuery(data: { city, ...args } = data; + const { insert } = clickhouse; const { getDateFormat, sendMessage } = kafka; const eventId = uuid(); const createdAt = getDateFormat(new Date()); @@ -164,7 +166,11 @@ async function clickhouseQuery(data: { created_at: createdAt, }; - await sendMessage(message, 'event'); + if (kafka.enabled) { + await sendMessage(message, 'event'); + } else { + await insert('website_event', [message]); + } if (eventData) { await saveEventData({ From 26462cffbc697b23527415005639cc6a7e183afa Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 5 Jul 2024 02:04:51 -0700 Subject: [PATCH 019/132] Support clickhouse for save session data. --- src/queries/analytics/sessions/saveSessionData.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/queries/analytics/sessions/saveSessionData.ts b/src/queries/analytics/sessions/saveSessionData.ts index 429c6e281b..6ab5d6ae1d 100644 --- a/src/queries/analytics/sessions/saveSessionData.ts +++ b/src/queries/analytics/sessions/saveSessionData.ts @@ -5,6 +5,7 @@ import prisma from 'lib/prisma'; import { DynamicData } from 'lib/types'; import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; import kafka from 'lib/kafka'; +import clickhouse from 'lib/clickhouse'; export async function saveSessionData(data: { websiteId: string; @@ -81,6 +82,7 @@ async function clickhouseQuery(data: { }) { const { websiteId, sessionId, sessionData, createdAt } = data; + const { insert } = clickhouse; const { getDateFormat, sendMessages } = kafka; const jsonKeys = flattenJSON(sessionData); @@ -98,7 +100,11 @@ async function clickhouseQuery(data: { }; }); - await sendMessages(messages, 'session_data'); + if (kafka.enabled) { + await sendMessages(messages, 'session_data'); + } else { + await insert('event_data', messages); + } return data; } From 72b3b6158ab3999767de351e30122ac56f2dd4cf Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 5 Jul 2024 02:29:12 -0700 Subject: [PATCH 020/132] Changed kafka method signature. --- src/lib/kafka.ts | 4 ++-- src/queries/analytics/eventData/saveEventData.ts | 2 +- src/queries/analytics/events/saveEvent.ts | 2 +- src/queries/analytics/sessions/saveSessionData.ts | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/kafka.ts b/src/lib/kafka.ts index da38baa44f..76692afb4b 100644 --- a/src/lib/kafka.ts +++ b/src/lib/kafka.ts @@ -61,8 +61,8 @@ function getDateFormat(date: Date, format?: string): string { } async function sendMessage( - message: { [key: string]: string | number }, topic: string, + message: { [key: string]: string | number }, ): Promise { await connect(); @@ -77,7 +77,7 @@ async function sendMessage( }); } -async function sendMessages(messages: { [key: string]: string | number }[], topic: string) { +async function sendMessages(topic: string, messages: { [key: string]: string | number }[]) { await connect(); await producer.send({ diff --git a/src/queries/analytics/eventData/saveEventData.ts b/src/queries/analytics/eventData/saveEventData.ts index 222e8b7326..9c7218e97b 100644 --- a/src/queries/analytics/eventData/saveEventData.ts +++ b/src/queries/analytics/eventData/saveEventData.ts @@ -82,7 +82,7 @@ async function clickhouseQuery(data: { }); if (kafka.enabled) { - await sendMessages(messages, 'event_data'); + await sendMessages('event_data', messages); } else { await insert('event_data', messages); } diff --git a/src/queries/analytics/events/saveEvent.ts b/src/queries/analytics/events/saveEvent.ts index 9a5787a38f..cd41b7a318 100644 --- a/src/queries/analytics/events/saveEvent.ts +++ b/src/queries/analytics/events/saveEvent.ts @@ -167,7 +167,7 @@ async function clickhouseQuery(data: { }; if (kafka.enabled) { - await sendMessage(message, 'event'); + await sendMessage('event', message); } else { await insert('website_event', [message]); } diff --git a/src/queries/analytics/sessions/saveSessionData.ts b/src/queries/analytics/sessions/saveSessionData.ts index 6ab5d6ae1d..1f5c149454 100644 --- a/src/queries/analytics/sessions/saveSessionData.ts +++ b/src/queries/analytics/sessions/saveSessionData.ts @@ -101,9 +101,9 @@ async function clickhouseQuery(data: { }); if (kafka.enabled) { - await sendMessages(messages, 'session_data'); + await sendMessages('session_data', messages); } else { - await insert('event_data', messages); + await insert('session_data', messages); } return data; From 082a751ffec85601cbc66d218d5ebf638f664edb Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 5 Jul 2024 02:52:36 -0700 Subject: [PATCH 021/132] Should allow non http referrers. Closes #2831 --- src/pages/api/send.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/api/send.ts b/src/pages/api/send.ts index 3431795edd..313184202d 100644 --- a/src/pages/api/send.ts +++ b/src/pages/api/send.ts @@ -121,7 +121,7 @@ export default async (req: NextApiRequestCollect, res: NextApiResponse) => { urlPath = '/'; } - if (referrerPath?.startsWith('http')) { + if (/^[\w-]+:\/\/\w+/.test(referrerPath)) { const refUrl = new URL(referrer); referrerPath = refUrl.pathname; referrerQuery = refUrl.search.substring(1); From db36c37d32038c52b066c80a22bb3c4cbca5c5cf Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Mon, 8 Jul 2024 01:45:54 -0700 Subject: [PATCH 022/132] Updated session and events queries. Added sessions page. --- .../websites/[websiteId]/WebsiteHeader.tsx | 9 ++- .../EventDataMetricsBar.module.css | 0 .../EventDataMetricsBar.tsx | 0 .../{event-data => events}/EventDataPage.tsx | 0 .../{event-data => events}/EventDataTable.tsx | 0 .../EventDataValueTable.tsx | 0 .../WebsiteEventData.module.css | 0 .../WebsiteEventData.tsx | 0 .../{event-data => events}/page.tsx | 0 .../[websiteId]/realtime/RealtimeLog.tsx | 5 +- .../sessions/SessionsDataTable.tsx | 25 ++++++++ .../[websiteId]/sessions/SessionsPage.tsx | 14 +++++ .../[websiteId]/sessions/SessionsTable.tsx | 21 +++++++ .../websites/[websiteId]/sessions/page.tsx | 10 ++++ src/app/api/scripts/telemetry/route.ts | 28 +++++++++ src/components/hooks/index.ts | 1 + src/components/hooks/queries/useSessions.ts | 20 +++++++ src/lib/clickhouse.ts | 54 +++++++++++++++-- src/lib/prisma.ts | 43 +++++++++++--- src/pages/api/scripts/telemetry.ts | 24 -------- .../api/websites/[websiteId]/sessions.ts | 42 ++++++++++++++ .../analytics/events/getEventMetrics.ts | 8 +-- src/queries/analytics/events/getEvents.ts | 58 ++++++++----------- src/queries/analytics/getRealtimeData.ts | 12 ++-- src/queries/analytics/getValues.ts | 4 +- src/queries/analytics/getWebsiteStats.ts | 4 +- .../analytics/pageviews/getPageviewMetrics.ts | 42 ++++++++------ .../analytics/pageviews/getPageviewStats.ts | 10 ++-- src/queries/analytics/reports/getInsights.ts | 4 +- src/queries/analytics/reports/getRetention.ts | 17 +++--- src/queries/analytics/reports/getRevenue.ts | 10 ++-- .../analytics/sessions/getSessionStats.ts | 10 ++-- src/queries/analytics/sessions/getSessions.ts | 53 +++++++---------- src/queries/index.ts | 10 ++-- src/queries/{admin => prisma}/report.ts | 6 +- src/queries/{admin => prisma}/team.ts | 0 src/queries/{admin => prisma}/teamUser.ts | 0 src/queries/{admin => prisma}/user.ts | 6 +- src/queries/{admin => prisma}/website.ts | 6 +- 39 files changed, 376 insertions(+), 180 deletions(-) rename src/app/(main)/websites/[websiteId]/{event-data => events}/EventDataMetricsBar.module.css (100%) rename src/app/(main)/websites/[websiteId]/{event-data => events}/EventDataMetricsBar.tsx (100%) rename src/app/(main)/websites/[websiteId]/{event-data => events}/EventDataPage.tsx (100%) rename src/app/(main)/websites/[websiteId]/{event-data => events}/EventDataTable.tsx (100%) rename src/app/(main)/websites/[websiteId]/{event-data => events}/EventDataValueTable.tsx (100%) rename src/app/(main)/websites/[websiteId]/{event-data => events}/WebsiteEventData.module.css (100%) rename src/app/(main)/websites/[websiteId]/{event-data => events}/WebsiteEventData.tsx (100%) rename src/app/(main)/websites/[websiteId]/{event-data => events}/page.tsx (100%) create mode 100644 src/app/(main)/websites/[websiteId]/sessions/SessionsDataTable.tsx create mode 100644 src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx create mode 100644 src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx create mode 100644 src/app/(main)/websites/[websiteId]/sessions/page.tsx create mode 100644 src/app/api/scripts/telemetry/route.ts create mode 100644 src/components/hooks/queries/useSessions.ts delete mode 100644 src/pages/api/scripts/telemetry.ts create mode 100644 src/pages/api/websites/[websiteId]/sessions.ts rename src/queries/{admin => prisma}/report.ts (93%) rename src/queries/{admin => prisma}/team.ts (100%) rename src/queries/{admin => prisma}/teamUser.ts (100%) rename src/queries/{admin => prisma}/user.ts (98%) rename src/queries/{admin => prisma}/website.ts (97%) diff --git a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx index 0cbaeb44d7..bbd6460aa0 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx @@ -46,9 +46,14 @@ export function WebsiteHeader({ path: '/reports', }, { - label: formatMessage(labels.eventData), + label: formatMessage(labels.sessions), + icon: , + path: '/sessions', + }, + { + label: formatMessage(labels.events), icon: , - path: '/event-data', + path: '/events', }, ]; diff --git a/src/app/(main)/websites/[websiteId]/event-data/EventDataMetricsBar.module.css b/src/app/(main)/websites/[websiteId]/events/EventDataMetricsBar.module.css similarity index 100% rename from src/app/(main)/websites/[websiteId]/event-data/EventDataMetricsBar.module.css rename to src/app/(main)/websites/[websiteId]/events/EventDataMetricsBar.module.css diff --git a/src/app/(main)/websites/[websiteId]/event-data/EventDataMetricsBar.tsx b/src/app/(main)/websites/[websiteId]/events/EventDataMetricsBar.tsx similarity index 100% rename from src/app/(main)/websites/[websiteId]/event-data/EventDataMetricsBar.tsx rename to src/app/(main)/websites/[websiteId]/events/EventDataMetricsBar.tsx diff --git a/src/app/(main)/websites/[websiteId]/event-data/EventDataPage.tsx b/src/app/(main)/websites/[websiteId]/events/EventDataPage.tsx similarity index 100% rename from src/app/(main)/websites/[websiteId]/event-data/EventDataPage.tsx rename to src/app/(main)/websites/[websiteId]/events/EventDataPage.tsx diff --git a/src/app/(main)/websites/[websiteId]/event-data/EventDataTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventDataTable.tsx similarity index 100% rename from src/app/(main)/websites/[websiteId]/event-data/EventDataTable.tsx rename to src/app/(main)/websites/[websiteId]/events/EventDataTable.tsx diff --git a/src/app/(main)/websites/[websiteId]/event-data/EventDataValueTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventDataValueTable.tsx similarity index 100% rename from src/app/(main)/websites/[websiteId]/event-data/EventDataValueTable.tsx rename to src/app/(main)/websites/[websiteId]/events/EventDataValueTable.tsx diff --git a/src/app/(main)/websites/[websiteId]/event-data/WebsiteEventData.module.css b/src/app/(main)/websites/[websiteId]/events/WebsiteEventData.module.css similarity index 100% rename from src/app/(main)/websites/[websiteId]/event-data/WebsiteEventData.module.css rename to src/app/(main)/websites/[websiteId]/events/WebsiteEventData.module.css diff --git a/src/app/(main)/websites/[websiteId]/event-data/WebsiteEventData.tsx b/src/app/(main)/websites/[websiteId]/events/WebsiteEventData.tsx similarity index 100% rename from src/app/(main)/websites/[websiteId]/event-data/WebsiteEventData.tsx rename to src/app/(main)/websites/[websiteId]/events/WebsiteEventData.tsx diff --git a/src/app/(main)/websites/[websiteId]/event-data/page.tsx b/src/app/(main)/websites/[websiteId]/events/page.tsx similarity index 100% rename from src/app/(main)/websites/[websiteId]/event-data/page.tsx rename to src/app/(main)/websites/[websiteId]/events/page.tsx diff --git a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx index cbdeb1ac43..700e83aeef 100644 --- a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx +++ b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx @@ -144,7 +144,10 @@ 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)); diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsDataTable.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsDataTable.tsx new file mode 100644 index 0000000000..9e9f97e9ef --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsDataTable.tsx @@ -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 ( + + {({ data }) => } + + ); +} diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx new file mode 100644 index 0000000000..e95145a7da --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx @@ -0,0 +1,14 @@ +'use client'; +import WebsiteHeader from '../WebsiteHeader'; +import SessionsDataTable from './SessionsDataTable'; + +export function SessionsPage({ websiteId }) { + return ( + <> + + + + ); +} + +export default SessionsPage; diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx new file mode 100644 index 0000000000..41c0fef353 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx @@ -0,0 +1,21 @@ +import { GridColumn, GridTable, useBreakpoint } from 'react-basics'; +import { useMessages } from 'components/hooks'; + +export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean }) { + const { formatMessage, labels } = useMessages(); + const breakpoint = useBreakpoint(); + + return ( + + + + + + + + + + ); +} + +export default SessionsTable; diff --git a/src/app/(main)/websites/[websiteId]/sessions/page.tsx b/src/app/(main)/websites/[websiteId]/sessions/page.tsx new file mode 100644 index 0000000000..771f682ddc --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/page.tsx @@ -0,0 +1,10 @@ +import SessionsPage from './SessionsPage'; +import { Metadata } from 'next'; + +export default function ({ params: { websiteId } }) { + return ; +} + +export const metadata: Metadata = { + title: 'Sessions', +}; diff --git a/src/app/api/scripts/telemetry/route.ts b/src/app/api/scripts/telemetry/route.ts new file mode 100644 index 0000000000..ecd83fcb1d --- /dev/null +++ b/src/app/api/scripts/telemetry/route.ts @@ -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', + }, + }); +} diff --git a/src/components/hooks/index.ts b/src/components/hooks/index.ts index df4fbd88fa..86abdd84c6 100644 --- a/src/components/hooks/index.ts +++ b/src/components/hooks/index.ts @@ -5,6 +5,7 @@ export * from './queries/useLogin'; export * from './queries/useRealtime'; export * from './queries/useReport'; export * from './queries/useReports'; +export * from './queries/useSessions'; export * from './queries/useShareToken'; export * from './queries/useTeam'; export * from './queries/useTeams'; diff --git a/src/components/hooks/queries/useSessions.ts b/src/components/hooks/queries/useSessions.ts new file mode 100644 index 0000000000..c54c3acd1b --- /dev/null +++ b/src/components/hooks/queries/useSessions.ts @@ -0,0 +1,20 @@ +import { useApi } from './useApi'; +import { useFilterQuery } from './useFilterQuery'; +import useModified from '../useModified'; + +export function useSessions(websiteId: string, params?: { [key: string]: string | number }) { + const { get } = useApi(); + const { modified } = useModified(`websites`); + + return useFilterQuery({ + queryKey: ['sessions', { websiteId, modified, ...params }], + queryFn: (data: any) => { + return get(`/websites/${websiteId}/sessions`, { + ...data, + ...params, + }); + }, + }); +} + +export default useSessions; diff --git a/src/lib/clickhouse.ts b/src/lib/clickhouse.ts index cd057cd70a..e716d6493f 100644 --- a/src/lib/clickhouse.ts +++ b/src/lib/clickhouse.ts @@ -2,8 +2,8 @@ import { ClickHouseClient, createClient } from '@clickhouse/client'; import dateFormat from 'dateformat'; import debug from 'debug'; import { CLICKHOUSE } from 'lib/db'; -import { QueryFilters, QueryOptions } from './types'; -import { OPERATORS } from './constants'; +import { PageParams, QueryFilters, QueryOptions } from './types'; +import { DEFAULT_PAGE_SIZE, OPERATORS } from './constants'; import { fetchWebsite } from './load'; import { maxDate } from './date'; import { filtersToArray } from './params'; @@ -47,11 +47,11 @@ function getClient() { return client; } -function getDateStringQuery(data: any, unit: string | number) { +function getDateStringSQL(data: any, unit: string | number) { return `formatDateTime(${data}, '${CLICKHOUSE_DATE_FORMATS[unit]}')`; } -function getDateQuery(field: string, unit: string, timezone?: string) { +function getDateSQL(field: string, unit: string, timezone?: string) { if (timezone) { return `date_trunc('${unit}', ${field}, '${timezone}')`; } @@ -95,6 +95,20 @@ function getFilterQuery(filters: QueryFilters = {}, options: QueryOptions = {}) return query.join('\n'); } +function getDateQuery(filters: QueryFilters = {}) { + const { startDate, endDate } = filters; + + if (startDate) { + if (endDate) { + return `and created_at between {startDate:DateTime64} and {endDate:DateTime64}`; + } else { + return `and created_at >= {startDate:DateTime64}`; + } + } + + return ''; +} + function getFilterParams(filters: QueryFilters = {}) { return filtersToArray(filters).reduce((obj, { name, value }) => { if (name && value !== undefined) { @@ -110,6 +124,7 @@ async function parseFilters(websiteId: string, filters: QueryFilters = {}, optio return { filterQuery: getFilterQuery(filters, options), + dateQuery: getDateQuery(filters), params: { ...getFilterParams(filters), websiteId, @@ -119,6 +134,32 @@ async function parseFilters(websiteId: string, filters: QueryFilters = {}, optio }; } +async function pagedQuery( + query: string, + queryParams: { [key: string]: any }, + pageParams: PageParams = {}, +) { + const { page = 1, pageSize, orderBy, sortDescending = false } = pageParams; + const size = +pageSize || DEFAULT_PAGE_SIZE; + const offset = +size * (page - 1); + const direction = sortDescending ? 'desc' : 'asc'; + + const statements = [ + orderBy && `order by ${orderBy} ${direction}`, + +size > 0 && `limit ${+size} offset ${offset}`, + ] + .filter(n => n) + .join('\n'); + + const count = await rawQuery(`select count(*) as num from (${query}) t`, queryParams).then( + res => res[0].num, + ); + + const data = await rawQuery(`${query}${statements}`, queryParams); + + return { data, count, page: +page, pageSize: size, orderBy }; +} + async function rawQuery( query: string, params: Record = {}, @@ -170,11 +211,12 @@ export default { client: clickhouse, log, connect, - getDateStringQuery, - getDateQuery, + getDateStringSQL, + getDateSQL, getDateFormat, getFilterQuery, parseFilters, + pagedQuery, findUnique, findFirst, rawQuery, diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts index 6250f2e546..28835414fc 100644 --- a/src/lib/prisma.ts +++ b/src/lib/prisma.ts @@ -60,7 +60,7 @@ function getCastColumnQuery(field: string, type: string): string { } } -function getDateQuery(field: string, unit: string, timezone?: string): string { +function getDateSQL(field: string, unit: string, timezone?: string): string { const db = getDatabaseType(); if (db === POSTGRESQL) { @@ -81,7 +81,19 @@ function getDateQuery(field: string, unit: string, timezone?: string): string { } } -function getTimestampDiffQuery(field1: string, field2: string): string { +export function getTimestampSQL(field: string) { + const db = getDatabaseType(); + + if (db === POSTGRESQL) { + return `floor(extract(epoch from ${field}))`; + } + + if (db === MYSQL) { + return `UNIX_TIMESTAMP(${field})`; + } +} + +function getTimestampDiffSQL(field1: string, field2: string): string { const db = getDatabaseType(); if (db === POSTGRESQL) { @@ -93,7 +105,7 @@ function getTimestampDiffQuery(field1: string, field2: string): string { } } -function getSearchQuery(column: string): string { +function getSearchSQL(column: string): string { const db = getDatabaseType(); const like = db === POSTGRESQL ? 'ilike' : 'like'; @@ -137,6 +149,20 @@ function getFilterQuery(filters: QueryFilters = {}, options: QueryOptions = {}): return query.join('\n'); } +function getDateQuery(filters: QueryFilters = {}) { + const { startDate, endDate } = filters; + + if (startDate) { + if (endDate) { + return `and website_event.created_at between {{startDate}} and {{endDate}}`; + } else { + return `and website_event.created_at >= {{startDate}}`; + } + } + + return ''; +} + function getFilterParams(filters: QueryFilters = {}) { return filtersToArray(filters).reduce((obj, { name, operator, value }) => { obj[name] = [OPERATORS.contains, OPERATORS.doesNotContain].includes(operator) @@ -161,6 +187,7 @@ async function parseFilters( ? `inner join session on website_event.session_id = session.session_id` : '', filterQuery: getFilterQuery(filters, options), + dateQuery: getDateQuery(filters), params: { ...getFilterParams(filters), websiteId, @@ -191,8 +218,8 @@ async function rawQuery(sql: string, data: object): Promise { return prisma.rawQuery(query, params); } -async function pagedQuery(model: string, criteria: T, filters: PageParams) { - const { page = 1, pageSize, orderBy, sortDescending = false } = filters || {}; +async function pagedQuery(model: string, criteria: T, pageParams: PageParams) { + const { page = 1, pageSize, orderBy, sortDescending = false } = pageParams || {}; const size = +pageSize || DEFAULT_PAGE_SIZE; const data = await prisma.client[model].findMany({ @@ -256,11 +283,11 @@ export default { getAddIntervalQuery, getCastColumnQuery, getDayDiffQuery, - getDateQuery, + getDateSQL, getFilterQuery, getSearchParameters, - getTimestampDiffQuery, - getSearchQuery, + getTimestampDiffSQL, + getSearchSQL, getQueryMode, pagedQuery, parseFilters, diff --git a/src/pages/api/scripts/telemetry.ts b/src/pages/api/scripts/telemetry.ts deleted file mode 100644 index a8a8872e53..0000000000 --- a/src/pages/api/scripts/telemetry.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ok } from 'next-basics'; -import { CURRENT_VERSION, TELEMETRY_PIXEL } from 'lib/constants'; -import { NextApiRequest, NextApiResponse } from 'next'; - -export default function handler(req: NextApiRequest, res: NextApiResponse) { - if (process.env.NODE_ENV === 'production') { - res.setHeader('content-type', 'text/javascript'); - - if (process.env.DISABLE_TELEMETRY || process.env.PRIVATE_MODE) { - return res.send('/* telemetry disabled */'); - } - - 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 res.send(script.replace(/\s\s+/g, '')); - } - - return ok(res); -} diff --git a/src/pages/api/websites/[websiteId]/sessions.ts b/src/pages/api/websites/[websiteId]/sessions.ts new file mode 100644 index 0000000000..21cacb1c2b --- /dev/null +++ b/src/pages/api/websites/[websiteId]/sessions.ts @@ -0,0 +1,42 @@ +import * as yup from 'yup'; +import { canViewWebsite } from 'lib/auth'; +import { useAuth, useCors, useValidate } from 'lib/middleware'; +import { NextApiRequestQueryBody, PageParams } from 'lib/types'; +import { NextApiResponse } from 'next'; +import { methodNotAllowed, ok, unauthorized } from 'next-basics'; +import { pageInfo } from 'lib/schema'; +import { getSessions } from 'queries'; + +export interface ReportsRequestQuery extends PageParams { + websiteId: string; +} + +const schema = { + GET: yup.object().shape({ + websiteId: yup.string().uuid().required(), + ...pageInfo, + }), +}; + +export default async ( + req: NextApiRequestQueryBody, + res: NextApiResponse, +) => { + await useCors(req, res); + await useAuth(req, res); + await useValidate(schema, req, res); + + const { websiteId } = req.query; + + if (req.method === 'GET') { + if (!(await canViewWebsite(req.auth, websiteId))) { + return unauthorized(res); + } + + const data = await getSessions(websiteId, {}, req.query); + + return ok(res, data); + } + + return methodNotAllowed(res); +}; diff --git a/src/queries/analytics/events/getEventMetrics.ts b/src/queries/analytics/events/getEventMetrics.ts index 32cccd3e85..8efbf7694f 100644 --- a/src/queries/analytics/events/getEventMetrics.ts +++ b/src/queries/analytics/events/getEventMetrics.ts @@ -15,7 +15,7 @@ export async function getEventMetrics( async function relationalQuery(websiteId: string, filters: QueryFilters) { const { timezone = 'utc', unit = 'day' } = filters; - const { rawQuery, getDateQuery, parseFilters } = prisma; + const { rawQuery, getDateSQL, parseFilters } = prisma; const { filterQuery, joinSession, params } = await parseFilters(websiteId, { ...filters, eventType: EVENT_TYPE.customEvent, @@ -25,7 +25,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { ` select event_name x, - ${getDateQuery('website_event.created_at', unit, timezone)} t, + ${getDateSQL('website_event.created_at', unit, timezone)} t, count(*) y from website_event ${joinSession} @@ -45,7 +45,7 @@ async function clickhouseQuery( filters: QueryFilters, ): Promise<{ x: string; t: string; y: number }[]> { const { timezone = 'UTC', unit = 'day' } = filters; - const { rawQuery, getDateQuery, parseFilters } = clickhouse; + const { rawQuery, getDateSQL, parseFilters } = clickhouse; const { filterQuery, params } = await parseFilters(websiteId, { ...filters, eventType: EVENT_TYPE.customEvent, @@ -55,7 +55,7 @@ async function clickhouseQuery( ` select event_name x, - ${getDateQuery('created_at', unit, timezone)} t, + ${getDateSQL('created_at', unit, timezone)} t, count(*) y from website_event where website_id = {websiteId:UUID} diff --git a/src/queries/analytics/events/getEvents.ts b/src/queries/analytics/events/getEvents.ts index c333242e35..a00f684851 100644 --- a/src/queries/analytics/events/getEvents.ts +++ b/src/queries/analytics/events/getEvents.ts @@ -1,45 +1,33 @@ import clickhouse from 'lib/clickhouse'; import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; import prisma from 'lib/prisma'; -import { QueryFilters } from 'lib/types'; +import { PageParams, QueryFilters } from 'lib/types'; -export function getEvents(...args: [websiteId: string, filters: QueryFilters]) { +export function getEvents( + ...args: [websiteId: string, filters: QueryFilters, pageParams?: PageParams] +) { return runQuery({ [PRISMA]: () => relationalQuery(...args), [CLICKHOUSE]: () => clickhouseQuery(...args), }); } -function relationalQuery(websiteId: string, filters: QueryFilters) { - const { startDate } = filters; +async function relationalQuery(websiteId: string, filters: QueryFilters, pageParams?: PageParams) { + const { pagedQuery } = prisma; - return prisma.client.websiteEvent - .findMany({ - where: { - websiteId, - createdAt: { - gte: startDate, - }, - }, - orderBy: { - createdAt: 'desc', - }, - }) - .then(a => { - return Object.values(a).map(a => { - return { - ...a, - timestamp: new Date(a.createdAt).getTime() / 1000, - }; - }); - }); + const where = { + ...filters, + id: websiteId, + }; + + return pagedQuery('website_event', { where }, pageParams); } -function clickhouseQuery(websiteId: string, filters: QueryFilters) { - const { rawQuery } = clickhouse; - const { startDate } = filters; +async function clickhouseQuery(websiteId: string, filters: QueryFilters, pageParams?: PageParams) { + const { pagedQuery, parseFilters } = clickhouse; + const { params, dateQuery, filterQuery } = await parseFilters(websiteId, filters); - return rawQuery( + return pagedQuery( ` select event_id as id, @@ -48,16 +36,20 @@ function clickhouseQuery(websiteId: string, filters: QueryFilters) { created_at as createdAt, toUnixTimestamp(created_at) as timestamp, url_path as urlPath, + url_query as urlQuery, + referrer_path as referrerPath, + referrer_query as referrerQuery, referrer_domain as referrerDomain, + page_title as pageTitle, + event_type as eventType, event_name as eventName from website_event where website_id = {websiteId:UUID} - and created_at >= {startDate:DateTime64} + ${dateQuery} + ${filterQuery} order by created_at desc `, - { - websiteId, - startDate, - }, + params, + pageParams, ); } diff --git a/src/queries/analytics/getRealtimeData.ts b/src/queries/analytics/getRealtimeData.ts index b42fbc50af..afc7ff8fa9 100644 --- a/src/queries/analytics/getRealtimeData.ts +++ b/src/queries/analytics/getRealtimeData.ts @@ -19,15 +19,15 @@ export async function getRealtimeData( const { startDate, timezone } = criteria; const filters = { startDate, endDate: new Date(), unit: 'minute', timezone }; const [events, sessions, pageviews, sessionviews] = await Promise.all([ - getEvents(websiteId, { startDate }), - getSessions(websiteId, { startDate }), + getEvents(websiteId, { startDate }, { pageSize: 10000 }), + getSessions(websiteId, { startDate }, { pageSize: 10000 }), getPageviewStats(websiteId, filters), getSessionStats(websiteId, filters), ]); const uniques = new Set(); - const sessionStats = sessions.reduce( + const sessionStats = sessions.data.reduce( (obj: { visitors: any; countries: any }, session: { id: any; country: any }) => { const { countries, visitors } = obj; const { id, country } = session; @@ -49,7 +49,7 @@ export async function getRealtimeData( }, ); - const eventStats = events.reduce( + const eventStats = events.data.reduce( ( obj: { urls: any; referrers: any; events: any }, event: { urlPath: any; referrerDomain: any }, @@ -81,9 +81,9 @@ export async function getRealtimeData( visitors: sessionviews, }, totals: { - views: events.filter(e => !e.eventName).length, + views: events.data.filter(e => !e.eventName).length, visitors: uniques.size, - events: events.filter(e => e.eventName).length, + events: events.data.filter(e => e.eventName).length, countries: Object.keys(sessionStats.countries).length, }, timestamp: Date.now(), diff --git a/src/queries/analytics/getValues.ts b/src/queries/analytics/getValues.ts index 7cd349940f..8b1afb3f0b 100644 --- a/src/queries/analytics/getValues.ts +++ b/src/queries/analytics/getValues.ts @@ -18,11 +18,11 @@ async function relationalQuery( endDate: Date, search: string, ) { - const { rawQuery, getSearchQuery } = prisma; + const { rawQuery, getSearchSQL } = prisma; let searchQuery = ''; if (search) { - searchQuery = getSearchQuery(column); + searchQuery = getSearchSQL(column); } return rawQuery( diff --git a/src/queries/analytics/getWebsiteStats.ts b/src/queries/analytics/getWebsiteStats.ts index 2f3c82e865..84ceaf1c10 100644 --- a/src/queries/analytics/getWebsiteStats.ts +++ b/src/queries/analytics/getWebsiteStats.ts @@ -21,7 +21,7 @@ async function relationalQuery( ): Promise< { pageviews: number; visitors: number; visits: number; bounces: number; totaltime: number }[] > { - const { getTimestampDiffQuery, parseFilters, rawQuery } = prisma; + const { getTimestampDiffSQL, parseFilters, rawQuery } = prisma; const { filterQuery, joinSession, params } = await parseFilters(websiteId, { ...filters, eventType: EVENT_TYPE.pageView, @@ -34,7 +34,7 @@ async function relationalQuery( count(distinct t.session_id) as "visitors", count(distinct t.visit_id) as "visits", sum(case when t.c = 1 then 1 else 0 end) as "bounces", - sum(${getTimestampDiffQuery('t.min_time', 't.max_time')}) as "totaltime" + sum(${getTimestampDiffSQL('t.min_time', 't.max_time')}) as "totaltime" from ( select website_event.session_id, diff --git a/src/queries/analytics/pageviews/getPageviewMetrics.ts b/src/queries/analytics/pageviews/getPageviewMetrics.ts index 67ccb04aab..b3ae633abd 100644 --- a/src/queries/analytics/pageviews/getPageviewMetrics.ts +++ b/src/queries/analytics/pageviews/getPageviewMetrics.ts @@ -42,15 +42,18 @@ async function relationalQuery( const aggregrate = type === 'entry' ? 'min' : 'max'; entryExitQuery = ` - JOIN (select visit_id, - ${aggregrate}(created_at) target_created_at - from website_event - where website_event.website_id = {{websiteId::uuid}} - and website_event.created_at between {{startDate}} and {{endDate}} - and event_type = {{eventType}} - group by visit_id) x - ON x.visit_id = website_event.visit_id - and x.target_created_at = website_event.created_at`; + join ( + select visit_id, + ${aggregrate}(created_at) target_created_at + from website_event + where website_event.website_id = {{websiteId::uuid}} + and website_event.created_at between {{startDate}} and {{endDate}} + and event_type = {{eventType}} + group by visit_id + ) x + on x.visit_id = website_event.visit_id + and x.target_created_at = website_event.created_at + `; } return rawQuery( @@ -97,15 +100,18 @@ async function clickhouseQuery( const aggregrate = type === 'entry' ? 'min' : 'max'; entryExitQuery = ` - JOIN (select visit_id, - ${aggregrate}(created_at) target_created_at - from website_event - where website_id = {websiteId:UUID} - and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type = {eventType:UInt32} - group by visit_id) x - ON x.visit_id = website_event.visit_id - and x.target_created_at = website_event.created_at`; + join ( + select visit_id, + ${aggregrate}(created_at) target_created_at + from website_event + where website_id = {websiteId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} + and event_type = {eventType:UInt32} + group by visit_id + ) x + on x.visit_id = website_event.visit_id + and x.target_created_at = website_event.created_at + `; } return rawQuery( diff --git a/src/queries/analytics/pageviews/getPageviewStats.ts b/src/queries/analytics/pageviews/getPageviewStats.ts index a37a15662f..65bc8625fb 100644 --- a/src/queries/analytics/pageviews/getPageviewStats.ts +++ b/src/queries/analytics/pageviews/getPageviewStats.ts @@ -13,7 +13,7 @@ export async function getPageviewStats(...args: [websiteId: string, filters: Que async function relationalQuery(websiteId: string, filters: QueryFilters) { const { timezone = 'utc', unit = 'day' } = filters; - const { getDateQuery, parseFilters, rawQuery } = prisma; + const { getDateSQL, parseFilters, rawQuery } = prisma; const { filterQuery, joinSession, params } = await parseFilters(websiteId, { ...filters, eventType: EVENT_TYPE.pageView, @@ -22,7 +22,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { return rawQuery( ` select - ${getDateQuery('website_event.created_at', unit, timezone)} x, + ${getDateSQL('website_event.created_at', unit, timezone)} x, count(*) y from website_event ${joinSession} @@ -41,7 +41,7 @@ async function clickhouseQuery( filters: QueryFilters, ): Promise<{ x: string; y: number }[]> { const { timezone = 'UTC', unit = 'day' } = filters; - const { parseFilters, rawQuery, getDateStringQuery, getDateQuery } = clickhouse; + const { parseFilters, rawQuery, getDateStringSQL, getDateSQL } = clickhouse; const { filterQuery, params } = await parseFilters(websiteId, { ...filters, eventType: EVENT_TYPE.pageView, @@ -50,11 +50,11 @@ async function clickhouseQuery( return rawQuery( ` select - ${getDateStringQuery('g.t', unit)} as x, + ${getDateStringSQL('g.t', unit)} as x, g.y as y from ( select - ${getDateQuery('created_at', unit, timezone)} as t, + ${getDateSQL('created_at', unit, timezone)} as t, count(*) as y from website_event where website_id = {websiteId:UUID} diff --git a/src/queries/analytics/reports/getInsights.ts b/src/queries/analytics/reports/getInsights.ts index c1a4f1f156..8e6e3289bd 100644 --- a/src/queries/analytics/reports/getInsights.ts +++ b/src/queries/analytics/reports/getInsights.ts @@ -23,7 +23,7 @@ async function relationalQuery( y: number; }[] > { - const { getTimestampDiffQuery, parseFilters, rawQuery } = prisma; + const { getTimestampDiffSQL, parseFilters, rawQuery } = prisma; const { filterQuery, joinSession, params } = await parseFilters( websiteId, { @@ -42,7 +42,7 @@ async function relationalQuery( count(distinct t.session_id) as "visitors", count(distinct t.visit_id) as "visits", sum(case when t.c = 1 then 1 else 0 end) as "bounces", - sum(${getTimestampDiffQuery('t.min_time', 't.max_time')}) as "totaltime", + sum(${getTimestampDiffSQL('t.min_time', 't.max_time')}) as "totaltime", ${parseFieldsByName(fields)} from ( select diff --git a/src/queries/analytics/reports/getRetention.ts b/src/queries/analytics/reports/getRetention.ts index de495cc48f..24aa2e3a81 100644 --- a/src/queries/analytics/reports/getRetention.ts +++ b/src/queries/analytics/reports/getRetention.ts @@ -35,14 +35,14 @@ async function relationalQuery( }[] > { const { startDate, endDate, timezone = 'UTC' } = filters; - const { getDateQuery, getDayDiffQuery, getCastColumnQuery, rawQuery } = prisma; + const { getDateSQL, getDayDiffQuery, getCastColumnQuery, rawQuery } = prisma; const unit = 'day'; return rawQuery( ` WITH cohort_items AS ( select session_id, - ${getDateQuery('created_at', unit, timezone)} as cohort_date + ${getDateSQL('created_at', unit, timezone)} as cohort_date from session where website_id = {{websiteId::uuid}} and created_at between {{startDate}} and {{endDate}} @@ -50,10 +50,7 @@ async function relationalQuery( user_activities AS ( select distinct w.session_id, - ${getDayDiffQuery( - getDateQuery('created_at', unit, timezone), - 'c.cohort_date', - )} as day_number + ${getDayDiffQuery(getDateSQL('created_at', unit, timezone), 'c.cohort_date')} as day_number from website_event w join cohort_items c on w.session_id = c.session_id @@ -115,14 +112,14 @@ async function clickhouseQuery( }[] > { const { startDate, endDate, timezone = 'UTC' } = filters; - const { getDateQuery, getDateStringQuery, rawQuery } = clickhouse; + const { getDateSQL, getDateStringSQL, rawQuery } = clickhouse; const unit = 'day'; return rawQuery( ` WITH cohort_items AS ( select - min(${getDateQuery('created_at', unit, timezone)}) as cohort_date, + min(${getDateSQL('created_at', unit, timezone)}) as cohort_date, session_id from website_event where website_id = {websiteId:UUID} @@ -132,7 +129,7 @@ async function clickhouseQuery( user_activities AS ( select distinct w.session_id, - (${getDateQuery('created_at', unit, timezone)} - c.cohort_date) / 86400 as day_number + (${getDateSQL('created_at', unit, timezone)} - c.cohort_date) / 86400 as day_number from website_event w join cohort_items c on w.session_id = c.session_id @@ -157,7 +154,7 @@ async function clickhouseQuery( group by 1, 2 ) select - ${getDateStringQuery('c.cohort_date', unit)} as date, + ${getDateStringSQL('c.cohort_date', unit)} as date, c.day_number as day, s.visitors as visitors, c.visitors returnVisitors, diff --git a/src/queries/analytics/reports/getRevenue.ts b/src/queries/analytics/reports/getRevenue.ts index 6b151bb72f..e4857a43e5 100644 --- a/src/queries/analytics/reports/getRevenue.ts +++ b/src/queries/analytics/reports/getRevenue.ts @@ -46,12 +46,12 @@ async function relationalQuery( timezone = 'UTC', unit = 'day', } = criteria; - const { getDateQuery, rawQuery } = prisma; + const { getDateSQL, rawQuery } = prisma; const chartRes = await rawQuery( ` select - ${getDateQuery('website_event.created_at', unit, timezone)} time, + ${getDateSQL('website_event.created_at', unit, timezone)} time, sum(case when data_key = {{revenueProperty}} then number_value else 0 end) sum, avg(case when data_key = {{revenueProperty}} then number_value else 0 end) avg, count(case when data_key = {{revenueProperty}} then 1 else 0 end) count, @@ -110,7 +110,7 @@ async function clickhouseQuery( timezone = 'UTC', unit = 'day', } = criteria; - const { getDateStringQuery, getDateQuery, rawQuery } = clickhouse; + const { getDateStringSQL, getDateSQL, rawQuery } = clickhouse; const chartRes = await rawQuery<{ time: string; @@ -121,14 +121,14 @@ async function clickhouseQuery( }>( ` select - ${getDateStringQuery('g.time', unit)} as time, + ${getDateStringSQL('g.time', unit)} as time, g.sum as sum, g.avg as avg, g.count as count, g.uniqueCount as uniqueCount from ( select - ${getDateQuery('created_at', unit, timezone)} as time, + ${getDateSQL('created_at', unit, timezone)} as time, sumIf(number_value, data_key = {revenueProperty:String}) as sum, avgIf(number_value, data_key = {revenueProperty:String}) as avg, countIf(data_key = {revenueProperty:String}) as count, diff --git a/src/queries/analytics/sessions/getSessionStats.ts b/src/queries/analytics/sessions/getSessionStats.ts index e3af7ba6d1..54c46c3552 100644 --- a/src/queries/analytics/sessions/getSessionStats.ts +++ b/src/queries/analytics/sessions/getSessionStats.ts @@ -13,7 +13,7 @@ export async function getSessionStats(...args: [websiteId: string, filters: Quer async function relationalQuery(websiteId: string, filters: QueryFilters) { const { timezone = 'utc', unit = 'day' } = filters; - const { getDateQuery, parseFilters, rawQuery } = prisma; + const { getDateSQL, parseFilters, rawQuery } = prisma; const { filterQuery, joinSession, params } = await parseFilters(websiteId, { ...filters, eventType: EVENT_TYPE.pageView, @@ -22,7 +22,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { return rawQuery( ` select - ${getDateQuery('website_event.created_at', unit, timezone)} x, + ${getDateSQL('website_event.created_at', unit, timezone)} x, count(distinct website_event.session_id) y from website_event ${joinSession} @@ -41,7 +41,7 @@ async function clickhouseQuery( filters: QueryFilters, ): Promise<{ x: string; y: number }[]> { const { timezone = 'UTC', unit = 'day' } = filters; - const { parseFilters, rawQuery, getDateStringQuery, getDateQuery } = clickhouse; + const { parseFilters, rawQuery, getDateStringSQL, getDateSQL } = clickhouse; const { filterQuery, params } = await parseFilters(websiteId, { ...filters, eventType: EVENT_TYPE.pageView, @@ -50,11 +50,11 @@ async function clickhouseQuery( return rawQuery( ` select - ${getDateStringQuery('g.t', unit)} as x, + ${getDateStringSQL('g.t', unit)} as x, g.y as y from ( select - ${getDateQuery('created_at', unit, timezone)} as t, + ${getDateSQL('created_at', unit, timezone)} as t, count(distinct session_id) as y from website_event where website_id = {websiteId:UUID} diff --git a/src/queries/analytics/sessions/getSessions.ts b/src/queries/analytics/sessions/getSessions.ts index a11edd39bd..47471d986d 100644 --- a/src/queries/analytics/sessions/getSessions.ts +++ b/src/queries/analytics/sessions/getSessions.ts @@ -1,45 +1,33 @@ import prisma from 'lib/prisma'; import clickhouse from 'lib/clickhouse'; import { runQuery, PRISMA, CLICKHOUSE } from 'lib/db'; -import { QueryFilters } from 'lib/types'; +import { PageParams, QueryFilters } from 'lib/types'; -export async function getSessions(...args: [websiteId: string, filters: QueryFilters]) { +export async function getSessions( + ...args: [websiteId: string, filters?: QueryFilters, pageParams?: PageParams] +) { return runQuery({ [PRISMA]: () => relationalQuery(...args), [CLICKHOUSE]: () => clickhouseQuery(...args), }); } -async function relationalQuery(websiteId: string, filters: QueryFilters) { - const { startDate } = filters; +async function relationalQuery(websiteId: string, filters: QueryFilters, pageParams: PageParams) { + const { pagedQuery } = prisma; - return prisma.client.session - .findMany({ - where: { - websiteId, - createdAt: { - gte: startDate, - }, - }, - orderBy: { - createdAt: 'desc', - }, - }) - .then(a => { - return Object.values(a).map(a => { - return { - ...a, - timestamp: new Date(a.createdAt).getTime() / 1000, - }; - }); - }); + const where = { + ...filters, + id: websiteId, + }; + + return pagedQuery('session', { where }, pageParams); } -async function clickhouseQuery(websiteId: string, filters: QueryFilters) { - const { rawQuery } = clickhouse; - const { startDate } = filters; +async function clickhouseQuery(websiteId: string, filters: QueryFilters, pageParams?: PageParams) { + const { pagedQuery, parseFilters } = clickhouse; + const { params, dateQuery, filterQuery } = await parseFilters(websiteId, filters); - return rawQuery( + return pagedQuery( ` select session_id as id, @@ -58,12 +46,11 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) { city from website_event where website_id = {websiteId:UUID} - and created_at >= {startDate:DateTime64} + ${dateQuery} + ${filterQuery} order by created_at desc `, - { - websiteId, - startDate, - }, + params, + pageParams, ); } diff --git a/src/queries/index.ts b/src/queries/index.ts index 8cef080aa1..796adea6f4 100644 --- a/src/queries/index.ts +++ b/src/queries/index.ts @@ -1,8 +1,8 @@ -export * from './admin/report'; -export * from './admin/team'; -export * from './admin/teamUser'; -export * from './admin/user'; -export * from './admin/website'; +export * from 'queries/prisma/report'; +export * from 'queries/prisma/team'; +export * from 'queries/prisma/teamUser'; +export * from 'queries/prisma/user'; +export * from 'queries/prisma/website'; export * from './analytics/events/getEventMetrics'; export * from './analytics/events/getEventUsage'; export * from './analytics/events/getEvents'; diff --git a/src/queries/admin/report.ts b/src/queries/prisma/report.ts similarity index 93% rename from src/queries/admin/report.ts rename to src/queries/prisma/report.ts index dc05a1d55b..a0e6364ce8 100644 --- a/src/queries/admin/report.ts +++ b/src/queries/prisma/report.ts @@ -17,9 +17,9 @@ export async function getReport(reportId: string): Promise { export async function getReports( criteria: ReportFindManyArgs, - filters: PageParams = {}, + pageParams: PageParams = {}, ): Promise> { - const { query } = filters; + const { query } = pageParams; const where: Prisma.ReportWhereInput = { ...criteria.where, @@ -45,7 +45,7 @@ export async function getReports( ]), }; - return prisma.pagedQuery('report', { ...criteria, where }, filters); + return prisma.pagedQuery('report', { ...criteria, where }, pageParams); } export async function getUserReports( diff --git a/src/queries/admin/team.ts b/src/queries/prisma/team.ts similarity index 100% rename from src/queries/admin/team.ts rename to src/queries/prisma/team.ts diff --git a/src/queries/admin/teamUser.ts b/src/queries/prisma/teamUser.ts similarity index 100% rename from src/queries/admin/teamUser.ts rename to src/queries/prisma/teamUser.ts diff --git a/src/queries/admin/user.ts b/src/queries/prisma/user.ts similarity index 98% rename from src/queries/admin/user.ts rename to src/queries/prisma/user.ts index 9e08511287..9b471787e4 100644 --- a/src/queries/admin/user.ts +++ b/src/queries/prisma/user.ts @@ -49,9 +49,9 @@ export async function getUserByUsername(username: string, options: GetUserOption export async function getUsers( criteria: UserFindManyArgs, - filters?: PageParams, + pageParams?: PageParams, ): Promise> { - const { query } = filters; + const { query } = pageParams; const where: Prisma.UserWhereInput = { ...criteria.where, @@ -68,7 +68,7 @@ export async function getUsers( { orderBy: 'createdAt', sortDescending: true, - ...filters, + ...pageParams, }, ); } diff --git a/src/queries/admin/website.ts b/src/queries/prisma/website.ts similarity index 97% rename from src/queries/admin/website.ts rename to src/queries/prisma/website.ts index eb07f77991..0814a137b6 100644 --- a/src/queries/admin/website.ts +++ b/src/queries/prisma/website.ts @@ -27,9 +27,9 @@ export async function getSharedWebsite(shareId: string) { export async function getWebsites( criteria: WebsiteFindManyArgs, - filters: PageParams, + pageParams: PageParams, ): Promise> { - const { query } = filters; + const { query } = pageParams; const where: Prisma.WebsiteWhereInput = { ...criteria.where, @@ -42,7 +42,7 @@ export async function getWebsites( deletedAt: null, }; - return prisma.pagedQuery('website', { ...criteria, where }, filters); + return prisma.pagedQuery('website', { ...criteria, where }, pageParams); } export async function getAllWebsites(userId: string) { From bfd5c5f150d2a44eb22e90d408e77763df085591 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Mon, 8 Jul 2024 11:18:30 -0700 Subject: [PATCH 023/132] update dashboard queries to use mv --- src/pages/api/websites/[websiteId]/metrics.ts | 13 ++++++++++--- .../analytics/pageviews/getPageviewMetrics.ts | 18 +++++++++++++++--- .../analytics/pageviews/getPageviewStats.ts | 5 +++-- .../analytics/sessions/getSessionMetrics.ts | 15 +++++++++++++-- .../analytics/sessions/getSessionStats.ts | 5 +++-- 5 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/pages/api/websites/[websiteId]/metrics.ts b/src/pages/api/websites/[websiteId]/metrics.ts index 3dac217b21..b37c38f743 100644 --- a/src/pages/api/websites/[websiteId]/metrics.ts +++ b/src/pages/api/websites/[websiteId]/metrics.ts @@ -64,7 +64,7 @@ export default async ( await useAuth(req, res); await useValidate(schema, req, res); - const { websiteId, type, limit, offset, search } = req.query; + const { websiteId, type, limit, offset, search, unit } = req.query; if (req.method === 'GET') { if (!(await canViewWebsite(req.auth, websiteId))) { @@ -89,7 +89,7 @@ export default async ( } if (SESSION_COLUMNS.includes(type)) { - const data = await getSessionMetrics(websiteId, type, filters, limit, offset); + const data = await getSessionMetrics(websiteId, type, filters, limit, offset, unit as string); if (type === 'language') { const combined = {}; @@ -111,7 +111,14 @@ export default async ( } if (EVENT_COLUMNS.includes(type)) { - const data = await getPageviewMetrics(websiteId, type, filters, limit, offset); + const data = await getPageviewMetrics( + websiteId, + type, + filters, + limit, + offset, + unit as string, + ); return ok(res, data); } diff --git a/src/queries/analytics/pageviews/getPageviewMetrics.ts b/src/queries/analytics/pageviews/getPageviewMetrics.ts index 67ccb04aab..25c264b780 100644 --- a/src/queries/analytics/pageviews/getPageviewMetrics.ts +++ b/src/queries/analytics/pageviews/getPageviewMetrics.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-unused-vars, @typescript-eslint/no-unused-vars */ import clickhouse from 'lib/clickhouse'; import { EVENT_TYPE, FILTER_COLUMNS, SESSION_COLUMNS } from 'lib/constants'; import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; @@ -5,7 +6,14 @@ import prisma from 'lib/prisma'; import { QueryFilters } from 'lib/types'; export async function getPageviewMetrics( - ...args: [websiteId: string, type: string, filters: QueryFilters, limit?: number, offset?: number] + ...args: [ + websiteId: string, + type: string, + filters: QueryFilters, + limit?: number, + offset?: number, + unit?: string, + ] ) { return runQuery({ [PRISMA]: () => relationalQuery(...args), @@ -19,6 +27,7 @@ async function relationalQuery( filters: QueryFilters, limit: number = 500, offset: number = 0, + unit: string, ) { const column = FILTER_COLUMNS[type] || type; const { rawQuery, parseFilters } = prisma; @@ -79,6 +88,7 @@ async function clickhouseQuery( filters: QueryFilters, limit: number = 500, offset: number = 0, + unit: string, ): Promise<{ x: string; y: number }[]> { const column = FILTER_COLUMNS[type] || type; const { rawQuery, parseFilters } = clickhouse; @@ -108,10 +118,12 @@ async function clickhouseQuery( and x.target_created_at = website_event.created_at`; } + const table = unit === 'hour' ? 'website_event_metric_hourly' : 'website_event_metric_daily'; + return rawQuery( ` - select ${column} x, count(*) y - from website_event + select ${column} x, countMerge(views) y + from ${table} website_event ${entryExitQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} diff --git a/src/queries/analytics/pageviews/getPageviewStats.ts b/src/queries/analytics/pageviews/getPageviewStats.ts index a37a15662f..f6942e6a2c 100644 --- a/src/queries/analytics/pageviews/getPageviewStats.ts +++ b/src/queries/analytics/pageviews/getPageviewStats.ts @@ -46,6 +46,7 @@ async function clickhouseQuery( ...filters, eventType: EVENT_TYPE.pageView, }); + const table = unit === 'hour' ? 'website_event_metric_hourly' : 'website_event_metric_daily'; return rawQuery( ` @@ -55,8 +56,8 @@ async function clickhouseQuery( from ( select ${getDateQuery('created_at', unit, timezone)} as t, - count(*) as y - from website_event + countMerge(views) as y + from ${table} website_event where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type = {eventType:UInt32} diff --git a/src/queries/analytics/sessions/getSessionMetrics.ts b/src/queries/analytics/sessions/getSessionMetrics.ts index e522a7effd..58b5198ceb 100644 --- a/src/queries/analytics/sessions/getSessionMetrics.ts +++ b/src/queries/analytics/sessions/getSessionMetrics.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-unused-vars, @typescript-eslint/no-unused-vars */ import prisma from 'lib/prisma'; import clickhouse from 'lib/clickhouse'; import { runQuery, CLICKHOUSE, PRISMA } from 'lib/db'; @@ -5,7 +6,14 @@ import { EVENT_TYPE, FILTER_COLUMNS, SESSION_COLUMNS } from 'lib/constants'; import { QueryFilters } from 'lib/types'; export async function getSessionMetrics( - ...args: [websiteId: string, type: string, filters: QueryFilters, limit?: number, offset?: number] + ...args: [ + websiteId: string, + type: string, + filters: QueryFilters, + limit?: number, + offset?: number, + unit?: string, + ] ) { return runQuery({ [PRISMA]: () => relationalQuery(...args), @@ -19,6 +27,7 @@ async function relationalQuery( filters: QueryFilters, limit: number = 500, offset: number = 0, + unit: string, ) { const column = FILTER_COLUMNS[type] || type; const { parseFilters, rawQuery } = prisma; @@ -62,6 +71,7 @@ async function clickhouseQuery( filters: QueryFilters, limit: number = 500, offset: number = 0, + unit: string, ): Promise<{ x: string; y: number }[]> { const column = FILTER_COLUMNS[type] || type; const { parseFilters, rawQuery } = clickhouse; @@ -70,6 +80,7 @@ async function clickhouseQuery( eventType: EVENT_TYPE.pageView, }); const includeCountry = column === 'city' || column === 'subdivision1'; + const table = unit === 'hour' ? 'website_event_metric_hourly' : 'website_event_metric_daily'; return rawQuery( ` @@ -77,7 +88,7 @@ async function clickhouseQuery( ${column} x, uniq(session_id) y ${includeCountry ? ', country' : ''} - from website_event + from ${table} website_event where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type = {eventType:UInt32} diff --git a/src/queries/analytics/sessions/getSessionStats.ts b/src/queries/analytics/sessions/getSessionStats.ts index e3af7ba6d1..dc6d635d14 100644 --- a/src/queries/analytics/sessions/getSessionStats.ts +++ b/src/queries/analytics/sessions/getSessionStats.ts @@ -46,6 +46,7 @@ async function clickhouseQuery( ...filters, eventType: EVENT_TYPE.pageView, }); + const table = unit === 'hour' ? 'website_event_metric_hourly' : 'website_event_metric_daily'; return rawQuery( ` @@ -55,8 +56,8 @@ async function clickhouseQuery( from ( select ${getDateQuery('created_at', unit, timezone)} as t, - count(distinct session_id) as y - from website_event + uniq(session_id) as y + from ${table} website_event where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type = {eventType:UInt32} From 62f35668ae785799b6bec1b67b4737d00876f451 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Mon, 8 Jul 2024 23:39:06 -0700 Subject: [PATCH 024/132] Fixed funnel chart. --- src/app/(main)/reports/funnel/FunnelChart.module.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/app/(main)/reports/funnel/FunnelChart.module.css b/src/app/(main)/reports/funnel/FunnelChart.module.css index 81b22c7873..7972d57362 100644 --- a/src/app/(main)/reports/funnel/FunnelChart.module.css +++ b/src/app/(main)/reports/funnel/FunnelChart.module.css @@ -34,6 +34,10 @@ background-color: var(--base100); } +.step:last-child::before { + display: none; +} + .card { display: grid; gap: 20px; From 4494665d2e72fc39cb3baa9f70726a6590a48080 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Mon, 8 Jul 2024 23:41:40 -0700 Subject: [PATCH 025/132] Updated date output for Clickhouse. --- .../[websiteId]/realtime/RealtimeLog.tsx | 4 ++-- src/lib/clickhouse.ts | 17 +++++++++++------ src/lib/date.ts | 2 +- src/queries/analytics/events/getEvents.ts | 5 ++--- src/queries/analytics/getRealtimeData.ts | 4 ++-- src/queries/analytics/sessions/getSessions.ts | 5 ++--- 6 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx index 700e83aeef..97fb16597b 100644 --- a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx +++ b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx @@ -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); @@ -149,7 +149,7 @@ export function RealtimeLog({ data }: { data: RealtimeData }) { ...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 }) => { diff --git a/src/lib/clickhouse.ts b/src/lib/clickhouse.ts index e716d6493f..2bda9bfa85 100644 --- a/src/lib/clickhouse.ts +++ b/src/lib/clickhouse.ts @@ -9,11 +9,12 @@ import { maxDate } from './date'; import { filtersToArray } from './params'; export const CLICKHOUSE_DATE_FORMATS = { - minute: '%Y-%m-%d %H:%i:00', - hour: '%Y-%m-%d %H:00:00', - day: '%Y-%m-%d', - month: '%Y-%m-01', - year: '%Y-01-01', + second: '%Y-%m-%dT%H:%i:%S', + minute: '%Y-%m-%dT%H:%i:00', + hour: '%Y-%m-%dT%H:00:00', + day: '%Y-%m-%dT00:00:00', + month: '%Y-%m-01T00:00:00', + year: '%Y-01-01T00:00:00', }; const log = debug('umami:clickhouse'); @@ -47,7 +48,11 @@ function getClient() { return client; } -function getDateStringSQL(data: any, unit: string | number) { +function getDateStringSQL(data: any, unit: string | number, timezone?: string) { + if (timezone) { + return `formatDateTime(${data}, '${CLICKHOUSE_DATE_FORMATS[unit]}', '${timezone}')`; + } + return `formatDateTime(${data}, '${CLICKHOUSE_DATE_FORMATS[unit]}')`; } diff --git a/src/lib/date.ts b/src/lib/date.ts index de76f7f327..2fb24073a6 100644 --- a/src/lib/date.ts +++ b/src/lib/date.ts @@ -292,7 +292,7 @@ export function getDateArray(data: any[], startDate: Date, endDate: Date, unit: for (let i = 0; i <= n; i++) { const t = start(add(startDate, i)); - const y = data.find(({ x }) => start(getDateFromString(x)).getTime() === t.getTime())?.y || 0; + const y = data.find(({ x }) => start(new Date(x)).getTime() === t.getTime())?.y || 0; arr.push({ x: t, y }); } diff --git a/src/queries/analytics/events/getEvents.ts b/src/queries/analytics/events/getEvents.ts index a00f684851..540c1a05d8 100644 --- a/src/queries/analytics/events/getEvents.ts +++ b/src/queries/analytics/events/getEvents.ts @@ -24,7 +24,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters, pagePar } async function clickhouseQuery(websiteId: string, filters: QueryFilters, pageParams?: PageParams) { - const { pagedQuery, parseFilters } = clickhouse; + const { pagedQuery, parseFilters, getDateStringSQL } = clickhouse; const { params, dateQuery, filterQuery } = await parseFilters(websiteId, filters); return pagedQuery( @@ -33,8 +33,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar event_id as id, website_id as websiteId, session_id as sessionId, - created_at as createdAt, - toUnixTimestamp(created_at) as timestamp, + ${getDateStringSQL('created_at', 'second', filters.timezone)} as createdAt, url_path as urlPath, url_query as urlQuery, referrer_path as referrerPath, diff --git a/src/queries/analytics/getRealtimeData.ts b/src/queries/analytics/getRealtimeData.ts index afc7ff8fa9..5a9c5a36c0 100644 --- a/src/queries/analytics/getRealtimeData.ts +++ b/src/queries/analytics/getRealtimeData.ts @@ -19,8 +19,8 @@ export async function getRealtimeData( const { startDate, timezone } = criteria; const filters = { startDate, endDate: new Date(), unit: 'minute', timezone }; const [events, sessions, pageviews, sessionviews] = await Promise.all([ - getEvents(websiteId, { startDate }, { pageSize: 10000 }), - getSessions(websiteId, { startDate }, { pageSize: 10000 }), + getEvents(websiteId, { startDate, timezone }, { pageSize: 10000 }), + getSessions(websiteId, { startDate, timezone }, { pageSize: 10000 }), getPageviewStats(websiteId, filters), getSessionStats(websiteId, filters), ]); diff --git a/src/queries/analytics/sessions/getSessions.ts b/src/queries/analytics/sessions/getSessions.ts index 47471d986d..538133bac3 100644 --- a/src/queries/analytics/sessions/getSessions.ts +++ b/src/queries/analytics/sessions/getSessions.ts @@ -24,7 +24,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters, pagePar } async function clickhouseQuery(websiteId: string, filters: QueryFilters, pageParams?: PageParams) { - const { pagedQuery, parseFilters } = clickhouse; + const { pagedQuery, parseFilters, getDateStringSQL } = clickhouse; const { params, dateQuery, filterQuery } = await parseFilters(websiteId, filters); return pagedQuery( @@ -32,8 +32,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar select session_id as id, website_id as websiteId, - created_at as createdAt, - toUnixTimestamp(created_at) as timestamp, + ${getDateStringSQL('created_at', 'second', filters.timezone)} as createdAt, hostname, browser, os, From 374faae27b6590adf09c0a02abeb4a379baf6d88 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Mon, 8 Jul 2024 23:42:26 -0700 Subject: [PATCH 026/132] Updated lang files. --- public/intl/messages/de-DE.json | 98 ++++++++++++++++----------------- public/intl/messages/pl-PL.json | 36 ++++++------ public/intl/messages/zh-CN.json | 28 +++++----- 3 files changed, 81 insertions(+), 81 deletions(-) diff --git a/public/intl/messages/de-DE.json b/public/intl/messages/de-DE.json index 2482b208b5..a7d1f1ab60 100644 --- a/public/intl/messages/de-DE.json +++ b/public/intl/messages/de-DE.json @@ -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 navigate 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" } ] } diff --git a/public/intl/messages/pl-PL.json b/public/intl/messages/pl-PL.json index 608c0b3a80..7157a4f26c 100644 --- a/public/intl/messages/pl-PL.json +++ b/public/intl/messages/pl-PL.json @@ -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 navigate 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": [ diff --git a/public/intl/messages/zh-CN.json b/public/intl/messages/zh-CN.json index 0b7da8a52e..bb77d83b98 100644 --- a/public/intl/messages/zh-CN.json +++ b/public/intl/messages/zh-CN.json @@ -152,7 +152,7 @@ "label.compare": [ { "type": 0, - "value": "对比" + "value": "比较" } ], "label.confirm": [ @@ -182,7 +182,7 @@ "label.count": [ { "type": 0, - "value": "数量" + "value": "统计" } ], "label.countries": [ @@ -236,7 +236,7 @@ "label.current": [ { "type": 0, - "value": "当前" + "value": "目前" } ], "label.current-password": [ @@ -398,13 +398,13 @@ "label.end-step": [ { "type": 0, - "value": "最后一步" + "value": "结束步骤" } ], "label.entry": [ { "type": 0, - "value": "首次访问 URL" + "value": "入口 URL" } ], "label.event": [ @@ -500,7 +500,7 @@ "label.goals-description": [ { "type": 0, - "value": "追踪您的页面浏览量和事件目标。" + "value": "跟踪页面浏览量和事件的目标。" } ], "label.greater-than": [ @@ -578,13 +578,13 @@ "label.journey": [ { "type": 0, - "value": "轨迹" + "value": "用户浏览轨迹" } ], "label.journey-description": [ { "type": 0, - "value": "了解用户如何浏览您网站的哪些页面。" + "value": "了解用户如何浏览网站。" } ], "label.language": [ @@ -882,19 +882,19 @@ "label.previous": [ { "type": 0, - "value": "之前" + "value": "先前" } ], "label.previous-period": [ { "type": 0, - "value": "前一周期" + "value": "上一时期" } ], "label.previous-year": [ { "type": 0, - "value": "前一年" + "value": "上一年" } ], "label.profile": [ @@ -1098,7 +1098,7 @@ "label.start-step": [ { "type": 0, - "value": "第一步" + "value": "开始步骤" } ], "label.steps": [ @@ -1134,7 +1134,7 @@ "label.team-manager": [ { "type": 0, - "value": "团队管理" + "value": "团队管理者" } ], "label.team-member": [ @@ -1450,7 +1450,7 @@ "message.collected-data": [ { "type": 0, - "value": "收集数据" + "value": "已收集的数据" } ], "message.confirm-delete": [ From 44ccb661a2ebb5c71976d10682a87c7e0ca81b2a Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Tue, 9 Jul 2024 16:33:43 -0700 Subject: [PATCH 027/132] Updated IP regex to work with IPv6. --- src/lib/constants.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 35917802d2..661611bc9f 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -248,7 +248,8 @@ export const UUID_REGEX = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/; export const HOSTNAME_REGEX = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])$/; -export const IP_REGEX = /^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\.(?!$)|$)){4}$/; +export const IP_REGEX = + /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$|^(?:(?:[0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:(?:(:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]+|::(ffff(:0{1,4})?:)?((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9]))$/; export const DATETIME_REGEX = /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{3}(Z|\+[0-9]{2}:[0-9]{2})?)?$/; From f518066d932fc689a10ae32c142698468d667144 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Wed, 10 Jul 2024 00:54:46 -0700 Subject: [PATCH 028/132] refactor views and queries for dashboard --- src/lib/constants.ts | 4 +-- src/pages/api/websites/[websiteId]/stats.ts | 10 ++++-- src/queries/analytics/getWebsiteStats.ts | 33 ++++++++----------- .../analytics/pageviews/getPageviewMetrics.ts | 22 ++++--------- .../analytics/pageviews/getPageviewStats.ts | 4 +-- .../analytics/sessions/getSessionMetrics.ts | 2 +- .../analytics/sessions/getSessionStats.ts | 2 +- 7 files changed, 34 insertions(+), 43 deletions(-) diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 35917802d2..8a23f93094 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -58,8 +58,8 @@ export const SESSION_COLUMNS = [ export const FILTER_COLUMNS = { url: 'url_path', - entry: 'url_path', - exit: 'url_path', + entry: 'entry_url', + exit: 'exit_url', referrer: 'referrer_domain', host: 'hostname', title: 'page_title', diff --git a/src/pages/api/websites/[websiteId]/stats.ts b/src/pages/api/websites/[websiteId]/stats.ts index 0189627a59..1c684dbed3 100644 --- a/src/pages/api/websites/[websiteId]/stats.ts +++ b/src/pages/api/websites/[websiteId]/stats.ts @@ -56,7 +56,7 @@ export default async ( await useAuth(req, res); await useValidate(schema, req, res); - const { websiteId, compare } = req.query; + const { websiteId, compare, unit } = req.query; if (req.method === 'GET') { if (!(await canViewWebsite(req.auth, websiteId))) { @@ -72,9 +72,13 @@ export default async ( const filters = getRequestFilters(req); - const metrics = await getWebsiteStats(websiteId, { ...filters, startDate, endDate }); + const metrics = await getWebsiteStats(websiteId, unit as string, { + ...filters, + startDate, + endDate, + }); - const prevPeriod = await getWebsiteStats(websiteId, { + const prevPeriod = await getWebsiteStats(websiteId, unit as string, { ...filters, startDate: compareStartDate, endDate: compareEndDate, diff --git a/src/queries/analytics/getWebsiteStats.ts b/src/queries/analytics/getWebsiteStats.ts index 2f3c82e865..13eadbbba8 100644 --- a/src/queries/analytics/getWebsiteStats.ts +++ b/src/queries/analytics/getWebsiteStats.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-unused-vars, @typescript-eslint/no-unused-vars */ import clickhouse from 'lib/clickhouse'; import { EVENT_TYPE } from 'lib/constants'; import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; @@ -5,7 +6,7 @@ import prisma from 'lib/prisma'; import { QueryFilters } from 'lib/types'; export async function getWebsiteStats( - ...args: [websiteId: string, filters: QueryFilters] + ...args: [websiteId: string, unit: string, filters: QueryFilters] ): Promise< { pageviews: number; visitors: number; visits: number; bounces: number; totaltime: number }[] > { @@ -17,6 +18,7 @@ export async function getWebsiteStats( async function relationalQuery( websiteId: string, + unit: string, filters: QueryFilters, ): Promise< { pageviews: number; visitors: number; visits: number; bounces: number; totaltime: number }[] @@ -57,6 +59,7 @@ async function relationalQuery( async function clickhouseQuery( websiteId: string, + unit: string, filters: QueryFilters, ): Promise< { pageviews: number; visitors: number; visits: number; bounces: number; totaltime: number }[] @@ -66,29 +69,21 @@ async function clickhouseQuery( ...filters, eventType: EVENT_TYPE.pageView, }); + const table = unit === 'hour' ? 'website_event_stats_hourly' : 'website_event_stats_daily'; return rawQuery( ` select - sum(t.c) as "pageviews", - uniq(t.session_id) as "visitors", - uniq(t.visit_id) as "visits", - sum(if(t.c = 1, 1, 0)) as "bounces", + sum(views) as "pageviews", + uniq(session_id) as "visitors", + uniq(visit_id) as "visits", + sumIf(1, views = 1) as "bounces", sum(max_time-min_time) as "totaltime" - from ( - select - session_id, - visit_id, - count(*) c, - min(created_at) min_time, - max(created_at) max_time - from website_event - where website_id = {websiteId:UUID} - and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type = {eventType:UInt32} - ${filterQuery} - group by session_id, visit_id - ) as t; + from ${table} "website_event" + where website_id = {websiteId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} + and event_type = {eventType:UInt32} + ${filterQuery}; `, params, ).then(result => { diff --git a/src/queries/analytics/pageviews/getPageviewMetrics.ts b/src/queries/analytics/pageviews/getPageviewMetrics.ts index 25c264b780..34d26d9dcd 100644 --- a/src/queries/analytics/pageviews/getPageviewMetrics.ts +++ b/src/queries/analytics/pageviews/getPageviewMetrics.ts @@ -97,34 +97,26 @@ async function clickhouseQuery( eventType: column === 'event_name' ? EVENT_TYPE.customEvent : EVENT_TYPE.pageView, }); - let entryExitQuery = ''; + let columnAgg = column; let excludeDomain = ''; if (column === 'referrer_domain') { excludeDomain = `and referrer_domain != {websiteDomain:String} and referrer_domain != ''`; } - if (type === 'entry' || type === 'exit') { - const aggregrate = type === 'entry' ? 'min' : 'max'; + if (type === 'entry') { + columnAgg = `argMinMerge(${column})`; + } - entryExitQuery = ` - JOIN (select visit_id, - ${aggregrate}(created_at) target_created_at - from website_event - where website_id = {websiteId:UUID} - and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type = {eventType:UInt32} - group by visit_id) x - ON x.visit_id = website_event.visit_id - and x.target_created_at = website_event.created_at`; + if (type === 'exit') { + columnAgg = `argMaxMerge(${column})`; } const table = unit === 'hour' ? 'website_event_metric_hourly' : 'website_event_metric_daily'; return rawQuery( ` - select ${column} x, countMerge(views) y + select ${column} x, sum(views) y from ${table} website_event - ${entryExitQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type = {eventType:UInt32} diff --git a/src/queries/analytics/pageviews/getPageviewStats.ts b/src/queries/analytics/pageviews/getPageviewStats.ts index f6942e6a2c..57292f6588 100644 --- a/src/queries/analytics/pageviews/getPageviewStats.ts +++ b/src/queries/analytics/pageviews/getPageviewStats.ts @@ -46,7 +46,7 @@ async function clickhouseQuery( ...filters, eventType: EVENT_TYPE.pageView, }); - const table = unit === 'hour' ? 'website_event_metric_hourly' : 'website_event_metric_daily'; + const table = unit === 'hour' ? 'website_event_stats_hourly' : 'website_event_stats_daily'; return rawQuery( ` @@ -56,7 +56,7 @@ async function clickhouseQuery( from ( select ${getDateQuery('created_at', unit, timezone)} as t, - countMerge(views) as y + sum(views) as y from ${table} website_event where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} diff --git a/src/queries/analytics/sessions/getSessionMetrics.ts b/src/queries/analytics/sessions/getSessionMetrics.ts index 58b5198ceb..93e36a55fa 100644 --- a/src/queries/analytics/sessions/getSessionMetrics.ts +++ b/src/queries/analytics/sessions/getSessionMetrics.ts @@ -80,7 +80,7 @@ async function clickhouseQuery( eventType: EVENT_TYPE.pageView, }); const includeCountry = column === 'city' || column === 'subdivision1'; - const table = unit === 'hour' ? 'website_event_metric_hourly' : 'website_event_metric_daily'; + const table = unit === 'hour' ? 'website_event_stats_hourly' : 'website_event_stats_daily'; return rawQuery( ` diff --git a/src/queries/analytics/sessions/getSessionStats.ts b/src/queries/analytics/sessions/getSessionStats.ts index dc6d635d14..6db03c46f2 100644 --- a/src/queries/analytics/sessions/getSessionStats.ts +++ b/src/queries/analytics/sessions/getSessionStats.ts @@ -46,7 +46,7 @@ async function clickhouseQuery( ...filters, eventType: EVENT_TYPE.pageView, }); - const table = unit === 'hour' ? 'website_event_metric_hourly' : 'website_event_metric_daily'; + const table = unit === 'hour' ? 'website_event_stats_hourly' : 'website_event_stats_daily'; return rawQuery( ` From d6a4eea7f8be6e8af118e3f199197cf6e500d6fc Mon Sep 17 00:00:00 2001 From: manethpak Date: Wed, 10 Jul 2024 17:39:40 +0700 Subject: [PATCH 029/132] Update and fill missing translation for khmer lang --- src/lang/km-KH.json | 268 ++++++++++++++++++++++---------------------- 1 file changed, 134 insertions(+), 134 deletions(-) diff --git a/src/lang/km-KH.json b/src/lang/km-KH.json index 9a374a0673..e10b32b56e 100644 --- a/src/lang/km-KH.json +++ b/src/lang/km-KH.json @@ -1,170 +1,170 @@ { "label.access-code": "Access code", "label.actions": "សកម្មភាព", - "label.activity-log": "Activity log", - "label.add": "Add", - "label.add-description": "Add description", - "label.add-member": "Add member", - "label.add-step": "Add step", + "label.activity-log": "កំណត់ហេតុ​សកម្មភាព", + "label.add": "បង្កើតបន្ថែម", + "label.add-description": "បន្ថែមពិពណ៌នា", + "label.add-member": "បន្ថែមសមាជិក", + "label.add-step": "បន្ថែមជំហាន", "label.add-website": "បន្ថែមគេហទំព័រ", "label.admin": "អ្នកគ្រប់គ្រង", - "label.after": "After", + "label.after": "បន្ទាប់", "label.all": "ទាំងអស់", "label.all-time": "គ្រប់ពេល", "label.analytics": "Analytics", - "label.average": "Average", + "label.average": "ជាមធ្យម", "label.back": "ថយក្រោយ", - "label.before": "Before", + "label.before": "មុន", "label.bounce-rate": "ចំនួនវិលត្រឡប់", "label.breakdown": "Breakdown", "label.browser": "Browser", "label.browsers": "កម្មវិធី", "label.cancel": "បោះបង់", "label.change-password": "ផ្លាស់ប្តូរពាក្យសម្ងាត់", - "label.cities": "Cities", - "label.city": "City", - "label.clear-all": "Clear all", - "label.compare": "Compare", - "label.confirm": "Confirm", + "label.cities": "ទីក្រុង", + "label.city": "ទីក្រុង", + "label.clear-all": "លុបទាំងអស់", + "label.compare": "ប្រៀបធៀប", + "label.confirm": "បញ្ជាក់", "label.confirm-password": "បញ្ជាក់ពាក្យសម្ងាត់", - "label.contains": "Contains", - "label.continue": "Continue", - "label.count": "Count", + "label.contains": "មាន", + "label.continue": "បន្ត", + "label.count": "ចំនួន", "label.countries": "ប្រទេស", - "label.country": "Country", - "label.create": "Create", - "label.create-report": "Create report", - "label.create-team": "Create team", - "label.create-user": "Create user", - "label.created": "Created", - "label.created-by": "Created By", - "label.current": "Current", + "label.country": "ប្រទេស", + "label.create": "បង្កើត", + "label.create-report": "បង្កើតរបាយការណ៍", + "label.create-team": "បង្កើតរបាយក្រុម", + "label.create-user": "បង្កើតអ្នកប្រើប្រាស់", + "label.created": "បង្កើតនៅ", + "label.created-by": "បង្កើតដោយ", + "label.current": "បច្ចុប្បន្ន", "label.current-password": "ពាក្យសម្ងាត់បច្ចុប្បន្ន", "label.custom-range": "កំណត់ដោយខ្លួនឯង", "label.dashboard": "ផ្ទាំងគ្រប់គ្រង", - "label.data": "Data", - "label.date": "Date", + "label.data": "ទិន្នន័យ", + "label.date": "កាលបរិច្ឆេទ", "label.date-range": "ចន្លោះកាលបរិច្ឆេទ", - "label.day": "Day", - "label.default-date-range": "ចន្លោះកាលបរិច្ឆេទស្រាប់", + "label.day": "ថ្ងៃ", + "label.default-date-range": "ចន្លោះកាលបរិច្ឆេទដើម", "label.delete": "លុប", - "label.delete-report": "Delete report", - "label.delete-team": "Delete team", - "label.delete-user": "Delete user", + "label.delete-report": "លុបរបាយការណ៍", + "label.delete-team": "លុបក្រុម", + "label.delete-user": "លុបអ្នកប្រើប្រាស់", "label.delete-website": "លុបគេហទំព័រ", - "label.description": "Description", + "label.description": "ការពិពណ៌នា", "label.desktop": "កុំព្យូទ័រលើតុ", - "label.details": "Details", - "label.device": "Device", + "label.details": "ព័ត៌មានលម្អិត", + "label.device": "ឧបករណ៍", "label.devices": "ឧបករណ៍", - "label.dismiss": "បដិសេធ", - "label.does-not-contain": "Does not contain", - "label.domain": "ឈ្មោះគេហទំព័រ", + "label.dismiss": "រំសាយ", + "label.does-not-contain": "មិនមាន", + "label.domain": "Domain", "label.dropoff": "Dropoff", "label.edit": "កែប្រែ", "label.edit-dashboard": "កែផ្ទាំងគ្រប់គ្រង", - "label.edit-member": "Edit member", + "label.edit-member": "កែព័ត៌មានសមាជិក", "label.enable-share-url": "បើកការចែករំលែក URL", - "label.end-step": "End Step", - "label.entry": "Entry URL", - "label.event": "Event", - "label.event-data": "Event data", + "label.end-step": "បញ្ចប់ជំហាន", + "label.entry": "URL ចូល", + "label.event": "ព្រឹត្តិការណ៍", + "label.event-data": "ទិន្នន័យព្រឹត្តិការណ៍", "label.events": "ព្រឹត្តិការណ៍", - "label.exit": "Exit URL", - "label.false": "False", + "label.exit": "URL ចេញ", + "label.false": "មិនពិត", "label.field": "Field", "label.fields": "Fields", - "label.filter": "Filter", + "label.filter": "ចម្រោះ", "label.filter-combined": "រួមបញ្ចូលគ្នា", "label.filter-raw": "ដើម", - "label.filters": "Filters", + "label.filters": "ចម្រោះ", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", - "label.goal": "Goal", - "label.goals": "Goals", - "label.goals-description": "Track your goals for pageviews and events.", - "label.greater-than": "Greater than", - "label.greater-than-equals": "Greater than or equals", + "label.goal": "គោលដៅ", + "label.goals": "គោលដៅ", + "label.goals-description": "តាមដានគោលដៅរបស់អ្នកសម្រាប់ pageviews និង events។", + "label.greater-than": "ធំជាង", + "label.greater-than-equals": "ធំជាងឬស្មើ", "label.host": "Host", "label.hosts": "Hosts", "label.insights": "Insights", "label.insights-description": "Dive deeper into your data by using segments and filters.", - "label.is": "Is", - "label.is-not": "Is not", - "label.is-not-set": "Is not set", - "label.is-set": "Is set", - "label.join": "Join", - "label.join-team": "Join team", - "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.is": "គឺ", + "label.is-not": "មិនមែន", + "label.is-not-set": "មិនបានកំណត់", + "label.is-set": "បានកំណត់", + "label.join": "ចូលរួម", + "label.join-team": "ចូលក្រុម", + "label.journey": "​ដំណើរ", + "label.journey-description": "ស្វែងយល់ពីការប្រើប្រាស់គេហទំព័ររបស់អតិថិជនអ្នក។", "label.language": "ភាសា", "label.languages": "ភាសា", "label.laptop": "កុំព្យូទ័រយួរដៃ", - "label.last-days": "{x} ថ្ងៃមុន", - "label.last-hours": "{x} ម៉ោងមុន", - "label.last-months": "Last {x} months", - "label.leave": "Leave", - "label.leave-team": "Leave team", - "label.less-than": "Less than", - "label.less-than-equals": "Less than or equals", + "label.last-days": "{x} ថ្ងៃចុងក្រោយ", + "label.last-hours": "{x} ម៉ោងចុងក្រោយ", + "label.last-months": "{x} ខែចុងក្រោយ", + "label.leave": "ចាកចេញ", + "label.leave-team": "ចេញពីក្រុម", + "label.less-than": "តិច​ជាង", + "label.less-than-equals": "តិចជាង ឬស្មើ", "label.login": "Login", - "label.logout": "ចេញ", - "label.manage": "Manage", - "label.manager": "Manager", + "label.logout": "Logout", + "label.manage": "គ្រប់គ្រង", + "label.manager": "អ្នកគ្រប់គ្រង", "label.max": "Max", - "label.member": "Member", - "label.members": "Members", + "label.member": "សមាជិក", + "label.members": "សមាជិក", "label.min": "Min", "label.mobile": "ទូរស័ព្ទចល័ត", "label.more": "បន្ថែម", - "label.my-account": "My account", - "label.my-websites": "My websites", + "label.my-account": "គណនី​របស់ខ្ញុំ", + "label.my-websites": "គេហទំព័ររបស់ខ្ញុំ", "label.name": "ឈ្មោះ", "label.new-password": "ពាក្យសម្ងាត់​ថ្មី", "label.none": "មិនមាន", "label.number-of-records": "{x} {x, plural, one {record} other {records}}", "label.ok": "OK", "label.os": "OS", - "label.overview": "Overview", + "label.overview": "ទិដ្ឋភាពរួម", "label.owner": "ម្ចាស់", - "label.page-of": "Page {current} of {total}", + "label.page-of": "ទំព័រទី {current} នៃ {total}", "label.page-views": "អ្នកមើលទំព័រ", - "label.pageTitle": "Page title", + "label.pageTitle": "ចំណងជើងទំព័រ", "label.pages": "ទំព័រ", "label.password": "ពាក្យសម្ងាត់​", "label.powered-by": "ដំណើរការដោយ {name}", - "label.previous": "Previous", - "label.previous-period": "Previous period", - "label.previous-year": "Previous year", - "label.profile": "ប្រវត្តិរូប", + "label.previous": "មុន", + "label.previous-period": "មួយរយៈពេលមុន", + "label.previous-year": "ឆ្នាំ​មុន", + "label.profile": "គណនី", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", "label.query-parameters": "ប៉ារ៉ាម៉ែត្រ Query", "label.realtime": "ឥលូវនេះ", - "label.referrer": "Referrer", + "label.referrer": "អ្នកណែនាំ", "label.referrers": "អ្នកណែនាំ", "label.refresh": "ផ្ទុកឡើងវិញ", "label.regenerate": "Regenerate", "label.region": "Region", "label.regions": "Regions", - "label.remove": "Remove", - "label.remove-member": "Remove member", - "label.reports": "Reports", + "label.remove": "លុប", + "label.remove-member": "លុបសមាជិកក្រុម", + "label.reports": "របាយការណ៍", "label.required": "ទាមទារ", - "label.reset": "កំណត់ឡើងវិញ", + "label.reset": "កែសម្រួល", "label.reset-website": "កំណត់ស្ថិតិឡើងវិញ", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", - "label.role": "Role", + "label.role": "មុខងារ", "label.run-query": "Run query", "label.save": "រក្សាទុក", "label.screens": "ប្រភេទឧបករណ៍", - "label.search": "Search", - "label.select": "Select", - "label.select-date": "Select date", - "label.select-role": "Select role", - "label.select-website": "Select website", + "label.search": "ស្វែងរក", + "label.select": "ជ្រើសរើស", + "label.select-date": "ជ្រើសរើសកាលបរិច្ឆេទ", + "label.select-role": "ជ្រើសរើសមុខងារ", + "label.select-website": "ជ្រើសរើសគេហទំព័រ", "label.sessions": "Sessions", "label.settings": "ការកំណត់", "label.share-url": "ចែករំលែក URL", @@ -173,34 +173,34 @@ "label.steps": "Steps", "label.sum": "Sum", "label.tablet": "ថេប្លេត", - "label.team": "Team", - "label.team-id": "Team ID", - "label.team-manager": "Team manager", - "label.team-member": "Team member", - "label.team-name": "Team name", - "label.team-owner": "Team owner", + "label.team": "ក្រុម", + "label.team-id": "ID ក្រុម", + "label.team-manager": "អ្នកគ្រប់គ្រងក្រុម", + "label.team-member": "សមាជិកក្រុម", + "label.team-name": "ឈ្មោះក្រុម", + "label.team-owner": "ម្ចាស់ក្រុម", "label.team-view-only": "Team view only", - "label.team-websites": "Team websites", - "label.teams": "Teams", + "label.team-websites": "គេហទំព័ររបស់ក្រុម", + "label.teams": "ក្រុម", "label.theme": "រូបរាង", "label.this-month": "ខែនេះ", "label.this-week": "ស​ប្តា​ហ៍​នេះ", "label.this-year": "ឆ្នាំ​នេះ", "label.timezone": "តំបន់ម៉ោង", - "label.title": "Title", + "label.title": "ចំណងជើង", "label.today": "ថ្ងៃនេះ", "label.toggle-charts": "បិទ/បើកតារាង", - "label.total": "Total", - "label.total-records": "Total records", + "label.total": "សរុប", + "label.total-records": "កំណត់ត្រាសរុប", "label.tracking-code": "លេខកូដតាមដាន", - "label.transfer": "Transfer", - "label.transfer-website": "Transfer website", - "label.true": "True", + "label.transfer": "ការផ្ទេរ", + "label.transfer-website": "ការផ្ទេរគេហទំព័រ", + "label.true": "ពិត", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "អ្នកចូលមើលម្នាក់ៗ", "label.unknown": "មិនស្គាល់", - "label.untitled": "Untitled", + "label.untitled": "គ្មានចំណងជើង", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", @@ -212,56 +212,56 @@ "label.value": "Value", "label.view": "View", "label.view-details": "មើលព័ត៌មានលម្អិត", - "label.view-only": "View only", + "label.view-only": "បានតែមើលប៉ុណ្ណោះ", "label.views": "អ្នកចូលមើល", "label.views-per-visit": "Views per visit", "label.visit-duration": "មើលជាមធ្យម", "label.visitors": "អ្នកទស្សនា", "label.visits": "Visits", - "label.website": "Website", - "label.website-id": "Website ID", + "label.website": "គេហទំព័រ", + "label.website-id": "ID គេហទំព័រ", "label.websites": "គេហទំព័រ", "label.window": "Window", "label.yesterday": "ម្សិលមិញ", - "message.action-confirmation": "Type {confirmation} in the box below to confirm.", + "message.action-confirmation": "សសេរ {confirmation} នៅក្នុងប្រអប់ខាងក្រោមដើម្បីបញ្ជាក់។", "message.active-users": "មានអ្នកមើល {x} នាក់ ឥលូវនេះ", "message.collected-data": "Collected data", "message.confirm-delete": "តើអ្នកប្រាកដថាចង់លុប {target} ទេ?", - "message.confirm-leave": "Are you sure you want to leave {target}?", - "message.confirm-remove": "Are you sure you want to remove {target}?", + "message.confirm-leave": "តើអ្នកប្រាកដថាចង់ចាកចេញ {target} ទេ?", + "message.confirm-remove": "តើអ្នកប្រាកដថាចង់លុប {target} ទេ?", "message.confirm-reset": "តើអ្នកប្រាកដថាចង់កំណត់ស្ថិតិរបស់ {target} ឡើងវិញទេ?", - "message.delete-team-warning": "Deleting a team will also delete all team websites.", - "message.delete-website-warning": "ទិន្នន័យដែលពាក់ព័ន្ធទាំងអស់នឹងត្រូវបានលុបផងដែរ។", + "message.delete-team-warning": "ពេលលុបក្រុម គេហទំព័ររបស់ក្រុមក៏នឹងត្រូវលប់ចោលទាំងអស់ផងដែរ។", + "message.delete-website-warning": "ទិន្នន័យរបស់គេហទំព័រទាំងអស់នឹងត្រូវលុបចោល។", "message.error": "មាន​អ្វីមួយ​មិន​ប្រក្រតី។", "message.event-log": "{event} on {url}", "message.go-to-settings": "ការកំណត់", - "message.incorrect-username-password": "ឈ្មោះអ្នកប្រើ/ពាក្យសម្ងាត់មិនត្រឹមត្រូវ។", - "message.invalid-domain": "ឈ្មោះគេហទំព័រមិន​ត្រឹមត្រូវ", - "message.min-password-length": "Minimum length of {n} characters", - "message.new-version-available": "A new version of Umami {version} is available!", - "message.no-data-available": "មិនមានទិន្នន័យទេ។", + "message.incorrect-username-password": "ឈ្មោះអ្នកប្រើឬពាក្យសម្ងាត់មិនត្រឹមត្រូវ។", + "message.invalid-domain": "Domain មិន​ត្រឹមត្រូវ", + "message.min-password-length": "តិចបំផុតដែលមានអក្សរ {n} តួអក្សរ", + "message.new-version-available": "Version ថ្មីនៃ Umami {version} អាចប្រើប្រាស់បានហើយ!", + "message.no-data-available": "មិនមានទិន្នន័យ។", "message.no-event-data": "No event data is available.", - "message.no-match-password": "ពាក្យសម្ងាត់មិនត្រូវគ្នាទេ", - "message.no-results-found": "No results were found.", - "message.no-team-websites": "This team does not have any websites.", - "message.no-teams": "You have not created any teams.", - "message.no-users": "There are no users.", + "message.no-match-password": "ពាក្យសម្ងាត់មិនត្រូវគ្នាទេ។", + "message.no-results-found": "មិនមានលទ្ធផល។", + "message.no-team-websites": "ក្រុមនេះមិនមានគេហទំព័រទេ។", + "message.no-teams": "អ្នកមិនទាន់បានបង្កើតក្រុមណាមួយទេ។", + "message.no-users": "មិនមានអ្នកប្រើប្រាស់ទេ។", "message.no-websites-configured": "អ្នកមិនទាន់បានដាក់គេហទំព័រណាមួយចូលទេ។", "message.page-not-found": "រកមិនឃើញទំព័រ។", - "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", + "message.reset-website": "ដើម្បីកែគេហទំព័រនេះឡើងវិញ សូមសរសេរ {confirmation} នៅក្នុងប្រអប់ខាងក្រោមដើម្បីបញ្ជាក់។", "message.reset-website-warning": "ស្ថិតិទាំងអស់សម្រាប់គេហទំព័រនេះនឹងត្រូវបានលុប ប៉ុន្តែកូដតាមដានរបស់អ្នកនឹងនៅដដែល។", "message.saved": "រក្សាទុកដោយជោគជ័យ។", - "message.share-url": "នេះគឺជា URL ដែលបានចែករំលែកជាសាធារណៈសម្រាប់ {target}។", + "message.share-url": "នេះគឺជា URL ដែលអាចចែករំលែកជាសាធារណៈបានសម្រាប់ {target}។", "message.team-already-member": "You are already a member of the team.", - "message.team-not-found": "Team not found.", - "message.team-websites-info": "Websites can be viewed by anyone on the team.", - "message.tracking-code": "លេខកូដតាមដាន", - "message.transfer-team-website-to-user": "Transfer this website to your account?", - "message.transfer-user-website-to-team": "Select the team to transfer this website to.", - "message.transfer-website": "Transfer website ownership to your account or another team.", + "message.team-not-found": "រកក្រុមមិនឃើញទេ។", + "message.team-websites-info": "គេហទំព័រនេះអាចមើលបានតែសមាជិកក្រុមតែប៉ុណ្ណោះ", + "message.tracking-code": "ដើម្បីតាមដានស្ថិតិសម្រាប់គេហទំព័រអ្នក សូមដាក់កូដខាងក្រោមទៅក្នុងផ្នែក ... នៃ HTML របស់អ្នក។", + "message.transfer-team-website-to-user": "ផ្ទេរគេហទំព័រនេះទៅគណនីរបស់អ្នក។?", + "message.transfer-user-website-to-team": "ជ្រើសក្រុមដែរត្រូវផ្ទេរគេហទំព័រនេះទៅ។", + "message.transfer-website": "ផ្ទេរកម្មសិទ្ធិគេហទំព័រទៅគណនីរបស់អ្នក ឬក្រុមផ្សេងទៀត។", "message.triggered-event": "Triggered event", - "message.user-deleted": "User deleted.", - "message.viewed-page": "Viewed page", + "message.user-deleted": "អ្នកប្រើប្រាស់ត្រូវបានលុបចោល។", + "message.viewed-page": "ទំព័រដែលបានមើល", "message.visitor-log": "អ្នកមើលពីប្រទេស {country} ប្រើប្រាស់កម្មវិធី {browser} លើឧបករណ៍ {os} {device}", "message.visitors-dropped-off": "Visitors dropped off" } From d270031793aaba5239ddd9c8c66180ef5dfc89b4 Mon Sep 17 00:00:00 2001 From: manethpak Date: Wed, 10 Jul 2024 22:18:31 +0700 Subject: [PATCH 030/132] fix: typo, update wording and fill missing words/sentence --- src/lang/km-KH.json | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/lang/km-KH.json b/src/lang/km-KH.json index e10b32b56e..f45ed44945 100644 --- a/src/lang/km-KH.json +++ b/src/lang/km-KH.json @@ -34,7 +34,7 @@ "label.country": "ប្រទេស", "label.create": "បង្កើត", "label.create-report": "បង្កើតរបាយការណ៍", - "label.create-team": "បង្កើតរបាយក្រុម", + "label.create-team": "បង្កើតក្រុម", "label.create-user": "បង្កើតអ្នកប្រើប្រាស់", "label.created": "បង្កើតនៅ", "label.created-by": "បង្កើតដោយ", @@ -146,15 +146,15 @@ "label.referrers": "អ្នកណែនាំ", "label.refresh": "ផ្ទុកឡើងវិញ", "label.regenerate": "Regenerate", - "label.region": "Region", - "label.regions": "Regions", + "label.region": "តំបន់", + "label.regions": "តំបន់", "label.remove": "លុប", "label.remove-member": "លុបសមាជិកក្រុម", "label.reports": "របាយការណ៍", "label.required": "ទាមទារ", "label.reset": "កែសម្រួល", "label.reset-website": "កំណត់ស្ថិតិឡើងវិញ", - "label.retention": "Retention", + "label.retention": "ការរក្សាទុក", "label.retention-description": "Measure your website stickiness by tracking how often users return.", "label.role": "មុខងារ", "label.run-query": "Run query", @@ -169,8 +169,8 @@ "label.settings": "ការកំណត់", "label.share-url": "ចែករំលែក URL", "label.single-day": "ថ្ងៃតែមួយ", - "label.start-step": "Start Step", - "label.steps": "Steps", + "label.start-step": "ជំហានចាប់ផ្តើម", + "label.steps": "ជំហាន", "label.sum": "Sum", "label.tablet": "ថេប្លេត", "label.team": "ក្រុម", @@ -204,20 +204,20 @@ "label.update": "Update", "label.url": "URL", "label.urls": "URLs", - "label.user": "User", + "label.user": "អ្នកប្រើប្រាស់", "label.username": "ឈ្មោះ​អ្នកប្រើប្រាស់", - "label.users": "Users", + "label.users": "អ្នកប្រើប្រាស់", "label.utm": "UTM", - "label.utm-description": "Track your campaigns through UTM parameters.", + "label.utm-description": "តាមដានយុទ្ធនាការរបស់អ្នកតាមរយៈប៉ារ៉ាម៉ែត្រ UTM។", "label.value": "Value", "label.view": "View", "label.view-details": "មើលព័ត៌មានលម្អិត", "label.view-only": "បានតែមើលប៉ុណ្ណោះ", "label.views": "អ្នកចូលមើល", "label.views-per-visit": "Views per visit", - "label.visit-duration": "មើលជាមធ្យម", + "label.visit-duration": "រយៈពេលទស្សនា", "label.visitors": "អ្នកទស្សនា", - "label.visits": "Visits", + "label.visits": "ទស្សនា", "label.website": "គេហទំព័រ", "label.website-id": "ID គេហទំព័រ", "label.websites": "គេហទំព័រ", @@ -225,7 +225,7 @@ "label.yesterday": "ម្សិលមិញ", "message.action-confirmation": "សសេរ {confirmation} នៅក្នុងប្រអប់ខាងក្រោមដើម្បីបញ្ជាក់។", "message.active-users": "មានអ្នកមើល {x} នាក់ ឥលូវនេះ", - "message.collected-data": "Collected data", + "message.collected-data": "ទិន្នន័យដែលបានប្រមូលទុក", "message.confirm-delete": "តើអ្នកប្រាកដថាចង់លុប {target} ទេ?", "message.confirm-leave": "តើអ្នកប្រាកដថាចង់ចាកចេញ {target} ទេ?", "message.confirm-remove": "តើអ្នកប្រាកដថាចង់លុប {target} ទេ?", @@ -240,7 +240,7 @@ "message.min-password-length": "តិចបំផុតដែលមានអក្សរ {n} តួអក្សរ", "message.new-version-available": "Version ថ្មីនៃ Umami {version} អាចប្រើប្រាស់បានហើយ!", "message.no-data-available": "មិនមានទិន្នន័យ។", - "message.no-event-data": "No event data is available.", + "message.no-event-data": "មិនមានទិន្នន័យព្រឹត្តិការណ៍ទេ។", "message.no-match-password": "ពាក្យសម្ងាត់មិនត្រូវគ្នាទេ។", "message.no-results-found": "មិនមានលទ្ធផល។", "message.no-team-websites": "ក្រុមនេះមិនមានគេហទំព័រទេ។", @@ -252,7 +252,7 @@ "message.reset-website-warning": "ស្ថិតិទាំងអស់សម្រាប់គេហទំព័រនេះនឹងត្រូវបានលុប ប៉ុន្តែកូដតាមដានរបស់អ្នកនឹងនៅដដែល។", "message.saved": "រក្សាទុកដោយជោគជ័យ។", "message.share-url": "នេះគឺជា URL ដែលអាចចែករំលែកជាសាធារណៈបានសម្រាប់ {target}។", - "message.team-already-member": "You are already a member of the team.", + "message.team-already-member": "អ្នកគឺជាសមាជិកនៃក្រុមរួចហើយ។", "message.team-not-found": "រកក្រុមមិនឃើញទេ។", "message.team-websites-info": "គេហទំព័រនេះអាចមើលបានតែសមាជិកក្រុមតែប៉ុណ្ណោះ", "message.tracking-code": "ដើម្បីតាមដានស្ថិតិសម្រាប់គេហទំព័រអ្នក សូមដាក់កូដខាងក្រោមទៅក្នុងផ្នែក ... នៃ HTML របស់អ្នក។", @@ -263,5 +263,5 @@ "message.user-deleted": "អ្នកប្រើប្រាស់ត្រូវបានលុបចោល។", "message.viewed-page": "ទំព័រដែលបានមើល", "message.visitor-log": "អ្នកមើលពីប្រទេស {country} ប្រើប្រាស់កម្មវិធី {browser} លើឧបករណ៍ {os} {device}", - "message.visitors-dropped-off": "Visitors dropped off" + "message.visitors-dropped-off": "ចំនួនអ្នកទស្សនាធ្លាក់ចុះ" } From eda5adfd79fe9c9d2656b0fcba09409a0945e388 Mon Sep 17 00:00:00 2001 From: Chris Heinz Date: Sat, 13 Jul 2024 16:50:31 +0200 Subject: [PATCH 031/132] replace vh with dvh --- src/app/(main)/layout.module.css | 1 + src/app/share/[...shareId]/SharePage.module.css | 1 + src/components/layout/Page.module.css | 1 + 3 files changed, 3 insertions(+) diff --git a/src/app/(main)/layout.module.css b/src/app/(main)/layout.module.css index 58c1cacf63..290c38a27c 100644 --- a/src/app/(main)/layout.module.css +++ b/src/app/(main)/layout.module.css @@ -17,5 +17,6 @@ grid-row: 2 / 3; min-height: 0; height: calc(100vh - 60px); + height: calc(100dvh - 60px); overflow-y: auto; } diff --git a/src/app/share/[...shareId]/SharePage.module.css b/src/app/share/[...shareId]/SharePage.module.css index d985435c11..f6c68cf6a1 100644 --- a/src/app/share/[...shareId]/SharePage.module.css +++ b/src/app/share/[...shareId]/SharePage.module.css @@ -1,4 +1,5 @@ .container { flex: 1; min-height: calc(100vh - 200px); + min-height: calc(100dvh - 200px); } diff --git a/src/components/layout/Page.module.css b/src/components/layout/Page.module.css index 52893157cf..3b9a4581b0 100644 --- a/src/components/layout/Page.module.css +++ b/src/components/layout/Page.module.css @@ -8,4 +8,5 @@ margin: 0 auto; padding: 0 20px; min-height: calc(100vh - 60px); + min-height: calc(100dvh - 60px); } From 4f113bc3ac67b136190098f68820e7be08257578 Mon Sep 17 00:00:00 2001 From: Chris Heinz Date: Sun, 14 Jul 2024 12:16:52 +0200 Subject: [PATCH 032/132] fix website header wrapping and height --- .../(main)/websites/[websiteId]/WebsiteHeader.module.css | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/WebsiteHeader.module.css b/src/app/(main)/websites/[websiteId]/WebsiteHeader.module.css index 3e58c8a3eb..90c3f5cb17 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteHeader.module.css +++ b/src/app/(main)/websites/[websiteId]/WebsiteHeader.module.css @@ -1,7 +1,9 @@ .header { - display: grid; - grid-template-columns: 1fr max-content; + display: flex; + gap: 10px; align-items: center; + flex-wrap: wrap; + padding: 20px 0px; } .title { @@ -12,7 +14,7 @@ font-size: 24px; font-weight: 700; overflow: hidden; - height: 100px; + height: 60px; } .actions { @@ -22,6 +24,7 @@ justify-content: flex-end; gap: 30px; min-height: 0; + margin-left: auto; } .selected { From 60d4d8db22d1ede650ee54768296e72962c88bee Mon Sep 17 00:00:00 2001 From: eagleon Date: Mon, 15 Jul 2024 11:23:21 +0800 Subject: [PATCH 033/132] fix: Compatible with browsers that do not support ES2019 --- src/tracker/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tracker/index.js b/src/tracker/index.js index 97bf3bb06a..90a73c432b 100644 --- a/src/tracker/index.js +++ b/src/tracker/index.js @@ -44,7 +44,7 @@ if (result !== str) { return result; } - } catch { + } catch (e) { return str; } @@ -55,7 +55,7 @@ try { const { pathname, search } = new URL(url); url = pathname + search; - } catch { + } catch (e) { /* empty */ } return excludeSearch ? url.split('?')[0] : url; @@ -217,7 +217,7 @@ const text = await res.text(); return (cache = text); - } catch { + } catch (e) { /* empty */ } }; From 5eead0e1c85bd6eebcfcabacd81a24d10d3b0eed Mon Sep 17 00:00:00 2001 From: Lena Emme <38376566+Feuerhamster@users.noreply.github.com> Date: Tue, 16 Jul 2024 14:17:00 +0200 Subject: [PATCH 034/132] improve german translation --- src/lang/de-DE.json | 124 ++++++++++++++++++++++---------------------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/src/lang/de-DE.json b/src/lang/de-DE.json index aca4b863f9..4e6b112fea 100644 --- a/src/lang/de-DE.json +++ b/src/lang/de-DE.json @@ -4,19 +4,19 @@ "label.activity-log": "Aktivitätsverlauf", "label.add": "Hinzufügen", "label.add-description": "Beschreibung hinzufügen", - "label.add-member": "Add member", - "label.add-step": "Add step", + "label.add-member": "Mitglied hinzufügen", + "label.add-step": "Schritt hinzufügen", "label.add-website": "Website hinzufügen", "label.admin": "Administrator", "label.after": "Nach", "label.all": "Alle", "label.all-time": "Gesamter Zeitraum", - "label.analytics": "Analytics", + "label.analytics": "Analysen", "label.average": "Durchschnitt", "label.back": "Zurück", "label.before": "Vor", "label.bounce-rate": "Absprungrate", - "label.breakdown": "Breakdown", + "label.breakdown": "Aufschlüsselung", "label.browser": "Browser", "label.browsers": "Browser", "label.cancel": "Abbrechen", @@ -24,21 +24,21 @@ "label.cities": "Städte", "label.city": "Stadt", "label.clear-all": "Alles löschen", - "label.compare": "Compare", + "label.compare": "Vergleichen", "label.confirm": "Bestätigen", "label.confirm-password": "Passwort wiederholen", "label.contains": "Enthält", "label.continue": "Weiter", - "label.count": "Count", + "label.count": "Anzahl", "label.countries": "Länder", "label.country": "Land", - "label.create": "Create", + "label.create": "Erstellen", "label.create-report": "Bericht erstellen", "label.create-team": "Team erstellen", "label.create-user": "Benutzer erstellen", "label.created": "Erstellt", - "label.created-by": "Created By", - "label.current": "Current", + "label.created-by": "Erstellt von", + "label.current": "Aktuell", "label.current-password": "Derzeitiges Passwort", "label.custom-range": "Benutzerdefinierter Bereich", "label.dashboard": "Übersicht", @@ -48,7 +48,7 @@ "label.day": "Tag", "label.default-date-range": "Voreingestellter Datumsbereich", "label.delete": "Löschen", - "label.delete-report": "Delete report", + "label.delete-report": "Bericht löschen", "label.delete-team": "Team löschen", "label.delete-user": "Benutzer löschen", "label.delete-website": "Website löschen", @@ -60,17 +60,17 @@ "label.dismiss": "Verwerfen", "label.does-not-contain": "Enthält nicht", "label.domain": "Domain", - "label.dropoff": "Dropoff", + "label.dropoff": "Absprung", "label.edit": "Bearbeiten", "label.edit-dashboard": "Dashboard bearbeiten", - "label.edit-member": "Edit member", + "label.edit-member": "Mitglied bearbeiten", "label.enable-share-url": "Freigabe-URL aktivieren", - "label.end-step": "End Step", - "label.entry": "Entry URL", + "label.end-step": "Schlussschritt", + "label.entry": "Eintrags-URL", "label.event": "Event", "label.event-data": "Eventdaten", "label.events": "Ereignisse", - "label.exit": "Exit URL", + "label.exit": "Ausgangs-URL", "label.false": "Falsch", "label.field": "Feld", "label.fields": "Felder", @@ -79,46 +79,46 @@ "label.filter-raw": "Rohdaten", "label.filters": "Filter", "label.funnel": "Funnel", - "label.funnel-description": "Understand the conversion and drop-off rate of users.", - "label.goal": "Goal", - "label.goals": "Goals", - "label.goals-description": "Track your goals for pageviews and events.", + "label.funnel-description": "Verstehe die Konversions- und Abbruchrate der Nutzer.", + "label.goal": "Ziel", + "label.goals": "Ziele", + "label.goals-description": "Verfolgen Sie Ihre Ziele für Seitenaufrufe und Ereignisse.", "label.greater-than": "Größer als", "label.greater-than-equals": "Größer oder gleich", "label.host": "Host", "label.hosts": "Hosts", - "label.insights": "Insights", - "label.insights-description": "Dive deeper into your data by using segments and filters.", + "label.insights": "Einblicke", + "label.insights-description": "Vertiefen Sie Ihre Daten mit Hilfe von Segmenten und Filtern.", "label.is": "Ist", "label.is-not": "Ist nicht", "label.is-not-set": "Ist nicht gesetzt", "label.is-set": "Ist gesetzt", "label.join": "Beitreten", "label.join-team": "Team beitreten", - "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey": "Reise", + "label.journey-description": "Verstehe, wie Nutzer auf der Webseite navigieren", "label.language": "Sprache", "label.languages": "Sprachen", "label.laptop": "Laptop", - "label.last-days": "Letzte {x} Tage", - "label.last-hours": "Letzte {x} Stunden", - "label.last-months": "Last {x} months", + "label.last-days": "Letzten {x} Tage", + "label.last-hours": "Letzten {x} Stunden", + "label.last-months": "Letzten {x} Monate", "label.leave": "Verlassen", "label.leave-team": "Team verlassen", "label.less-than": "Kleiner als", "label.less-than-equals": "Kleiner oder gleich", "label.login": "Anmelden", "label.logout": "Abmelden", - "label.manage": "Manage", - "label.manager": "Manager", + "label.manage": "Verwalten", + "label.manager": "Verwaltung", "label.max": "Max", - "label.member": "Member", + "label.member": "Mitglied", "label.members": "Mitglieder", "label.min": "Min", "label.mobile": "Handy", "label.more": "Mehr", - "label.my-account": "My account", - "label.my-websites": "Meine Websites", + "label.my-account": "Mein Account", + "label.my-websites": "Meine Webseiten", "label.name": "Name", "label.new-password": "Neues Passwort", "label.none": "Keine", @@ -133,11 +133,11 @@ "label.pages": "Seiten", "label.password": "Passwort", "label.powered-by": "Betrieben durch {name}", - "label.previous": "Previous", - "label.previous-period": "Previous period", - "label.previous-year": "Previous year", + "label.previous": "Vorheriges", + "label.previous-period": "Vorherige Periode", + "label.previous-year": "Vorheriges Jahr", "label.profile": "Profil", - "label.property": "Property", + "label.property": "Eigentum", "label.queries": "Abfragen", "label.query": "Abfrage", "label.query-parameters": "Abfrageparameter", @@ -149,37 +149,37 @@ "label.region": "Region", "label.regions": "Regionen", "label.remove": "Entfernen", - "label.remove-member": "Remove member", + "label.remove-member": "Mitglied entfernen", "label.reports": "Berichte", "label.required": "Erforderlich", "label.reset": "Zurücksetzen", "label.reset-website": "Statistik zurücksetzen", - "label.retention": "Retention", - "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.retention": "Bewahrung", + "label.retention-description": "Messen Sie die Verweildauer auf Ihrer Website, indem Sie verfolgen, wie oft die Nutzer zurückkehren.", "label.role": "Rolle", "label.run-query": "Abfrage starten", "label.save": "Speichern", "label.screens": "Bildschirmauflösungen", - "label.search": "Search", - "label.select": "Select", + "label.search": "Suche", + "label.select": "Auswählen", "label.select-date": "Datum auswählen", - "label.select-role": "Select role", - "label.select-website": "Website auswählen", + "label.select-role": "Rolle auswählen", + "label.select-website": "Webseite auswählen", "label.sessions": "Sitzungen", "label.settings": "Einstellungen", "label.share-url": "Freigabe-URL", "label.single-day": "Ein Tag", - "label.start-step": "Start Step", - "label.steps": "Steps", + "label.start-step": "Startschritt", + "label.steps": "Schritte", "label.sum": "Summe", "label.tablet": "Tablet", "label.team": "Team", "label.team-id": "Team-ID", - "label.team-manager": "Team manager", + "label.team-manager": "Team-Manager", "label.team-member": "Team-Mitglied", "label.team-name": "Name des Teams", "label.team-owner": "Team-Eigentümer", - "label.team-view-only": "Team view only", + "label.team-view-only": "Nur für Team sichtbar", "label.team-websites": "Team-Websites", "label.teams": "Teams", "label.theme": "Thema", @@ -208,29 +208,29 @@ "label.username": "Benutzername", "label.users": "Benutzer", "label.utm": "UTM", - "label.utm-description": "Track your campaigns through UTM parameters.", + "label.utm-description": "Tracke deine Kampagnen mit UTM parameterns.", "label.value": "Wert", "label.view": "Anzeigen", "label.view-details": "Details anzeigen", "label.view-only": "Nur ansehen", "label.views": "Aufrufe", - "label.views-per-visit": "Views per visit", + "label.views-per-visit": "Aufrufe pro Besuch", "label.visit-duration": "Durchschn. Besuchszeit", "label.visitors": "Besucher", - "label.visits": "Visits", - "label.website": "Website", - "label.website-id": "Website ID", - "label.websites": "Websites", + "label.visits": "Besuche", + "label.website": "Webseite", + "label.website-id": "Webseiten-ID", + "label.websites": "Webseiten", "label.window": "Fenster", "label.yesterday": "Gestern", - "message.action-confirmation": "Type {confirmation} in the box below to confirm.", + "message.action-confirmation": "Schreibe {confirmation} in die Box zur bestätigung.", "message.active-users": "{x} {x, plural, one {aktiver Besucher} other {aktive Besucher}}", - "message.collected-data": "Collected data", + "message.collected-data": "Gesammelte Daten", "message.confirm-delete": "Sind Sie sich sicher, {target} zu löschen?", "message.confirm-leave": "Sind Sie sicher, dass die {target} verlassen möchten?", - "message.confirm-remove": "Are you sure you want to remove {target}?", + "message.confirm-remove": "Sind Sie sicher, {target} zu entfernen?", "message.confirm-reset": "Sind Sie sicher, dass Sie die Statistiken von {target} zurücksetzen wollen?", - "message.delete-team-warning": "Deleting a team will also delete all team websites.", + "message.delete-team-warning": "Ein Team zu löschen, wird auch alle Team-Webseiten löschen.", "message.delete-website-warning": "Alle zugehörigen Daten werden ebenfalls gelöscht.", "message.error": "Es ist ein Fehler aufgetreten.", "message.event-log": "{event} auf {url}", @@ -256,12 +256,12 @@ "message.team-not-found": "Team nicht gefunden.", "message.team-websites-info": "Websites können von jedem im Team eingesehen werden.", "message.tracking-code": "Tracking Code", - "message.transfer-team-website-to-user": "Transfer this website to your account?", - "message.transfer-user-website-to-team": "Select the team to transfer this website to.", - "message.transfer-website": "Transfer website ownership to your account or another team.", - "message.triggered-event": "Triggered event", + "message.transfer-team-website-to-user": "Diese Webseite zu deinem Account transferieren?", + "message.transfer-user-website-to-team": "Wähle ein Team aus, zu dem die Webseite transferiert werden soll.", + "message.transfer-website": "Übertrage die Eigentümerrechte zu deinem Account oder einem anderen Team.", + "message.triggered-event": "Ausgelöstes Event", "message.user-deleted": "Benutzer gelöscht.", - "message.viewed-page": "Viewed page", + "message.viewed-page": "Besuchte Seite", "message.visitor-log": "Besucher aus {country} benutzt {browser} auf {os} {device}", - "message.visitors-dropped-off": "Visitors dropped off" + "message.visitors-dropped-off": "Besucherverlust" } From 3a4cefef7264e32a2721d08309bdc45caf52df0d Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Fri, 19 Jul 2024 11:20:18 -0700 Subject: [PATCH 035/132] update event/pageview metrics --- .../analytics/events/getEventMetrics.ts | 14 +++++++++---- .../analytics/pageviews/getPageviewMetrics.ts | 20 ++++++++++--------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/queries/analytics/events/getEventMetrics.ts b/src/queries/analytics/events/getEventMetrics.ts index 32cccd3e85..d8818bbabf 100644 --- a/src/queries/analytics/events/getEventMetrics.ts +++ b/src/queries/analytics/events/getEventMetrics.ts @@ -51,17 +51,23 @@ async function clickhouseQuery( eventType: EVENT_TYPE.customEvent, }); + const table = unit === 'hour' ? 'website_event_stats_hourly' : 'website_event_stats_daily'; + return rawQuery( ` select event_name x, ${getDateQuery('created_at', unit, timezone)} t, count(*) y - from website_event - where website_id = {websiteId:UUID} - and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type = {eventType:UInt32} + from ( + select arrayJoin(event_name) as event_name, + created_at + from ${table} website_event + where website_id = {websiteId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} + and event_type = {eventType:UInt32} ${filterQuery} + ) as g group by x, t order by t `, diff --git a/src/queries/analytics/pageviews/getPageviewMetrics.ts b/src/queries/analytics/pageviews/getPageviewMetrics.ts index 34d26d9dcd..f1c316d270 100644 --- a/src/queries/analytics/pageviews/getPageviewMetrics.ts +++ b/src/queries/analytics/pageviews/getPageviewMetrics.ts @@ -100,7 +100,7 @@ async function clickhouseQuery( let columnAgg = column; let excludeDomain = ''; if (column === 'referrer_domain') { - excludeDomain = `and referrer_domain != {websiteDomain:String} and referrer_domain != ''`; + excludeDomain = `and t != {websiteDomain:String} and t != ''`; } if (type === 'entry') { @@ -111,17 +111,19 @@ async function clickhouseQuery( columnAgg = `argMaxMerge(${column})`; } - const table = unit === 'hour' ? 'website_event_metric_hourly' : 'website_event_metric_daily'; + const table = unit === 'hour' ? 'website_event_stats_hourly' : 'website_event_stats_daily'; return rawQuery( ` - select ${column} x, sum(views) y - from ${table} website_event - where website_id = {websiteId:UUID} - and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type = {eventType:UInt32} - ${excludeDomain} - ${filterQuery} + select g.t as x, + count(*) as y + from ( + select arrayJoin(${column}) as t + from ${table} website_event + where website_id = {websiteId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} + and event_type = {eventType:UInt32} + ${filterQuery}) as g group by x order by y desc limit ${limit} From 829a45b40e8f0cb5177b6d8e3342459b365f7e9d Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Mon, 22 Jul 2024 18:06:36 -0700 Subject: [PATCH 036/132] Added sessions table. --- package.json | 6 +- .../(main)/reports/create/ReportTemplates.tsx | 6 ++ .../sessions/SessionMetricsBar.tsx | 5 ++ .../[websiteId]/sessions/SessionsTable.tsx | 20 +++-- src/components/metrics/EventsChart.tsx | 5 -- yarn.lock | 80 +++++++++---------- 6 files changed, 69 insertions(+), 53 deletions(-) create mode 100644 src/app/(main)/websites/[websiteId]/sessions/SessionMetricsBar.tsx diff --git a/package.json b/package.json index a7a3aba1e2..f14e552d50 100644 --- a/package.json +++ b/package.json @@ -64,9 +64,9 @@ ".next/cache" ], "dependencies": { - "@clickhouse/client": "^1.2.0", + "@clickhouse/client": "^1.3.0", "@fontsource/inter": "^4.5.15", - "@prisma/client": "5.16.1", + "@prisma/client": "5.16.2", "@prisma/extension-read-replicas": "^0.3.0", "@react-spring/web": "^9.7.3", "@tanstack/react-query": "^5.28.6", @@ -102,7 +102,7 @@ "next-basics": "^0.39.0", "node-fetch": "^3.2.8", "npm-run-all": "^4.1.5", - "prisma": "5.16.1", + "prisma": "5.16.2", "react": "^18.2.0", "react-basics": "^0.123.0", "react-beautiful-dnd": "^13.1.0", diff --git a/src/app/(main)/reports/create/ReportTemplates.tsx b/src/app/(main)/reports/create/ReportTemplates.tsx index e37efc0367..2314f7d6a6 100644 --- a/src/app/(main)/reports/create/ReportTemplates.tsx +++ b/src/app/(main)/reports/create/ReportTemplates.tsx @@ -51,6 +51,12 @@ export function ReportTemplates({ showHeader = true }: { showHeader?: boolean }) url: renderTeamUrl('/reports/journey'), icon: , }, + { + title: formatMessage(labels.revenue), + description: formatMessage(labels.revenueDescription), + url: renderTeamUrl('/reports/revenue'), + icon: , + }, ]; return ( diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionMetricsBar.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionMetricsBar.tsx new file mode 100644 index 0000000000..c4f21875d2 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionMetricsBar.tsx @@ -0,0 +1,5 @@ +export function SessionMetricsBar({ websiteId }: { websiteId: string }) { + return

{websiteId}

; +} + +export default SessionMetricsBar; diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx index 41c0fef353..d5ca439ad8 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx @@ -1,19 +1,29 @@ import { GridColumn, GridTable, useBreakpoint } from 'react-basics'; -import { useMessages } from 'components/hooks'; +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 ( - + + {row => formatValue(row.country, 'country')} + - + + {row => formatValue(row.browser, 'browser')} + - - + + {row => formatValue(row.device, 'device')} + + + {row => formatDistanceToNow(new Date(row.createdAt))} + ); } diff --git a/src/components/metrics/EventsChart.tsx b/src/components/metrics/EventsChart.tsx index 842ae60568..36ec867233 100644 --- a/src/components/metrics/EventsChart.tsx +++ b/src/components/metrics/EventsChart.tsx @@ -1,5 +1,4 @@ import { useMemo } from 'react'; -import { Loading } from 'react-basics'; import { colord } from 'colord'; import BarChart from 'components/charts/BarChart'; import { getDateArray } from 'lib/date'; @@ -51,10 +50,6 @@ export function EventsChart({ websiteId, className }: EventsChartProps) { }; }, [data, startDate, endDate, unit]); - if (isLoading) { - return ; - } - return ( Date: Mon, 22 Jul 2024 21:30:06 -0700 Subject: [PATCH 037/132] resolve entry / exit queries --- db/clickhouse/schema.sql | 201 +++++++++++++++++- .../analytics/pageviews/getPageviewMetrics.ts | 18 +- 2 files changed, 213 insertions(+), 6 deletions(-) diff --git a/db/clickhouse/schema.sql b/db/clickhouse/schema.sql index 53fba1fb76..e36ddf6121 100644 --- a/db/clickhouse/schema.sql +++ b/db/clickhouse/schema.sql @@ -66,4 +66,203 @@ CREATE TABLE umami.session_data ) engine = MergeTree ORDER BY (website_id, session_id, data_key, created_at) - SETTINGS index_granularity = 8192; \ No newline at end of file + 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), + 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, + toStartOfHour(created_at), + cityHash64(visit_id), + visit_id +) +SAMPLE BY cityHash64(visit_id) +TTL created_at + INTERVAL 10 DAY; + +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, + 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, + 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, + country, + subdivision1, + city, + event_type, + timestamp); + +-- stats daily +CREATE TABLE umami.website_event_stats_daily +( + website_id UUID, + session_id UUID, + visit_id UUID, + hostname LowCardinality(String), + browser LowCardinality(String), + os LowCardinality(String), + device 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, + toStartOfDay(created_at), + cityHash64(visit_id), + visit_id +) +SAMPLE BY cityHash64(visit_id); + +CREATE MATERIALIZED VIEW umami.website_event_stats_daily_mv +TO umami.website_event_stats_daily +AS +SELECT + website_id, + session_id, + visit_id, + hostname, + browser, + os, + device, + 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, + 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, + toStartOfDay(created_at) timestamp +FROM umami.website_event +GROUP BY website_id, + session_id, + visit_id, + hostname, + browser, + os, + device, + country, + subdivision1, + city, + event_type, + timestamp); \ No newline at end of file diff --git a/src/queries/analytics/pageviews/getPageviewMetrics.ts b/src/queries/analytics/pageviews/getPageviewMetrics.ts index d47af35f53..e9598cca6e 100644 --- a/src/queries/analytics/pageviews/getPageviewMetrics.ts +++ b/src/queries/analytics/pageviews/getPageviewMetrics.ts @@ -100,18 +100,25 @@ async function clickhouseQuery( eventType: column === 'event_name' ? EVENT_TYPE.customEvent : EVENT_TYPE.pageView, }); - let columnAgg = column; let excludeDomain = ''; + let groupByQuery = ''; + if (column === 'referrer_domain') { excludeDomain = `and t != {websiteDomain:String} and t != ''`; } + let columnQuery = `arrayJoin(${column})`; + if (type === 'entry') { - columnAgg = `argMinMerge(${column})`; + columnQuery = `visit_id x, argMinMerge(${column})`; } if (type === 'exit') { - columnAgg = `argMaxMerge(${column})`; + columnQuery = `visit_id x, argMaxMerge(${column})`; + } + + if (type === 'entry' || type === 'exit') { + groupByQuery = 'group by x'; } const table = unit === 'hour' ? 'website_event_stats_hourly' : 'website_event_stats_daily'; @@ -121,12 +128,13 @@ async function clickhouseQuery( select g.t as x, count(*) as y from ( - select arrayJoin(${column}) as t + select ${columnQuery} as t from ${table} website_event where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type = {eventType:UInt32} - ${filterQuery}) as g + ${filterQuery} + ${groupByQuery}) as g group by x order by y desc limit ${limit} From 6d0ae13bc82bd13cdeb143b71aa1d163c7058057 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Mon, 22 Jul 2024 22:13:04 -0700 Subject: [PATCH 038/132] add back exclude domain logic --- src/queries/analytics/pageviews/getPageviewMetrics.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/queries/analytics/pageviews/getPageviewMetrics.ts b/src/queries/analytics/pageviews/getPageviewMetrics.ts index e9598cca6e..d66ec6aa7a 100644 --- a/src/queries/analytics/pageviews/getPageviewMetrics.ts +++ b/src/queries/analytics/pageviews/getPageviewMetrics.ts @@ -133,7 +133,8 @@ async function clickhouseQuery( where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type = {eventType:UInt32} - ${filterQuery} + ${excludeDomain} + ${filterQuery} ${groupByQuery}) as g group by x order by y desc From 7ec845db36134e3f7b80abb1d3b0339b41e11ad3 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Mon, 22 Jul 2024 23:16:57 -0700 Subject: [PATCH 039/132] fix undefined datasets --- src/components/charts/Chart.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/charts/Chart.tsx b/src/components/charts/Chart.tsx index 6ba601597f..926defaf9e 100644 --- a/src/components/charts/Chart.tsx +++ b/src/components/charts/Chart.tsx @@ -79,7 +79,7 @@ export function Chart({ }; const updateChart = (data: any) => { - if (data.datasets.length === chart.current.data.datasets.length) { + if ((data?.datasets?.length || 0) === 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; From 9f3138e9b450dec8ab58e3b88b3409dece339542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Israel=20Ortiz=20de=20Z=C3=A1rate?= <69572@hubbell.com> Date: Tue, 23 Jul 2024 17:26:03 +0200 Subject: [PATCH 040/132] Update translations and fix typos --- public/intl/messages/am-ET.json | 2 +- public/intl/messages/ar-SA.json | 2 +- public/intl/messages/be-BY.json | 2 +- public/intl/messages/bg-BG.json | 2 +- public/intl/messages/bn-BD.json | 2 +- public/intl/messages/bs-BA.json | 2 +- public/intl/messages/ca-ES.json | 2 +- public/intl/messages/cs-CZ.json | 2 +- public/intl/messages/da-DK.json | 2 +- public/intl/messages/de-CH.json | 2 +- public/intl/messages/de-DE.json | 2 +- public/intl/messages/el-GR.json | 2 +- public/intl/messages/en-GB.json | 2 +- public/intl/messages/en-US.json | 2 +- public/intl/messages/es-ES.json | 2 +- public/intl/messages/fa-IR.json | 2 +- public/intl/messages/fi-FI.json | 2 +- public/intl/messages/fo-FO.json | 2 +- public/intl/messages/fr-FR.json | 2 +- public/intl/messages/ga-ES.json | 2 +- public/intl/messages/he-IL.json | 2 +- public/intl/messages/hi-IN.json | 2 +- public/intl/messages/hr-HR.json | 2 +- public/intl/messages/hu-HU.json | 2 +- public/intl/messages/id-ID.json | 2 +- public/intl/messages/it-IT.json | 2 +- public/intl/messages/ja-JP.json | 2 +- public/intl/messages/km-KH.json | 2 +- public/intl/messages/ko-KR.json | 2 +- public/intl/messages/lt-LT.json | 2 +- public/intl/messages/mn-MN.json | 2 +- public/intl/messages/ms-MY.json | 2 +- public/intl/messages/my-MM.json | 2 +- public/intl/messages/nb-NO.json | 2 +- public/intl/messages/nl-NL.json | 2 +- public/intl/messages/pl-PL.json | 2 +- public/intl/messages/pt-BR.json | 2 +- public/intl/messages/pt-PT.json | 2 +- public/intl/messages/ro-RO.json | 2 +- public/intl/messages/ru-RU.json | 2 +- public/intl/messages/si-LK.json | 2 +- public/intl/messages/sk-SK.json | 2 +- public/intl/messages/sl-SI.json | 2 +- public/intl/messages/sv-SE.json | 2 +- public/intl/messages/ta-IN.json | 2 +- public/intl/messages/th-TH.json | 2 +- public/intl/messages/tr-TR.json | 2 +- public/intl/messages/uk-UA.json | 2 +- public/intl/messages/ur-PK.json | 2 +- public/intl/messages/vi-VN.json | 2 +- public/intl/messages/zh-CN.json | 2 +- public/intl/messages/zh-TW.json | 2 +- src/components/messages.ts | 2 +- src/lang/am-ET.json | 2 +- src/lang/ar-SA.json | 2 +- src/lang/be-BY.json | 2 +- src/lang/bg-BG.json | 2 +- src/lang/bn-BD.json | 2 +- src/lang/bs-BA.json | 2 +- src/lang/ca-ES.json | 2 +- src/lang/cs-CZ.json | 2 +- src/lang/da-DK.json | 2 +- src/lang/de-CH.json | 2 +- src/lang/de-DE.json | 2 +- src/lang/el-GR.json | 2 +- src/lang/en-GB.json | 2 +- src/lang/en-US.json | 2 +- src/lang/es-ES.json | 66 ++++++++++++++++----------------- src/lang/fa-IR.json | 2 +- src/lang/fi-FI.json | 2 +- src/lang/fo-FO.json | 2 +- src/lang/fr-FR.json | 2 +- src/lang/ga-ES.json | 2 +- src/lang/he-IL.json | 2 +- src/lang/hi-IN.json | 2 +- src/lang/hr-HR.json | 2 +- src/lang/hu-HU.json | 2 +- src/lang/id-ID.json | 2 +- src/lang/it-IT.json | 2 +- src/lang/ja-JP.json | 2 +- src/lang/km-KH.json | 2 +- src/lang/lt-LT.json | 2 +- src/lang/mn-MN.json | 2 +- src/lang/ms-MY.json | 2 +- src/lang/my-MM.json | 2 +- src/lang/nb-NO.json | 2 +- src/lang/nl-NL.json | 2 +- src/lang/pl-PL.json | 2 +- src/lang/pt-BR.json | 2 +- src/lang/pt-PT.json | 2 +- src/lang/ro-RO.json | 2 +- src/lang/ru-RU.json | 2 +- src/lang/si-LK.json | 2 +- src/lang/sk-SK.json | 2 +- src/lang/sl-SI.json | 2 +- src/lang/sv-SE.json | 2 +- src/lang/ta-IN.json | 2 +- src/lang/th-TH.json | 2 +- src/lang/tr-TR.json | 2 +- src/lang/uk-UA.json | 2 +- src/lang/ur-PK.json | 2 +- src/lang/vi-VN.json | 2 +- src/lang/zh-CN.json | 2 +- src/lang/zh-TW.json | 2 +- 104 files changed, 136 insertions(+), 136 deletions(-) diff --git a/public/intl/messages/am-ET.json b/public/intl/messages/am-ET.json index 6565c98b20..65427a024d 100644 --- a/public/intl/messages/am-ET.json +++ b/public/intl/messages/am-ET.json @@ -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": [ diff --git a/public/intl/messages/ar-SA.json b/public/intl/messages/ar-SA.json index 721584d5a1..d6a1855e1f 100644 --- a/public/intl/messages/ar-SA.json +++ b/public/intl/messages/ar-SA.json @@ -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": [ diff --git a/public/intl/messages/be-BY.json b/public/intl/messages/be-BY.json index 9af4e89523..e6afc2ca85 100644 --- a/public/intl/messages/be-BY.json +++ b/public/intl/messages/be-BY.json @@ -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": [ diff --git a/public/intl/messages/bg-BG.json b/public/intl/messages/bg-BG.json index bf5f15dcd5..e970ec52e5 100644 --- a/public/intl/messages/bg-BG.json +++ b/public/intl/messages/bg-BG.json @@ -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": [ diff --git a/public/intl/messages/bn-BD.json b/public/intl/messages/bn-BD.json index 827271e3b6..31e04cfa49 100644 --- a/public/intl/messages/bn-BD.json +++ b/public/intl/messages/bn-BD.json @@ -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": [ diff --git a/public/intl/messages/bs-BA.json b/public/intl/messages/bs-BA.json index f3edb77352..3b5c10ff53 100644 --- a/public/intl/messages/bs-BA.json +++ b/public/intl/messages/bs-BA.json @@ -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": [ diff --git a/public/intl/messages/ca-ES.json b/public/intl/messages/ca-ES.json index f9b63535f0..94c7d3d41f 100644 --- a/public/intl/messages/ca-ES.json +++ b/public/intl/messages/ca-ES.json @@ -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": [ diff --git a/public/intl/messages/cs-CZ.json b/public/intl/messages/cs-CZ.json index a2500688a0..eaef42457d 100644 --- a/public/intl/messages/cs-CZ.json +++ b/public/intl/messages/cs-CZ.json @@ -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": [ diff --git a/public/intl/messages/da-DK.json b/public/intl/messages/da-DK.json index d9a6903f80..aac48bbc9b 100644 --- a/public/intl/messages/da-DK.json +++ b/public/intl/messages/da-DK.json @@ -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": [ diff --git a/public/intl/messages/de-CH.json b/public/intl/messages/de-CH.json index 32d3b4434c..0d06a0e03e 100644 --- a/public/intl/messages/de-CH.json +++ b/public/intl/messages/de-CH.json @@ -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": [ diff --git a/public/intl/messages/de-DE.json b/public/intl/messages/de-DE.json index 9a9b6d14f7..2482b208b5 100644 --- a/public/intl/messages/de-DE.json +++ b/public/intl/messages/de-DE.json @@ -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": [ diff --git a/public/intl/messages/el-GR.json b/public/intl/messages/el-GR.json index f44bd026a0..af0f11be3e 100644 --- a/public/intl/messages/el-GR.json +++ b/public/intl/messages/el-GR.json @@ -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": [ diff --git a/public/intl/messages/en-GB.json b/public/intl/messages/en-GB.json index efb8f1be4d..38a7857ccb 100644 --- a/public/intl/messages/en-GB.json +++ b/public/intl/messages/en-GB.json @@ -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": [ diff --git a/public/intl/messages/en-US.json b/public/intl/messages/en-US.json index bf911728a8..52b968ddf1 100644 --- a/public/intl/messages/en-US.json +++ b/public/intl/messages/en-US.json @@ -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": [ diff --git a/public/intl/messages/es-ES.json b/public/intl/messages/es-ES.json index 40ddded568..496fabc664 100644 --- a/public/intl/messages/es-ES.json +++ b/public/intl/messages/es-ES.json @@ -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": [ diff --git a/public/intl/messages/fa-IR.json b/public/intl/messages/fa-IR.json index cb10df4b1e..d5408e6458 100644 --- a/public/intl/messages/fa-IR.json +++ b/public/intl/messages/fa-IR.json @@ -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": [ diff --git a/public/intl/messages/fi-FI.json b/public/intl/messages/fi-FI.json index e43a185500..0af4dc6f4f 100644 --- a/public/intl/messages/fi-FI.json +++ b/public/intl/messages/fi-FI.json @@ -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": [ diff --git a/public/intl/messages/fo-FO.json b/public/intl/messages/fo-FO.json index 1b2d4f0721..c9df65e1e2 100644 --- a/public/intl/messages/fo-FO.json +++ b/public/intl/messages/fo-FO.json @@ -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": [ diff --git a/public/intl/messages/fr-FR.json b/public/intl/messages/fr-FR.json index 44fd708bee..bc4c51bb3f 100644 --- a/public/intl/messages/fr-FR.json +++ b/public/intl/messages/fr-FR.json @@ -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": [ diff --git a/public/intl/messages/ga-ES.json b/public/intl/messages/ga-ES.json index 690083ae05..7e182d73c7 100644 --- a/public/intl/messages/ga-ES.json +++ b/public/intl/messages/ga-ES.json @@ -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": [ diff --git a/public/intl/messages/he-IL.json b/public/intl/messages/he-IL.json index 740dfffaa5..ee8599f349 100644 --- a/public/intl/messages/he-IL.json +++ b/public/intl/messages/he-IL.json @@ -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": [ diff --git a/public/intl/messages/hi-IN.json b/public/intl/messages/hi-IN.json index 2c178669b5..5fdffe3755 100644 --- a/public/intl/messages/hi-IN.json +++ b/public/intl/messages/hi-IN.json @@ -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": [ diff --git a/public/intl/messages/hr-HR.json b/public/intl/messages/hr-HR.json index 974bdebd71..87f37e73ad 100644 --- a/public/intl/messages/hr-HR.json +++ b/public/intl/messages/hr-HR.json @@ -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": [ diff --git a/public/intl/messages/hu-HU.json b/public/intl/messages/hu-HU.json index 227ef208a0..2cc464e990 100644 --- a/public/intl/messages/hu-HU.json +++ b/public/intl/messages/hu-HU.json @@ -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": [ diff --git a/public/intl/messages/id-ID.json b/public/intl/messages/id-ID.json index dbe78a5018..8123d03b3a 100644 --- a/public/intl/messages/id-ID.json +++ b/public/intl/messages/id-ID.json @@ -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": [ diff --git a/public/intl/messages/it-IT.json b/public/intl/messages/it-IT.json index 21b70650dc..d755b277a6 100644 --- a/public/intl/messages/it-IT.json +++ b/public/intl/messages/it-IT.json @@ -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": [ diff --git a/public/intl/messages/ja-JP.json b/public/intl/messages/ja-JP.json index c11311c455..b2bc66a09a 100644 --- a/public/intl/messages/ja-JP.json +++ b/public/intl/messages/ja-JP.json @@ -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": [ diff --git a/public/intl/messages/km-KH.json b/public/intl/messages/km-KH.json index d8f7d50123..841dabdd33 100644 --- a/public/intl/messages/km-KH.json +++ b/public/intl/messages/km-KH.json @@ -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": [ diff --git a/public/intl/messages/ko-KR.json b/public/intl/messages/ko-KR.json index da196a3c96..b2213cd84a 100644 --- a/public/intl/messages/ko-KR.json +++ b/public/intl/messages/ko-KR.json @@ -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": [ diff --git a/public/intl/messages/lt-LT.json b/public/intl/messages/lt-LT.json index 891aed6ffa..e6c6cd1857 100644 --- a/public/intl/messages/lt-LT.json +++ b/public/intl/messages/lt-LT.json @@ -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": [ diff --git a/public/intl/messages/mn-MN.json b/public/intl/messages/mn-MN.json index b11d88c12e..d9461929e1 100644 --- a/public/intl/messages/mn-MN.json +++ b/public/intl/messages/mn-MN.json @@ -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": [ diff --git a/public/intl/messages/ms-MY.json b/public/intl/messages/ms-MY.json index 996af333db..8f47f3cd7a 100644 --- a/public/intl/messages/ms-MY.json +++ b/public/intl/messages/ms-MY.json @@ -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": [ diff --git a/public/intl/messages/my-MM.json b/public/intl/messages/my-MM.json index 34f83c029f..9451a0febb 100644 --- a/public/intl/messages/my-MM.json +++ b/public/intl/messages/my-MM.json @@ -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": [ diff --git a/public/intl/messages/nb-NO.json b/public/intl/messages/nb-NO.json index eeb648c482..3e73b45ee8 100644 --- a/public/intl/messages/nb-NO.json +++ b/public/intl/messages/nb-NO.json @@ -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": [ diff --git a/public/intl/messages/nl-NL.json b/public/intl/messages/nl-NL.json index 16f2de07f9..a186de8625 100644 --- a/public/intl/messages/nl-NL.json +++ b/public/intl/messages/nl-NL.json @@ -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": [ diff --git a/public/intl/messages/pl-PL.json b/public/intl/messages/pl-PL.json index 29e99d4fe6..608c0b3a80 100644 --- a/public/intl/messages/pl-PL.json +++ b/public/intl/messages/pl-PL.json @@ -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": [ diff --git a/public/intl/messages/pt-BR.json b/public/intl/messages/pt-BR.json index 149af775da..ae5b0de4ba 100644 --- a/public/intl/messages/pt-BR.json +++ b/public/intl/messages/pt-BR.json @@ -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": [ diff --git a/public/intl/messages/pt-PT.json b/public/intl/messages/pt-PT.json index 340ebb8852..02704e9e0f 100644 --- a/public/intl/messages/pt-PT.json +++ b/public/intl/messages/pt-PT.json @@ -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": [ diff --git a/public/intl/messages/ro-RO.json b/public/intl/messages/ro-RO.json index 67d0929bcf..311199cf39 100644 --- a/public/intl/messages/ro-RO.json +++ b/public/intl/messages/ro-RO.json @@ -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": [ diff --git a/public/intl/messages/ru-RU.json b/public/intl/messages/ru-RU.json index 1d0a86a1e7..30974dd64e 100644 --- a/public/intl/messages/ru-RU.json +++ b/public/intl/messages/ru-RU.json @@ -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": [ diff --git a/public/intl/messages/si-LK.json b/public/intl/messages/si-LK.json index d7cfd7bf43..e6b06c6ba7 100644 --- a/public/intl/messages/si-LK.json +++ b/public/intl/messages/si-LK.json @@ -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": [ diff --git a/public/intl/messages/sk-SK.json b/public/intl/messages/sk-SK.json index 2be1f01521..738ead435d 100644 --- a/public/intl/messages/sk-SK.json +++ b/public/intl/messages/sk-SK.json @@ -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": [ diff --git a/public/intl/messages/sl-SI.json b/public/intl/messages/sl-SI.json index 83c383c1aa..67f65ad407 100644 --- a/public/intl/messages/sl-SI.json +++ b/public/intl/messages/sl-SI.json @@ -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": [ diff --git a/public/intl/messages/sv-SE.json b/public/intl/messages/sv-SE.json index a6336a3044..c2b2c5e06f 100644 --- a/public/intl/messages/sv-SE.json +++ b/public/intl/messages/sv-SE.json @@ -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": [ diff --git a/public/intl/messages/ta-IN.json b/public/intl/messages/ta-IN.json index 4afcb8db74..13ab67980f 100644 --- a/public/intl/messages/ta-IN.json +++ b/public/intl/messages/ta-IN.json @@ -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": [ diff --git a/public/intl/messages/th-TH.json b/public/intl/messages/th-TH.json index 32d5e677d2..43158951dc 100644 --- a/public/intl/messages/th-TH.json +++ b/public/intl/messages/th-TH.json @@ -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": [ diff --git a/public/intl/messages/tr-TR.json b/public/intl/messages/tr-TR.json index ac49dd47d5..1edfdbbc4e 100644 --- a/public/intl/messages/tr-TR.json +++ b/public/intl/messages/tr-TR.json @@ -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": [ diff --git a/public/intl/messages/uk-UA.json b/public/intl/messages/uk-UA.json index 6ea471a6b3..33579a3373 100644 --- a/public/intl/messages/uk-UA.json +++ b/public/intl/messages/uk-UA.json @@ -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": [ diff --git a/public/intl/messages/ur-PK.json b/public/intl/messages/ur-PK.json index 18fce7ebb7..9d3c932b6c 100644 --- a/public/intl/messages/ur-PK.json +++ b/public/intl/messages/ur-PK.json @@ -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": [ diff --git a/public/intl/messages/vi-VN.json b/public/intl/messages/vi-VN.json index 50409fab26..424d30a691 100644 --- a/public/intl/messages/vi-VN.json +++ b/public/intl/messages/vi-VN.json @@ -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": [ diff --git a/public/intl/messages/zh-CN.json b/public/intl/messages/zh-CN.json index e66a8eb158..c8852041ab 100644 --- a/public/intl/messages/zh-CN.json +++ b/public/intl/messages/zh-CN.json @@ -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": [ diff --git a/public/intl/messages/zh-TW.json b/public/intl/messages/zh-TW.json index d6514ab4df..37c490d7c4 100644 --- a/public/intl/messages/zh-TW.json +++ b/public/intl/messages/zh-TW.json @@ -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": [ diff --git a/src/components/messages.ts b/src/components/messages.ts index fa515c665f..6609076235 100644 --- a/src/components/messages.ts +++ b/src/components/messages.ts @@ -265,7 +265,7 @@ export const labels = defineMessages({ journey: { id: 'label.journey', defaultMessage: 'Journey' }, journeyDescription: { id: 'label.journey-description', - defaultMessage: 'Understand how users nagivate through your website.', + defaultMessage: 'Understand how users navigate through your website.', }, compare: { id: 'label.compare', defaultMessage: 'Compare' }, current: { id: 'label.current', defaultMessage: 'Current' }, diff --git a/src/lang/am-ET.json b/src/lang/am-ET.json index 323fe4e88b..7c533031ab 100644 --- a/src/lang/am-ET.json +++ b/src/lang/am-ET.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "Laptop", diff --git a/src/lang/ar-SA.json b/src/lang/ar-SA.json index a9fd02cfa4..f764f29e0a 100644 --- a/src/lang/ar-SA.json +++ b/src/lang/ar-SA.json @@ -96,7 +96,7 @@ "label.join": "انضم", "label.join-team": "انضم للفريق", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "اللغة", "label.languages": "اللغات", "label.laptop": "لابتوب", diff --git a/src/lang/be-BY.json b/src/lang/be-BY.json index 3a4a033581..53533023fd 100644 --- a/src/lang/be-BY.json +++ b/src/lang/be-BY.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Мова", "label.languages": "Мовы", "label.laptop": "Ноўтбук", diff --git a/src/lang/bg-BG.json b/src/lang/bg-BG.json index 9b22b466f4..5095b38799 100644 --- a/src/lang/bg-BG.json +++ b/src/lang/bg-BG.json @@ -96,7 +96,7 @@ "label.join": "Присъедини се", "label.join-team": "Присъедини се към екип", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Език", "label.languages": "Езици", "label.laptop": "Лаптоп", diff --git a/src/lang/bn-BD.json b/src/lang/bn-BD.json index 43b41420c8..18eb1d896e 100644 --- a/src/lang/bn-BD.json +++ b/src/lang/bn-BD.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "ভাষা", "label.languages": "ভাষা", "label.laptop": "ল্যাপটপ", diff --git a/src/lang/bs-BA.json b/src/lang/bs-BA.json index 04fb89ee4b..000af0dc20 100644 --- a/src/lang/bs-BA.json +++ b/src/lang/bs-BA.json @@ -96,7 +96,7 @@ "label.join": "Učlani se", "label.join-team": "Učlani se u tim", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Jezik", "label.languages": "Jezici", "label.laptop": "Laptop", diff --git a/src/lang/ca-ES.json b/src/lang/ca-ES.json index 5824492c3e..d70dd2321b 100644 --- a/src/lang/ca-ES.json +++ b/src/lang/ca-ES.json @@ -96,7 +96,7 @@ "label.join": "Unir", "label.join-team": "Unir-se al equip", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Idioma", "label.languages": "Idiomes", "label.laptop": "Portàtil", diff --git a/src/lang/cs-CZ.json b/src/lang/cs-CZ.json index 9c6d3ba197..3a57f6b28c 100644 --- a/src/lang/cs-CZ.json +++ b/src/lang/cs-CZ.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "Přenosný počítač", diff --git a/src/lang/da-DK.json b/src/lang/da-DK.json index a0cbe5e650..715df99e21 100644 --- a/src/lang/da-DK.json +++ b/src/lang/da-DK.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Sprog", "label.languages": "Sprog", "label.laptop": "Laptop", diff --git a/src/lang/de-CH.json b/src/lang/de-CH.json index bdd37cdc8d..6891480fa9 100644 --- a/src/lang/de-CH.json +++ b/src/lang/de-CH.json @@ -96,7 +96,7 @@ "label.join": "Biträte", "label.join-team": "Team biträte", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Sprach", "label.languages": "Sprache", "label.laptop": "Laptop", diff --git a/src/lang/de-DE.json b/src/lang/de-DE.json index aca4b863f9..10b633263f 100644 --- a/src/lang/de-DE.json +++ b/src/lang/de-DE.json @@ -96,7 +96,7 @@ "label.join": "Beitreten", "label.join-team": "Team beitreten", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Sprache", "label.languages": "Sprachen", "label.laptop": "Laptop", diff --git a/src/lang/el-GR.json b/src/lang/el-GR.json index 8155b97af7..ceb0812bdd 100644 --- a/src/lang/el-GR.json +++ b/src/lang/el-GR.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "Λάπτοπ", diff --git a/src/lang/en-GB.json b/src/lang/en-GB.json index aeefe43997..9bb61811b4 100644 --- a/src/lang/en-GB.json +++ b/src/lang/en-GB.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "Laptop", diff --git a/src/lang/en-US.json b/src/lang/en-US.json index 7487e220a2..e895036cc1 100644 --- a/src/lang/en-US.json +++ b/src/lang/en-US.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "Laptop", diff --git a/src/lang/es-ES.json b/src/lang/es-ES.json index bdcb3e0ad0..532e4d20c9 100644 --- a/src/lang/es-ES.json +++ b/src/lang/es-ES.json @@ -5,7 +5,7 @@ "label.add": "Añadir", "label.add-description": "Añadir descripción", "label.add-member": "Añadir miembro", - "label.add-step": "Add step", + "label.add-step": "Añadir paso", "label.add-website": "Nuevo sitio web", "label.admin": "Administrador", "label.after": "Después", @@ -24,12 +24,12 @@ "label.cities": "Ciudades", "label.city": "Ciudad", "label.clear-all": "Limpiar todo", - "label.compare": "Compare", + "label.compare": "Comparar", "label.confirm": "Confirmar", "label.confirm-password": "Confirmar contraseña", "label.contains": "Contiene", "label.continue": "Continuar", - "label.count": "Count", + "label.count": "Contar", "label.countries": "Países", "label.country": "País", "label.create": "Crear", @@ -37,8 +37,8 @@ "label.create-team": "Crear equipo", "label.create-user": "Crear usuario", "label.created": "Creado", - "label.created-by": "Created By", - "label.current": "Current", + "label.created-by": "Creado por", + "label.current": "Actual", "label.current-password": "Contraseña actual", "label.custom-range": "Intervalo personalizado", "label.dashboard": "Panel de control", @@ -63,14 +63,14 @@ "label.dropoff": "Abandono", "label.edit": "Editar", "label.edit-dashboard": "Editar panel", - "label.edit-member": "Edit member", + "label.edit-member": "Editar miembro", "label.enable-share-url": "Habilitar compartir URL", - "label.end-step": "End Step", - "label.entry": "Entry URL", + "label.end-step": "Paso final", + "label.entry": "URL de entrada", "label.event": "Evento", "label.event-data": "Datos de evento", "label.events": "Eventos", - "label.exit": "Exit URL", + "label.exit": "URL de salida", "label.false": "Falso", "label.field": "Campo", "label.fields": "Campos", @@ -78,11 +78,11 @@ "label.filter-combined": "Combinado", "label.filter-raw": "En crudo", "label.filters": "Filtros", - "label.funnel": "Funnel", + "label.funnel": "Embudo", "label.funnel-description": "Comprender conversión y abandono de usuarios.", - "label.goal": "Goal", - "label.goals": "Goals", - "label.goals-description": "Track your goals for pageviews and events.", + "label.goal": "Objetivo", + "label.goals": "Objetivos", + "label.goals-description": "Realice un seguimiento de sus objetivos de páginas vistas y eventos.", "label.greater-than": "Mayor que", "label.greater-than-equals": "Mayor que o igual a", "label.host": "Host", @@ -95,14 +95,14 @@ "label.is-set": "Está establecido", "label.join": "Unir", "label.join-team": "Unirse al equipo", - "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey": "Viaje", + "label.journey-description": "Comprenda cómo los usuarios navegan por su sitio web.", "label.language": "Idioma", "label.languages": "Idiomas", "label.laptop": "Portátil", "label.last-days": "Últimos {x} días", "label.last-hours": "Últimas {x} horas", - "label.last-months": "Last {x} months", + "label.last-months": "Últimos {x} meses", "label.leave": "Abandonar", "label.leave-team": "Abandonar equipo", "label.less-than": "Menor que", @@ -111,10 +111,10 @@ "label.logout": "Cerrar sesión", "label.manage": "Administrar", "label.manager": "Manager", - "label.max": "Máx", + "label.max": "Max", "label.member": "Miembro", "label.members": "Miembros", - "label.min": "Mín", + "label.min": "Min", "label.mobile": "Móvil", "label.more": "Más", "label.my-account": "Mi cuenta", @@ -133,11 +133,11 @@ "label.pages": "Páginas", "label.password": "Contraseña", "label.powered-by": "Analíticas de {name}", - "label.previous": "Previous", - "label.previous-period": "Previous period", - "label.previous-year": "Previous year", + "label.previous": "Anterior", + "label.previous-period": "Periodo anterior", + "label.previous-year": "Año anterior", "label.profile": "Perfil", - "label.property": "Property", + "label.property": "Propiedad", "label.queries": "Consultas", "label.query": "Consulta", "label.query-parameters": "Parámetros de petición", @@ -146,7 +146,7 @@ "label.referrers": "Referido desde", "label.refresh": "Actualizar", "label.regenerate": "Regenerar", - "label.region": "Region", + "label.region": "Región", "label.regions": "Regiones", "label.remove": "Quitar", "label.remove-member": "Eliminar miembro", @@ -161,7 +161,7 @@ "label.save": "Guardar", "label.screens": "Pantallas", "label.search": "Buscar", - "label.select": "Select", + "label.select": "Seleccionar", "label.select-date": "Seleccionar fecha", "label.select-role": "Seleccionar rol", "label.select-website": "Seleccionar sitio web", @@ -169,13 +169,13 @@ "label.settings": "Ajustes", "label.share-url": "Compartir URL", "label.single-day": "Un solo día", - "label.start-step": "Start Step", - "label.steps": "Steps", + "label.start-step": "Paso inical", + "label.steps": "Pasos", "label.sum": "Suma", "label.tablet": "Tableta", "label.team": "Equipo", "label.team-id": "ID del equipo", - "label.team-manager": "Team manager", + "label.team-manager": "Jefe de equipo", "label.team-member": "Miembro del equipo", "label.team-name": "Nombre del equipo", "label.team-owner": "Admin. del equipo", @@ -201,23 +201,23 @@ "label.unique-visitors": "Visitantes únicos", "label.unknown": "Desconocida", "label.untitled": "Sin título", - "label.update": "Update", + "label.update": "Actualizar", "label.url": "URL", "label.urls": "URLs", "label.user": "Usuario", "label.username": "Nombre de usuario", "label.users": "Usuarios", "label.utm": "UTM", - "label.utm-description": "Track your campaigns through UTM parameters.", + "label.utm-description": "Realice un seguimiento de sus campañas a través de parámetros UTM.", "label.value": "Valor", "label.view": "Visualizar", "label.view-details": "Ver detalles", "label.view-only": "Ver sólo", "label.views": "Vistas", - "label.views-per-visit": "Views per visit", + "label.views-per-visit": "Vistas por visita", "label.visit-duration": "Tiempo promedio de visita", "label.visitors": "Visitantes", - "label.visits": "Visits", + "label.visits": "Visitas", "label.website": "Sitio web", "label.website-id": "ID del sitio web", "label.websites": "Sitios web", @@ -225,7 +225,7 @@ "label.yesterday": "Ayer", "message.action-confirmation": "Escriba {confirmation} en el cuadro a continuación para confirmar.", "message.active-users": "{x} {x, plural, one {activo} other {activos}}", - "message.collected-data": "Collected data", + "message.collected-data": "Datos obtenidos", "message.confirm-delete": "¿Seguro que quieres eliminar {target}?", "message.confirm-leave": "¿Seguro que quieres abandonar {target}?", "message.confirm-remove": "¿Estás seguro de que desea eliminar {target}?", @@ -257,7 +257,7 @@ "message.team-websites-info": "Las analíticas de tus sitios web pueden ser vistas por cualquier miembro del equipo.", "message.tracking-code": "Código de rastreo", "message.transfer-team-website-to-user": "¿Transferir este sitio web a su cuenta?", - "message.transfer-user-website-to-team": "Select the team to transfer this website to.", + "message.transfer-user-website-to-team": "Seleccione el equipo al que transferir este sitio web.", "message.transfer-website": "Seleccione el equipo al que transferir este sitio web.", "message.triggered-event": "Evento lanzado", "message.user-deleted": "Usuario eliminado.", diff --git a/src/lang/fa-IR.json b/src/lang/fa-IR.json index 8eb2df241d..fe1705e30a 100644 --- a/src/lang/fa-IR.json +++ b/src/lang/fa-IR.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "زبان", "label.languages": "زبان‌ها", "label.laptop": "لپ‌تاپ", diff --git a/src/lang/fi-FI.json b/src/lang/fi-FI.json index f6d18b35e5..02561dcb23 100644 --- a/src/lang/fi-FI.json +++ b/src/lang/fi-FI.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Kieli", "label.languages": "Kielet", "label.laptop": "Kannettava tietokone", diff --git a/src/lang/fo-FO.json b/src/lang/fo-FO.json index 98646d2832..e9ca2aae91 100644 --- a/src/lang/fo-FO.json +++ b/src/lang/fo-FO.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "Fartelda", diff --git a/src/lang/fr-FR.json b/src/lang/fr-FR.json index 23c6652588..edb2abd36b 100644 --- a/src/lang/fr-FR.json +++ b/src/lang/fr-FR.json @@ -96,7 +96,7 @@ "label.join": "Rejoindre", "label.join-team": "Rejoindre une équipe", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Langue", "label.languages": "Langues", "label.laptop": "Portable", diff --git a/src/lang/ga-ES.json b/src/lang/ga-ES.json index 9e04806129..d267137f1a 100644 --- a/src/lang/ga-ES.json +++ b/src/lang/ga-ES.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Idioma", "label.languages": "Idiomas", "label.laptop": "Portátil", diff --git a/src/lang/he-IL.json b/src/lang/he-IL.json index 584d5f5d43..d7ebf875b6 100644 --- a/src/lang/he-IL.json +++ b/src/lang/he-IL.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "לפטופ", diff --git a/src/lang/hi-IN.json b/src/lang/hi-IN.json index 0cf024e332..1fbee2c02e 100644 --- a/src/lang/hi-IN.json +++ b/src/lang/hi-IN.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "लैपटॉप", diff --git a/src/lang/hr-HR.json b/src/lang/hr-HR.json index bc69742664..39d4d9d1dd 100644 --- a/src/lang/hr-HR.json +++ b/src/lang/hr-HR.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Jezik", "label.languages": "Languages", "label.laptop": "Laptop", diff --git a/src/lang/hu-HU.json b/src/lang/hu-HU.json index 111ce00fa1..6762181bd7 100644 --- a/src/lang/hu-HU.json +++ b/src/lang/hu-HU.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "Laptop", diff --git a/src/lang/id-ID.json b/src/lang/id-ID.json index 0f2c705aa0..930b02da5c 100644 --- a/src/lang/id-ID.json +++ b/src/lang/id-ID.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Bahasa", "label.languages": "Bahasa", "label.laptop": "Laptop", diff --git a/src/lang/it-IT.json b/src/lang/it-IT.json index 3e73781894..229254d56a 100644 --- a/src/lang/it-IT.json +++ b/src/lang/it-IT.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Lingua", "label.languages": "Lingue", "label.laptop": "Portatile", diff --git a/src/lang/ja-JP.json b/src/lang/ja-JP.json index ef0cce1dd6..02e300bbb8 100644 --- a/src/lang/ja-JP.json +++ b/src/lang/ja-JP.json @@ -96,7 +96,7 @@ "label.join": "参加", "label.join-team": "チームに参加", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "言語", "label.languages": "言語", "label.laptop": "ノートPC", diff --git a/src/lang/km-KH.json b/src/lang/km-KH.json index 9a374a0673..f3189ac2bd 100644 --- a/src/lang/km-KH.json +++ b/src/lang/km-KH.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "ភាសា", "label.languages": "ភាសា", "label.laptop": "កុំព្យូទ័រយួរដៃ", diff --git a/src/lang/lt-LT.json b/src/lang/lt-LT.json index f96e643e64..424c06ce3e 100644 --- a/src/lang/lt-LT.json +++ b/src/lang/lt-LT.json @@ -96,7 +96,7 @@ "label.join": "Prisijungti", "label.join-team": "Prisijungti į komandą", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Kalba", "label.languages": "Kalbos", "label.laptop": "Laptop", diff --git a/src/lang/mn-MN.json b/src/lang/mn-MN.json index 47e08bca3f..0e99817063 100644 --- a/src/lang/mn-MN.json +++ b/src/lang/mn-MN.json @@ -96,7 +96,7 @@ "label.join": "Нэгдэх", "label.join-team": "Багт нэгдэх", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Хэл", "label.languages": "Хэл", "label.laptop": "Зөөврийн компьютер", diff --git a/src/lang/ms-MY.json b/src/lang/ms-MY.json index bf28b05cfe..82cfbb6b42 100644 --- a/src/lang/ms-MY.json +++ b/src/lang/ms-MY.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "Laptop", diff --git a/src/lang/my-MM.json b/src/lang/my-MM.json index dffbe43e48..8a71cd4974 100644 --- a/src/lang/my-MM.json +++ b/src/lang/my-MM.json @@ -96,7 +96,7 @@ "label.join": "ဝင်မည်", "label.join-team": "အသင်းဝင်မည်", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "ဘာသာစကား", "label.languages": "ဘာသာစကားများ", "label.laptop": "လက်တော့ပ်", diff --git a/src/lang/nb-NO.json b/src/lang/nb-NO.json index ca360d0950..4a6e0409c1 100644 --- a/src/lang/nb-NO.json +++ b/src/lang/nb-NO.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Språk", "label.languages": "Språk", "label.laptop": "Bærbar", diff --git a/src/lang/nl-NL.json b/src/lang/nl-NL.json index 6208147309..9a1c852bc1 100644 --- a/src/lang/nl-NL.json +++ b/src/lang/nl-NL.json @@ -96,7 +96,7 @@ "label.join": "Lid worden", "label.join-team": "Word lid van een team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Taal", "label.languages": "Talen", "label.laptop": "Laptop", diff --git a/src/lang/pl-PL.json b/src/lang/pl-PL.json index 8068d9b360..e663d44598 100644 --- a/src/lang/pl-PL.json +++ b/src/lang/pl-PL.json @@ -96,7 +96,7 @@ "label.join": "Dołącz", "label.join-team": "Dołącz do zespołu", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Język", "label.languages": "Języki", "label.laptop": "Laptop", diff --git a/src/lang/pt-BR.json b/src/lang/pt-BR.json index c1f9c708ff..be96d09772 100644 --- a/src/lang/pt-BR.json +++ b/src/lang/pt-BR.json @@ -96,7 +96,7 @@ "label.join": "Participar", "label.join-team": "Participar da equipe", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Idioma", "label.languages": "Idiomas", "label.laptop": "Notebook", diff --git a/src/lang/pt-PT.json b/src/lang/pt-PT.json index d497bb3a8f..252457f833 100644 --- a/src/lang/pt-PT.json +++ b/src/lang/pt-PT.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Língua", "label.languages": "Línguas", "label.laptop": "Portátil", diff --git a/src/lang/ro-RO.json b/src/lang/ro-RO.json index 107303ae94..c06690c23d 100644 --- a/src/lang/ro-RO.json +++ b/src/lang/ro-RO.json @@ -96,7 +96,7 @@ "label.join": "Alătură-te", "label.join-team": "Alătură-te echipei", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Limbă", "label.languages": "Limbi", "label.laptop": "Laptop", diff --git a/src/lang/ru-RU.json b/src/lang/ru-RU.json index 535d22921d..257c0491aa 100644 --- a/src/lang/ru-RU.json +++ b/src/lang/ru-RU.json @@ -96,7 +96,7 @@ "label.join": "Присоединиться", "label.join-team": "Присоединиться к команде", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Язык", "label.languages": "Языки", "label.laptop": "Ноутбук", diff --git a/src/lang/si-LK.json b/src/lang/si-LK.json index 03b42df782..6263f91a96 100644 --- a/src/lang/si-LK.json +++ b/src/lang/si-LK.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "භාෂාව", "label.languages": "Languages", "label.laptop": "Laptop", diff --git a/src/lang/sk-SK.json b/src/lang/sk-SK.json index 3d5b8fbb13..4447ada298 100644 --- a/src/lang/sk-SK.json +++ b/src/lang/sk-SK.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "Prenosný počítač", diff --git a/src/lang/sl-SI.json b/src/lang/sl-SI.json index 01fc7f30b4..3c684dec03 100644 --- a/src/lang/sl-SI.json +++ b/src/lang/sl-SI.json @@ -96,7 +96,7 @@ "label.join": "Pridruži se", "label.join-team": "Pridruži se ekipi", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Jezik", "label.languages": "Jeziki", "label.laptop": "Prenosni računalnik", diff --git a/src/lang/sv-SE.json b/src/lang/sv-SE.json index 92c9df03c6..c60b6cf904 100644 --- a/src/lang/sv-SE.json +++ b/src/lang/sv-SE.json @@ -96,7 +96,7 @@ "label.join": "Gå med", "label.join-team": "Gå med i team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Språk", "label.languages": "Språk", "label.laptop": "Bärbar", diff --git a/src/lang/ta-IN.json b/src/lang/ta-IN.json index 99558b11d8..0f95bc1861 100644 --- a/src/lang/ta-IN.json +++ b/src/lang/ta-IN.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Languages", "label.laptop": "மடிக்கணினி", diff --git a/src/lang/th-TH.json b/src/lang/th-TH.json index 253f420c82..a594bb3120 100644 --- a/src/lang/th-TH.json +++ b/src/lang/th-TH.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "ภาษา", "label.languages": "ภาษา", "label.laptop": "แล็ปท็อป", diff --git a/src/lang/tr-TR.json b/src/lang/tr-TR.json index 49e71b9a31..b8271ed219 100644 --- a/src/lang/tr-TR.json +++ b/src/lang/tr-TR.json @@ -96,7 +96,7 @@ "label.join": "Katıl", "label.join-team": "Takıma katıl", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Dil", "label.languages": "Diller", "label.laptop": "Dizüstü", diff --git a/src/lang/uk-UA.json b/src/lang/uk-UA.json index ec658b7ef6..ecfd27f50a 100644 --- a/src/lang/uk-UA.json +++ b/src/lang/uk-UA.json @@ -93,7 +93,7 @@ "label.join": "Приєднатись", "label.join-team": "Приєднатись до команди", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Мова", "label.languages": "Мови", "label.laptop": "Ноутбук", diff --git a/src/lang/ur-PK.json b/src/lang/ur-PK.json index 7d649148ab..97de5d6940 100644 --- a/src/lang/ur-PK.json +++ b/src/lang/ur-PK.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "زبانیں", "label.laptop": "لیپ ٹاپ", diff --git a/src/lang/vi-VN.json b/src/lang/vi-VN.json index 1be8684bdb..054f449a8a 100644 --- a/src/lang/vi-VN.json +++ b/src/lang/vi-VN.json @@ -96,7 +96,7 @@ "label.join": "Join", "label.join-team": "Join team", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "Language", "label.languages": "Ngôn ngữ", "label.laptop": "Laptop", diff --git a/src/lang/zh-CN.json b/src/lang/zh-CN.json index bcdd3612b6..91df00a615 100644 --- a/src/lang/zh-CN.json +++ b/src/lang/zh-CN.json @@ -96,7 +96,7 @@ "label.join": "加入", "label.join-team": "加入团队", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "语言", "label.languages": "语言", "label.laptop": "笔记本", diff --git a/src/lang/zh-TW.json b/src/lang/zh-TW.json index 9d8329e9af..10069181e3 100644 --- a/src/lang/zh-TW.json +++ b/src/lang/zh-TW.json @@ -96,7 +96,7 @@ "label.join": "加入", "label.join-team": "加入團隊", "label.journey": "Journey", - "label.journey-description": "Understand how users nagivate through your website.", + "label.journey-description": "Understand how users navigate through your website.", "label.language": "語言", "label.languages": "語言", "label.laptop": "筆記型電腦", From 038ecdb59222f9f332dfb3d76c2e56e025b78838 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Tue, 23 Jul 2024 15:34:25 -0700 Subject: [PATCH 041/132] fix pkey for stats tables --- db/clickhouse/schema.sql | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/db/clickhouse/schema.sql b/db/clickhouse/schema.sql index e36ddf6121..9082884b2d 100644 --- a/db/clickhouse/schema.sql +++ b/db/clickhouse/schema.sql @@ -100,7 +100,8 @@ ORDER BY ( website_id, toStartOfHour(created_at), cityHash64(visit_id), - visit_id + visit_id, + event_type ) SAMPLE BY cityHash64(visit_id) TTL created_at + INTERVAL 10 DAY; @@ -200,7 +201,8 @@ ORDER BY ( website_id, toStartOfDay(created_at), cityHash64(visit_id), - visit_id + visit_id, + event_type ) SAMPLE BY cityHash64(visit_id); @@ -265,4 +267,4 @@ GROUP BY website_id, subdivision1, city, event_type, - timestamp); \ No newline at end of file + timestamp); \ No newline at end of file From 174b9e4376379ea1b185371bf68dd6e235342c98 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Tue, 23 Jul 2024 22:35:11 -0700 Subject: [PATCH 042/132] only use hourly table, remove daily table logic, fix updatechart undefined --- db/clickhouse/schema.sql | 111 ++---------------- src/components/charts/Chart.tsx | 24 ++-- src/pages/api/websites/[websiteId]/metrics.ts | 13 +- src/pages/api/websites/[websiteId]/stats.ts | 6 +- .../analytics/events/getEventMetrics.ts | 4 +- src/queries/analytics/getWebsiteStats.ts | 7 +- .../analytics/pageviews/getPageviewMetrics.ts | 16 +-- .../analytics/pageviews/getPageviewStats.ts | 3 +- .../analytics/sessions/getSessionMetrics.ts | 19 +-- .../analytics/sessions/getSessionStats.ts | 3 +- 10 files changed, 39 insertions(+), 167 deletions(-) diff --git a/db/clickhouse/schema.sql b/db/clickhouse/schema.sql index 9082884b2d..29cdeabc21 100644 --- a/db/clickhouse/schema.sql +++ b/db/clickhouse/schema.sql @@ -78,6 +78,8 @@ CREATE TABLE umami.website_event_stats_hourly browser LowCardinality(String), os LowCardinality(String), device LowCardinality(String), + screen LowCardinality(String), + language LowCardinality(String), country LowCardinality(String), subdivision1 LowCardinality(String), city String, @@ -103,8 +105,7 @@ ORDER BY ( visit_id, event_type ) -SAMPLE BY cityHash64(visit_id) -TTL created_at + INTERVAL 10 DAY; +SAMPLE BY cityHash64(visit_id); CREATE MATERIALIZED VIEW umami.website_event_stats_hourly_mv TO umami.website_event_stats_hourly @@ -117,6 +118,8 @@ SELECT browser, os, device, + screen, + language, country, subdivision1, city, @@ -140,6 +143,8 @@ FROM (SELECT browser, os, device, + screen, + language, country, subdivision1, city, @@ -163,106 +168,8 @@ GROUP BY website_id, browser, os, device, - country, - subdivision1, - city, - event_type, - timestamp); - --- stats daily -CREATE TABLE umami.website_event_stats_daily -( - website_id UUID, - session_id UUID, - visit_id UUID, - hostname LowCardinality(String), - browser LowCardinality(String), - os LowCardinality(String), - device 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, - toStartOfDay(created_at), - cityHash64(visit_id), - visit_id, - event_type -) -SAMPLE BY cityHash64(visit_id); - -CREATE MATERIALIZED VIEW umami.website_event_stats_daily_mv -TO umami.website_event_stats_daily -AS -SELECT - website_id, - session_id, - visit_id, - hostname, - browser, - os, - device, - 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, - 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, - toStartOfDay(created_at) timestamp -FROM umami.website_event -GROUP BY website_id, - session_id, - visit_id, - hostname, - browser, - os, - device, + screen, + language, country, subdivision1, city, diff --git a/src/components/charts/Chart.tsx b/src/components/charts/Chart.tsx index 926defaf9e..a4badbcef0 100644 --- a/src/components/charts/Chart.tsx +++ b/src/components/charts/Chart.tsx @@ -79,18 +79,20 @@ export function Chart({ }; const updateChart = (data: any) => { - if ((data?.datasets?.length || 0) === 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 (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; + } } - } - }); - } else { - chart.current.data.datasets = data.datasets; + }); + } else { + chart.current.data.datasets = data.datasets; + } } chart.current.options = options; diff --git a/src/pages/api/websites/[websiteId]/metrics.ts b/src/pages/api/websites/[websiteId]/metrics.ts index b37c38f743..3dac217b21 100644 --- a/src/pages/api/websites/[websiteId]/metrics.ts +++ b/src/pages/api/websites/[websiteId]/metrics.ts @@ -64,7 +64,7 @@ export default async ( await useAuth(req, res); await useValidate(schema, req, res); - const { websiteId, type, limit, offset, search, unit } = req.query; + const { websiteId, type, limit, offset, search } = req.query; if (req.method === 'GET') { if (!(await canViewWebsite(req.auth, websiteId))) { @@ -89,7 +89,7 @@ export default async ( } if (SESSION_COLUMNS.includes(type)) { - const data = await getSessionMetrics(websiteId, type, filters, limit, offset, unit as string); + const data = await getSessionMetrics(websiteId, type, filters, limit, offset); if (type === 'language') { const combined = {}; @@ -111,14 +111,7 @@ export default async ( } if (EVENT_COLUMNS.includes(type)) { - const data = await getPageviewMetrics( - websiteId, - type, - filters, - limit, - offset, - unit as string, - ); + const data = await getPageviewMetrics(websiteId, type, filters, limit, offset); return ok(res, data); } diff --git a/src/pages/api/websites/[websiteId]/stats.ts b/src/pages/api/websites/[websiteId]/stats.ts index 1c684dbed3..9ca84c7440 100644 --- a/src/pages/api/websites/[websiteId]/stats.ts +++ b/src/pages/api/websites/[websiteId]/stats.ts @@ -56,7 +56,7 @@ export default async ( await useAuth(req, res); await useValidate(schema, req, res); - const { websiteId, compare, unit } = req.query; + const { websiteId, compare } = req.query; if (req.method === 'GET') { if (!(await canViewWebsite(req.auth, websiteId))) { @@ -72,13 +72,13 @@ export default async ( const filters = getRequestFilters(req); - const metrics = await getWebsiteStats(websiteId, unit as string, { + const metrics = await getWebsiteStats(websiteId, { ...filters, startDate, endDate, }); - const prevPeriod = await getWebsiteStats(websiteId, unit as string, { + const prevPeriod = await getWebsiteStats(websiteId, { ...filters, startDate: compareStartDate, endDate: compareEndDate, diff --git a/src/queries/analytics/events/getEventMetrics.ts b/src/queries/analytics/events/getEventMetrics.ts index c97c11ad27..3623213584 100644 --- a/src/queries/analytics/events/getEventMetrics.ts +++ b/src/queries/analytics/events/getEventMetrics.ts @@ -51,8 +51,6 @@ async function clickhouseQuery( eventType: EVENT_TYPE.customEvent, }); - const table = unit === 'hour' ? 'website_event_stats_hourly' : 'website_event_stats_daily'; - return rawQuery( ` select @@ -62,7 +60,7 @@ async function clickhouseQuery( from ( select arrayJoin(event_name) as event_name, created_at - from ${table} website_event + from website_event_stats_hourly website_event where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type = {eventType:UInt32} diff --git a/src/queries/analytics/getWebsiteStats.ts b/src/queries/analytics/getWebsiteStats.ts index 125715ca6c..ebe711a03f 100644 --- a/src/queries/analytics/getWebsiteStats.ts +++ b/src/queries/analytics/getWebsiteStats.ts @@ -6,7 +6,7 @@ import prisma from 'lib/prisma'; import { QueryFilters } from 'lib/types'; export async function getWebsiteStats( - ...args: [websiteId: string, unit: string, filters: QueryFilters] + ...args: [websiteId: string, filters: QueryFilters] ): Promise< { pageviews: number; visitors: number; visits: number; bounces: number; totaltime: number }[] > { @@ -18,7 +18,6 @@ export async function getWebsiteStats( async function relationalQuery( websiteId: string, - unit: string, filters: QueryFilters, ): Promise< { pageviews: number; visitors: number; visits: number; bounces: number; totaltime: number }[] @@ -59,7 +58,6 @@ async function relationalQuery( async function clickhouseQuery( websiteId: string, - unit: string, filters: QueryFilters, ): Promise< { pageviews: number; visitors: number; visits: number; bounces: number; totaltime: number }[] @@ -69,7 +67,6 @@ async function clickhouseQuery( ...filters, eventType: EVENT_TYPE.pageView, }); - const table = unit === 'hour' ? 'website_event_stats_hourly' : 'website_event_stats_daily'; return rawQuery( ` @@ -79,7 +76,7 @@ async function clickhouseQuery( uniq(visit_id) as "visits", sumIf(1, views = 1) as "bounces", sum(max_time-min_time) as "totaltime" - from ${table} "website_event" + from website_event_stats_hourly "website_event" where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type = {eventType:UInt32} diff --git a/src/queries/analytics/pageviews/getPageviewMetrics.ts b/src/queries/analytics/pageviews/getPageviewMetrics.ts index d66ec6aa7a..ccfe4ef032 100644 --- a/src/queries/analytics/pageviews/getPageviewMetrics.ts +++ b/src/queries/analytics/pageviews/getPageviewMetrics.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-unused-vars, @typescript-eslint/no-unused-vars */ import clickhouse from 'lib/clickhouse'; import { EVENT_TYPE, FILTER_COLUMNS, SESSION_COLUMNS } from 'lib/constants'; import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; @@ -6,14 +5,7 @@ import prisma from 'lib/prisma'; import { QueryFilters } from 'lib/types'; export async function getPageviewMetrics( - ...args: [ - websiteId: string, - type: string, - filters: QueryFilters, - limit?: number, - offset?: number, - unit?: string, - ] + ...args: [websiteId: string, type: string, filters: QueryFilters, limit?: number, offset?: number] ) { return runQuery({ [PRISMA]: () => relationalQuery(...args), @@ -27,7 +19,6 @@ async function relationalQuery( filters: QueryFilters, limit: number = 500, offset: number = 0, - unit: string, ) { const column = FILTER_COLUMNS[type] || type; const { rawQuery, parseFilters } = prisma; @@ -91,7 +82,6 @@ async function clickhouseQuery( filters: QueryFilters, limit: number = 500, offset: number = 0, - unit: string, ): Promise<{ x: string; y: number }[]> { const column = FILTER_COLUMNS[type] || type; const { rawQuery, parseFilters } = clickhouse; @@ -121,15 +111,13 @@ async function clickhouseQuery( groupByQuery = 'group by x'; } - const table = unit === 'hour' ? 'website_event_stats_hourly' : 'website_event_stats_daily'; - return rawQuery( ` select g.t as x, count(*) as y from ( select ${columnQuery} as t - from ${table} website_event + from website_event_stats_hourly website_event where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type = {eventType:UInt32} diff --git a/src/queries/analytics/pageviews/getPageviewStats.ts b/src/queries/analytics/pageviews/getPageviewStats.ts index 0bb16ca9be..8d757de31d 100644 --- a/src/queries/analytics/pageviews/getPageviewStats.ts +++ b/src/queries/analytics/pageviews/getPageviewStats.ts @@ -46,7 +46,6 @@ async function clickhouseQuery( ...filters, eventType: EVENT_TYPE.pageView, }); - const table = unit === 'hour' ? 'website_event_stats_hourly' : 'website_event_stats_daily'; return rawQuery( ` @@ -57,7 +56,7 @@ async function clickhouseQuery( select ${getDateSQL('created_at', unit, timezone)} as t, sum(views) as y - from ${table} website_event + from website_event_stats_hourly website_event where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type = {eventType:UInt32} diff --git a/src/queries/analytics/sessions/getSessionMetrics.ts b/src/queries/analytics/sessions/getSessionMetrics.ts index 93e36a55fa..9baf2a5c07 100644 --- a/src/queries/analytics/sessions/getSessionMetrics.ts +++ b/src/queries/analytics/sessions/getSessionMetrics.ts @@ -1,19 +1,11 @@ -/* eslint-disable no-unused-vars, @typescript-eslint/no-unused-vars */ -import prisma from 'lib/prisma'; import clickhouse from 'lib/clickhouse'; -import { runQuery, CLICKHOUSE, PRISMA } from 'lib/db'; import { EVENT_TYPE, FILTER_COLUMNS, SESSION_COLUMNS } from 'lib/constants'; +import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; +import prisma from 'lib/prisma'; import { QueryFilters } from 'lib/types'; export async function getSessionMetrics( - ...args: [ - websiteId: string, - type: string, - filters: QueryFilters, - limit?: number, - offset?: number, - unit?: string, - ] + ...args: [websiteId: string, type: string, filters: QueryFilters, limit?: number, offset?: number] ) { return runQuery({ [PRISMA]: () => relationalQuery(...args), @@ -27,7 +19,6 @@ async function relationalQuery( filters: QueryFilters, limit: number = 500, offset: number = 0, - unit: string, ) { const column = FILTER_COLUMNS[type] || type; const { parseFilters, rawQuery } = prisma; @@ -71,7 +62,6 @@ async function clickhouseQuery( filters: QueryFilters, limit: number = 500, offset: number = 0, - unit: string, ): Promise<{ x: string; y: number }[]> { const column = FILTER_COLUMNS[type] || type; const { parseFilters, rawQuery } = clickhouse; @@ -80,7 +70,6 @@ async function clickhouseQuery( eventType: EVENT_TYPE.pageView, }); const includeCountry = column === 'city' || column === 'subdivision1'; - const table = unit === 'hour' ? 'website_event_stats_hourly' : 'website_event_stats_daily'; return rawQuery( ` @@ -88,7 +77,7 @@ async function clickhouseQuery( ${column} x, uniq(session_id) y ${includeCountry ? ', country' : ''} - from ${table} website_event + from website_event_stats_hourly website_event where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type = {eventType:UInt32} diff --git a/src/queries/analytics/sessions/getSessionStats.ts b/src/queries/analytics/sessions/getSessionStats.ts index 2b57b922b4..0fe0b8a55c 100644 --- a/src/queries/analytics/sessions/getSessionStats.ts +++ b/src/queries/analytics/sessions/getSessionStats.ts @@ -46,7 +46,6 @@ async function clickhouseQuery( ...filters, eventType: EVENT_TYPE.pageView, }); - const table = unit === 'hour' ? 'website_event_stats_hourly' : 'website_event_stats_daily'; return rawQuery( ` @@ -57,7 +56,7 @@ async function clickhouseQuery( select ${getDateSQL('created_at', unit, timezone)} as t, uniq(session_id) as y - from ${table} website_event + from website_event_stats_hourly website_event where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type = {eventType:UInt32} From 3205b4f0f417e025a0d82ccd28156b9291592328 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Tue, 23 Jul 2024 22:44:10 -0700 Subject: [PATCH 043/132] fix realtime queries for stats --- src/queries/analytics/pageviews/getPageviewStats.ts | 7 +++++-- src/queries/analytics/sessions/getSessionStats.ts | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/queries/analytics/pageviews/getPageviewStats.ts b/src/queries/analytics/pageviews/getPageviewStats.ts index 8d757de31d..f6ea1e08fc 100644 --- a/src/queries/analytics/pageviews/getPageviewStats.ts +++ b/src/queries/analytics/pageviews/getPageviewStats.ts @@ -47,6 +47,9 @@ async function clickhouseQuery( eventType: EVENT_TYPE.pageView, }); + const table = unit === 'minute' ? 'website_event' : 'website_event_stats_hourly'; + const columnQuery = unit === 'minute' ? 'count(*)' : 'sum(views)'; + return rawQuery( ` select @@ -55,8 +58,8 @@ async function clickhouseQuery( from ( select ${getDateSQL('created_at', unit, timezone)} as t, - sum(views) as y - from website_event_stats_hourly website_event + ${columnQuery} as y + from ${table} website_event where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type = {eventType:UInt32} diff --git a/src/queries/analytics/sessions/getSessionStats.ts b/src/queries/analytics/sessions/getSessionStats.ts index 0fe0b8a55c..7bba14df1b 100644 --- a/src/queries/analytics/sessions/getSessionStats.ts +++ b/src/queries/analytics/sessions/getSessionStats.ts @@ -47,6 +47,9 @@ async function clickhouseQuery( eventType: EVENT_TYPE.pageView, }); + const table = unit === 'minute' ? 'website_event' : 'website_event_stats_hourly'; + const columnQuery = unit === 'minute' ? 'count(distinct session_id)' : 'uniq(session_id)'; + return rawQuery( ` select @@ -55,8 +58,8 @@ async function clickhouseQuery( from ( select ${getDateSQL('created_at', unit, timezone)} as t, - uniq(session_id) as y - from website_event_stats_hourly website_event + ${columnQuery} as y + from ${table} website_event where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type = {eventType:UInt32} From 161da582ba681fb77517593b851783a0a28ff03f Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Wed, 24 Jul 2024 16:57:23 -0700 Subject: [PATCH 044/132] reorder CH stats index --- db/clickhouse/schema.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/db/clickhouse/schema.sql b/db/clickhouse/schema.sql index 29cdeabc21..6179bfb04c 100644 --- a/db/clickhouse/schema.sql +++ b/db/clickhouse/schema.sql @@ -100,10 +100,10 @@ ENGINE = AggregatingMergeTree PARTITION BY toYYYYMM(created_at) ORDER BY ( website_id, + event_type, toStartOfHour(created_at), cityHash64(visit_id), - visit_id, - event_type + visit_id ) SAMPLE BY cityHash64(visit_id); From 2742f1708d6a42626c6e295a3c7c4ff8c15e68c8 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Wed, 24 Jul 2024 17:55:37 -0700 Subject: [PATCH 045/132] remove sessions link --- src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx index bbd6460aa0..365783360a 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx @@ -45,11 +45,11 @@ export function WebsiteHeader({ icon: , path: '/reports', }, - { - label: formatMessage(labels.sessions), - icon: , - path: '/sessions', - }, + // { + // label: formatMessage(labels.sessions), + // icon: , + // path: '/sessions', + // }, { label: formatMessage(labels.events), icon: , From cd0f185f7769db68fb3783fee610f57937cd12ce Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Thu, 25 Jul 2024 12:24:10 -0700 Subject: [PATCH 046/132] remove revenue report --- src/app/(main)/reports/create/ReportTemplates.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/app/(main)/reports/create/ReportTemplates.tsx b/src/app/(main)/reports/create/ReportTemplates.tsx index 2314f7d6a6..7a754d5f29 100644 --- a/src/app/(main)/reports/create/ReportTemplates.tsx +++ b/src/app/(main)/reports/create/ReportTemplates.tsx @@ -51,12 +51,12 @@ export function ReportTemplates({ showHeader = true }: { showHeader?: boolean }) url: renderTeamUrl('/reports/journey'), icon: , }, - { - title: formatMessage(labels.revenue), - description: formatMessage(labels.revenueDescription), - url: renderTeamUrl('/reports/revenue'), - icon: , - }, + // { + // title: formatMessage(labels.revenue), + // description: formatMessage(labels.revenueDescription), + // url: renderTeamUrl('/reports/revenue'), + // icon: , + // }, ]; return ( From ac60d08ee5e71e1347d4261bbdaf005c7d7db8ea Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Sun, 28 Jul 2024 19:51:14 -0700 Subject: [PATCH 047/132] Sessions page. --- next.config.js | 2 +- package.json | 2 + .../websites/[websiteId]/WebsiteHeader.tsx | 12 +- .../[websiteId]/sessions/SessionsTable.tsx | 13 +- .../[sessionId]/SessionDetailsPage.module.css | 3 + .../[sessionId]/SessionDetailsPage.tsx | 27 +++ .../sessions/[sessionId]/SessionInfo.tsx | 17 ++ .../[websiteId]/sessions/[sessionId]/page.tsx | 10 + src/components/common/Profile.tsx | 41 ++++ src/components/hooks/index.ts | 1 + src/components/hooks/queries/useSession.ts | 14 ++ src/components/hooks/queries/useSessions.ts | 2 +- .../[websiteId]/sessions/[sessionId].ts | 42 ++++ .../{sessions.ts => sessions/index.ts} | 0 src/queries/analytics/sessions/getSession.ts | 41 +++- src/queries/analytics/sessions/getSessions.ts | 8 +- yarn.lock | 195 +++++++++++++++++- 17 files changed, 414 insertions(+), 16 deletions(-) create mode 100644 src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.module.css create mode 100644 src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx create mode 100644 src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.tsx create mode 100644 src/app/(main)/websites/[websiteId]/sessions/[sessionId]/page.tsx create mode 100644 src/components/common/Profile.tsx create mode 100644 src/components/hooks/queries/useSession.ts create mode 100644 src/pages/api/websites/[websiteId]/sessions/[sessionId].ts rename src/pages/api/websites/[websiteId]/{sessions.ts => sessions/index.ts} (100%) diff --git a/next.config.js b/next.config.js index acb83a9217..b610eb2c83 100644 --- a/next.config.js +++ b/next.config.js @@ -17,7 +17,7 @@ const trackerScriptName = process.env.TRACKER_SCRIPT_NAME; const contentSecurityPolicy = [ `default-src 'self'`, - `img-src *`, + `img-src * data:`, `script-src 'self' 'unsafe-eval' 'unsafe-inline'`, `style-src 'self' 'unsafe-inline'`, `connect-src 'self' api.umami.is cloud.umami.is`, diff --git a/package.json b/package.json index f14e552d50..f9513c43c9 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,8 @@ ], "dependencies": { "@clickhouse/client": "^1.3.0", + "@dicebear/collection": "^9.2.1", + "@dicebear/core": "^9.2.1", "@fontsource/inter": "^4.5.15", "@prisma/client": "5.16.2", "@prisma/extension-read-replicas": "^0.3.0", diff --git a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx index 365783360a..bdf0ba7d74 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx @@ -45,11 +45,11 @@ export function WebsiteHeader({ icon: , path: '/reports', }, - // { - // label: formatMessage(labels.sessions), - // icon: , - // path: '/sessions', - // }, + { + label: formatMessage(labels.sessions), + icon: , + path: '/sessions', + }, { label: formatMessage(labels.events), icon: , @@ -69,7 +69,7 @@ export function WebsiteHeader({
{links.map(({ label, icon, path }) => { const selected = path - ? pathname.endsWith(path) + ? pathname.includes(path) : pathname.match(/^\/websites\/[\w-]+$/); return ( diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx index d5ca439ad8..7bae8b6838 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx @@ -1,6 +1,8 @@ +import Link from 'next/link'; import { GridColumn, GridTable, useBreakpoint } from 'react-basics'; import { useFormat, useMessages } from 'components/hooks'; import { formatDistanceToNow } from 'date-fns'; +import Profile from 'components/common/Profile'; export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean }) { const { formatMessage, labels } = useMessages(); @@ -9,7 +11,16 @@ export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean return ( - + + {row => } + + + {row => ( + + {row.id} ({row.firstAt !== row.lastAt ? 'YES' : 'NO'}) + + )} + {row => formatValue(row.country, 'country')} diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.module.css b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.module.css new file mode 100644 index 0000000000..2f71e9a885 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.module.css @@ -0,0 +1,3 @@ +.page { + display: grid; +} diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx new file mode 100644 index 0000000000..fc6945ceae --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx @@ -0,0 +1,27 @@ +'use client'; +import WebsiteHeader from '../../WebsiteHeader'; +import SessionInfo from './SessionInfo'; +import { useSession } from 'components/hooks'; +import { Loading } from 'react-basics'; +import styles from './SessionDetailsPage.module.css'; + +export default function SessionDetailsPage({ + websiteId, + sessionId, +}: { + websiteId: string; + sessionId: string; +}) { + const { data, isLoading } = useSession(websiteId, sessionId); + + if (isLoading) { + return ; + } + + return ( +
+ + +
+ ); +} diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.tsx new file mode 100644 index 0000000000..051703a3eb --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.tsx @@ -0,0 +1,17 @@ +import Profile from 'components/common/Profile'; + +export default function SessionInfo({ data }) { + return ( +

+ +
+
ID
+
{data?.id}
+
Country
+
{data?.country}
+
City
+
{data?.city}
+
+

+ ); +} diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/page.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/page.tsx new file mode 100644 index 0000000000..952e8cc580 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/page.tsx @@ -0,0 +1,10 @@ +import SessionDetailsPage from './SessionDetailsPage'; +import { Metadata } from 'next'; + +export default function WebsitePage({ params: { websiteId, sessionId } }) { + return ; +} + +export const metadata: Metadata = { + title: 'Websites', +}; diff --git a/src/components/common/Profile.tsx b/src/components/common/Profile.tsx new file mode 100644 index 0000000000..63a59c1687 --- /dev/null +++ b/src/components/common/Profile.tsx @@ -0,0 +1,41 @@ +import { useMemo } from 'react'; +import { createAvatar } from '@dicebear/core'; +import { lorelei } from '@dicebear/collection'; +import md5 from 'md5'; + +const lib = lorelei; + +function convertToPastel(hexColor: string, pastelFactor: number = 0.5) { + // Remove the # if present + hexColor = hexColor.replace(/^#/, ''); + + // Convert hex to RGB + let r = parseInt(hexColor.substr(0, 2), 16); + let g = parseInt(hexColor.substr(2, 2), 16); + let b = parseInt(hexColor.substr(4, 2), 16); + + // Calculate pastel version (mix with white) + //const pastelFactor = 0.5; // Adjust this value to control pastel intensity + + r = Math.floor((r + 255 * pastelFactor) / (1 + pastelFactor)); + g = Math.floor((g + 255 * pastelFactor) / (1 + pastelFactor)); + b = Math.floor((b + 255 * pastelFactor) / (1 + pastelFactor)); + + // Convert back to hex + return `#${((1 << 24) | (r << 16) | (g << 8) | b).toString(16).slice(1)}`; +} + +function Profile({ seed, size = 128, ...props }: { seed: string; size?: number }) { + const avatar = useMemo(() => { + return createAvatar(lib, { + ...props, + seed, + size, + backgroundColor: [convertToPastel(md5(seed).substring(0, 6), 2).replace(/^#/, '')], + }).toDataUri(); + }, []); + + return Avatar; +} + +export default Profile; diff --git a/src/components/hooks/index.ts b/src/components/hooks/index.ts index 86abdd84c6..b0271ff2cd 100644 --- a/src/components/hooks/index.ts +++ b/src/components/hooks/index.ts @@ -5,6 +5,7 @@ export * from './queries/useLogin'; export * from './queries/useRealtime'; export * from './queries/useReport'; export * from './queries/useReports'; +export * from './queries/useSession'; export * from './queries/useSessions'; export * from './queries/useShareToken'; export * from './queries/useTeam'; diff --git a/src/components/hooks/queries/useSession.ts b/src/components/hooks/queries/useSession.ts new file mode 100644 index 0000000000..f65efcf45a --- /dev/null +++ b/src/components/hooks/queries/useSession.ts @@ -0,0 +1,14 @@ +import { useApi } from './useApi'; + +export function useSession(websiteId: string, sessionId: string) { + const { get, useQuery } = useApi(); + + return useQuery({ + queryKey: ['session', { websiteId, sessionId }], + queryFn: () => { + return get(`/websites/${websiteId}/sessions/${sessionId}`); + }, + }); +} + +export default useSession; diff --git a/src/components/hooks/queries/useSessions.ts b/src/components/hooks/queries/useSessions.ts index c54c3acd1b..91f4ea4c64 100644 --- a/src/components/hooks/queries/useSessions.ts +++ b/src/components/hooks/queries/useSessions.ts @@ -4,7 +4,7 @@ import useModified from '../useModified'; export function useSessions(websiteId: string, params?: { [key: string]: string | number }) { const { get } = useApi(); - const { modified } = useModified(`websites`); + const { modified } = useModified(`sessions`); return useFilterQuery({ queryKey: ['sessions', { websiteId, modified, ...params }], diff --git a/src/pages/api/websites/[websiteId]/sessions/[sessionId].ts b/src/pages/api/websites/[websiteId]/sessions/[sessionId].ts new file mode 100644 index 0000000000..57e99ea376 --- /dev/null +++ b/src/pages/api/websites/[websiteId]/sessions/[sessionId].ts @@ -0,0 +1,42 @@ +import * as yup from 'yup'; +import { canViewWebsite } from 'lib/auth'; +import { useAuth, useCors, useValidate } from 'lib/middleware'; +import { NextApiRequestQueryBody, PageParams } from 'lib/types'; +import { NextApiResponse } from 'next'; +import { methodNotAllowed, ok, unauthorized } from 'next-basics'; +import { getSession } from 'queries'; + +export interface ReportsRequestQuery extends PageParams { + websiteId: string; + sessionId: string; +} + +const schema = { + GET: yup.object().shape({ + websiteId: yup.string().uuid().required(), + sessionId: yup.string().uuid().required(), + }), +}; + +export default async ( + req: NextApiRequestQueryBody, + res: NextApiResponse, +) => { + await useCors(req, res); + await useAuth(req, res); + await useValidate(schema, req, res); + + const { websiteId, sessionId } = req.query; + + if (req.method === 'GET') { + if (!(await canViewWebsite(req.auth, websiteId))) { + return unauthorized(res); + } + + const data = await getSession(websiteId, sessionId); + + return ok(res, data); + } + + return methodNotAllowed(res); +}; diff --git a/src/pages/api/websites/[websiteId]/sessions.ts b/src/pages/api/websites/[websiteId]/sessions/index.ts similarity index 100% rename from src/pages/api/websites/[websiteId]/sessions.ts rename to src/pages/api/websites/[websiteId]/sessions/index.ts diff --git a/src/queries/analytics/sessions/getSession.ts b/src/queries/analytics/sessions/getSession.ts index 256ada4c08..2c008e30cc 100644 --- a/src/queries/analytics/sessions/getSession.ts +++ b/src/queries/analytics/sessions/getSession.ts @@ -1,9 +1,46 @@ import prisma from 'lib/prisma'; +import clickhouse from 'lib/clickhouse'; +import { runQuery, PRISMA, CLICKHOUSE } from 'lib/db'; -export async function getSession(id: string) { +export async function getSession(...args: [websiteId: string, sessionId: string]) { + return runQuery({ + [PRISMA]: () => relationalQuery(...args), + [CLICKHOUSE]: () => clickhouseQuery(...args), + }); +} + +async function relationalQuery(websiteId: string, sessionId: string) { return prisma.client.session.findUnique({ where: { - id, + id: sessionId, }, }); } + +async function clickhouseQuery(websiteId: string, sessionId: string) { + const { rawQuery } = clickhouse; + + return rawQuery( + ` + select + session_id as id, + website_id as websiteId, + min(created_at) as firstAt, + max(created_at) as lastAt, + hostname, + browser, + os, + device, + screen, + language, + country, + subdivision1, + city + from website_event + where website_id = {websiteId:UUID} + and session_id = {sessionId:UUID} + group by session_id, website_id, hostname, browser, os, device, screen, language, country, subdivision1, city + `, + { websiteId, sessionId }, + ).then(result => result?.[0]); +} diff --git a/src/queries/analytics/sessions/getSessions.ts b/src/queries/analytics/sessions/getSessions.ts index 538133bac3..e7c9596339 100644 --- a/src/queries/analytics/sessions/getSessions.ts +++ b/src/queries/analytics/sessions/getSessions.ts @@ -24,7 +24,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters, pagePar } async function clickhouseQuery(websiteId: string, filters: QueryFilters, pageParams?: PageParams) { - const { pagedQuery, parseFilters, getDateStringSQL } = clickhouse; + const { pagedQuery, parseFilters } = clickhouse; const { params, dateQuery, filterQuery } = await parseFilters(websiteId, filters); return pagedQuery( @@ -32,7 +32,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar select session_id as id, website_id as websiteId, - ${getDateStringSQL('created_at', 'second', filters.timezone)} as createdAt, + min(created_at) as createdAt, hostname, browser, os, @@ -41,13 +41,13 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar language, country, subdivision1, - subdivision2, city from website_event where website_id = {websiteId:UUID} ${dateQuery} ${filterQuery} - order by created_at desc + group by session_id, website_id, hostname, browser, os, device, screen, language, country, subdivision1, city + order by createdAt desc `, params, pageParams, diff --git a/yarn.lock b/yarn.lock index 1fe4013562..470cad8fc7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1381,6 +1381,199 @@ debug "^3.1.0" lodash.once "^4.1.1" +"@dicebear/adventurer-neutral@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/adventurer-neutral/-/adventurer-neutral-9.2.1.tgz#0416ff71d0dd3ff391db9c9fda2acb166b074c5f" + integrity sha512-iP6Tc6CgrJt63j08i/hlyNiGEbDNgP9Ws6WKT9n/0oTU9X/DKLncGStV3uhgYPIOVQE/tw9a/GjbGjrwBlN8CQ== + +"@dicebear/adventurer@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/adventurer/-/adventurer-9.2.1.tgz#3d522d2aaabe17d172ea2302bfdf62d601ebcd64" + integrity sha512-utJr8oEOwPy9y+7rIOnB7mls+2XQrc3Kdlx/ay9KBY/HEUMnwMoN/GJhg4HcyGnV+DS7VhN6JSrnwwD9+SQyBw== + +"@dicebear/avataaars-neutral@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/avataaars-neutral/-/avataaars-neutral-9.2.1.tgz#61d3894f4d08d9ee3722f3a64c36725729c6aaf3" + integrity sha512-yceQMVBLimAHgZDL8VKCDGNs5JQ8BERaUMNIJXXRKEYZXlofoXZpYtcWPKQY9lmRJJznO1GX7ZK12ILnZjRPBQ== + +"@dicebear/avataaars@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/avataaars/-/avataaars-9.2.1.tgz#2460fb0d7d364a12e546b40b6e56c220db71c851" + integrity sha512-WIZL7CWSsmzLswY/4ZrgtE/7EvnaNrYreLyT8hjiGyVb9J4cQaVZXSMuDIGFa5wT062AW/4/i82kh/7nh0oL+w== + +"@dicebear/big-ears-neutral@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/big-ears-neutral/-/big-ears-neutral-9.2.1.tgz#dfee12787f8a0efa7b3552fca5e9f24c9102fc44" + integrity sha512-98qOCFEhbqCHeyO7ZXBAMMov8bquZt8vhtjj0YeHjGjI/OEWbA2gxq2ryv1BHSehVc/vTrd1KbHag7yYoeCDuw== + +"@dicebear/big-ears@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/big-ears/-/big-ears-9.2.1.tgz#4a7f2f3d987c11d76602b6ee8b398f9dcd2ce486" + integrity sha512-BUVTonwSYiGKcnk8wdwUHZ1b34GhfzRpG1kguK4kWAKlayBq7Q+iDJlmk4Bch0XdDQc2bqFf1GQCCj+xXWRHyg== + +"@dicebear/big-smile@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/big-smile/-/big-smile-9.2.1.tgz#d6cf95d34d65a393901925df370a1afbd0b0ae76" + integrity sha512-bspur+wtnlv/Z4QDvRWg9rs3snf+iuBkamkgw4nZOUFKMlZdPQGqNoh1DkycRcLXNX1Q61KM172K6bS60ZlKxw== + +"@dicebear/bottts-neutral@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/bottts-neutral/-/bottts-neutral-9.2.1.tgz#cc89b1ed834b7e8b7163dee2928dc1aa74077f75" + integrity sha512-uwd+xcbRQUIHKQ1iEiLjf5RwCaVzOfBgIu2WRE+6MUaahYi6cJ0eJAs0h1q+zpgYyvqPDPDAi9j7AUwjmig0GA== + +"@dicebear/bottts@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/bottts/-/bottts-9.2.1.tgz#922ccdba942d8c2c15655be0b6f25f4e9691cb80" + integrity sha512-AQQ/WKd54G9sa+TkQptcu6c+Tjfc9hitgB70uA5GqJe+w6Bal+gwY6kPm5sJ1CY2mk/UBh1rXBuauQZ25bgTcQ== + +"@dicebear/collection@^9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/collection/-/collection-9.2.1.tgz#b2de84ef654ac1550458a17049b02a0013213b92" + integrity sha512-Su1eygO8llKuJ68N+xhBCzBN2Lqrsx9ZNdlvfZeH/s70RjL0raNQaI6/hRABDmlbLYwW4AjRh2lOgDdGfCp5DQ== + dependencies: + "@dicebear/adventurer" "9.2.1" + "@dicebear/adventurer-neutral" "9.2.1" + "@dicebear/avataaars" "9.2.1" + "@dicebear/avataaars-neutral" "9.2.1" + "@dicebear/big-ears" "9.2.1" + "@dicebear/big-ears-neutral" "9.2.1" + "@dicebear/big-smile" "9.2.1" + "@dicebear/bottts" "9.2.1" + "@dicebear/bottts-neutral" "9.2.1" + "@dicebear/croodles" "9.2.1" + "@dicebear/croodles-neutral" "9.2.1" + "@dicebear/dylan" "9.2.1" + "@dicebear/fun-emoji" "9.2.1" + "@dicebear/glass" "9.2.1" + "@dicebear/icons" "9.2.1" + "@dicebear/identicon" "9.2.1" + "@dicebear/initials" "9.2.1" + "@dicebear/lorelei" "9.2.1" + "@dicebear/lorelei-neutral" "9.2.1" + "@dicebear/micah" "9.2.1" + "@dicebear/miniavs" "9.2.1" + "@dicebear/notionists" "9.2.1" + "@dicebear/notionists-neutral" "9.2.1" + "@dicebear/open-peeps" "9.2.1" + "@dicebear/personas" "9.2.1" + "@dicebear/pixel-art" "9.2.1" + "@dicebear/pixel-art-neutral" "9.2.1" + "@dicebear/rings" "9.2.1" + "@dicebear/shapes" "9.2.1" + "@dicebear/thumbs" "9.2.1" + +"@dicebear/core@^9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/core/-/core-9.2.1.tgz#b93800ac7e21ae955cceaa2370e7dc033a3cc557" + integrity sha512-Y3E59+3xO2UWKdf3Zt/rwMdy9r7uccTgM89Kv8aXN1vmdrnA4YYmr4jslRRRqPLVpenuT4105nkboC4rMnDgHw== + dependencies: + "@types/json-schema" "^7.0.11" + +"@dicebear/croodles-neutral@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/croodles-neutral/-/croodles-neutral-9.2.1.tgz#379646cf53ba4d61b6c94c13b3ec3386359e3541" + integrity sha512-2iyr+B/y795P7cSIpFg4RjxUu6kljesKjtepvMzfeBR9xKyI84exBNHRoCTEVwOCFePmlPJX1qtw/YWM0sAPJw== + +"@dicebear/croodles@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/croodles/-/croodles-9.2.1.tgz#74fa6aa8c0ee0dc303c0ad82fd78f5d6cd66610d" + integrity sha512-V7+m21BizYTGgLgxmh5dxHHADeD3gkeuPYkhKqP8Uu8jZFBgh5wKFqqfVI/XSQkx/+lRla5c6l55mymgjt4k8Q== + +"@dicebear/dylan@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/dylan/-/dylan-9.2.1.tgz#997aff74b4bf112e4895463eeea0adc2456d332f" + integrity sha512-UeKz3Gxprh4bJ73Q2DjDpmjt854G3xfakc5KfeBmPV25EP+al7HCsM/HE+ZgKTSh+PPz5/mVtZQYU40pTzJEyg== + +"@dicebear/fun-emoji@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/fun-emoji/-/fun-emoji-9.2.1.tgz#e88c8c2db7927d732ef2d0af3a24c4d4152a7f02" + integrity sha512-F08p+Ggdxo4Ryji+3aCJXAKnjx4rM4UMtrJU4eA2t8lAkpwFNgfGK6mpMYPnxmKULYljGOgySmw7AyWcbX8s2Q== + +"@dicebear/glass@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/glass/-/glass-9.2.1.tgz#61bc231a5b0bd8d15cfa94c76ac608ab8fe98ac1" + integrity sha512-UoErQwg7/qkEKWyEDTyt8FYhw/aZryP0Tr7cwBEuxMXZ585NUTvEel0K5j9aDkBrimJVEM+jKzOFIIMAGLlR0g== + +"@dicebear/icons@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/icons/-/icons-9.2.1.tgz#0ef78e8ff742bd9985a3c36cd9cbd2705449e789" + integrity sha512-0VuWohGMiv4n1nxwehYi6w+PIT9OBRlV721yNoewQWgQCrnMKBvM0cFRX9Dtg+MvwLMslQCIU3pEauEZ5FNmFA== + +"@dicebear/identicon@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/identicon/-/identicon-9.2.1.tgz#aebd7b692de9a6601746ab47711359f81471841c" + integrity sha512-Dlqpn3tzqimR8KPIRkSJCKd5XwKgTLVXzT5KiY+2ysMZZQh4uJvBjVfY5SLrHDHC2a42W6EdwQxU6tFTRiKQuQ== + +"@dicebear/initials@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/initials/-/initials-9.2.1.tgz#d4e435c18e48837f97086ba5210d4742594ba181" + integrity sha512-d6Shnt1LiCf9yAEck3y/w4pXG4bWYVjBFCeI43l0BAR39Mk2Dq05UEFZH5Dtj2kyfNozMjh6vG1cQyBigtamug== + +"@dicebear/lorelei-neutral@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/lorelei-neutral/-/lorelei-neutral-9.2.1.tgz#5d5d6400c0e2232081f58f8d82efff00ee307b81" + integrity sha512-4YkkR697qXAYxN5N/zVsRe955QLhw0yLib2CzeBga1QXXMIkywq2nRFa3fr4toSRPl45kl1eF8J5HC17CU9inw== + +"@dicebear/lorelei@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/lorelei/-/lorelei-9.2.1.tgz#b23ddb02b098578ef1bcb121c7a638795f3de89e" + integrity sha512-DNjZpUpe/CxKK8Byn1meBvRz/NJWtBizcoS2DzIIyqPYOwA5cLIa2g/qKkESvXzU9naEMkiHfMZb1RYYzN2FAA== + +"@dicebear/micah@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/micah/-/micah-9.2.1.tgz#2e65fdb15b6ee6338123329c92e5afb98f694718" + integrity sha512-FK91igiVpPNhGCsfGpOgwYFKRP+FNR1V45Z4Tg/f82ux9TBdTmeoIfkgwrfhcXmCgagoYg2EAY+L72stUVapcA== + +"@dicebear/miniavs@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/miniavs/-/miniavs-9.2.1.tgz#de1e571663775b4b2a30944d8033d6a8b972aca3" + integrity sha512-r0TcaSrKJDPMqMYIiXNArq9i//cZzA1yuiXJw46iTloBDTh7yL1tpnL84CDxMpQ+OZLeMiRA6jVBx0coer4vmg== + +"@dicebear/notionists-neutral@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/notionists-neutral/-/notionists-neutral-9.2.1.tgz#2e77ae7330201628c4b3e4789b1aa83ffc684b82" + integrity sha512-Vi/FwMXzc1m/U2TjBnY9NHedoLbPc3BBsNQL8jPU27wdkXoyJHuXBevcUtsF0Zf8OuRbNpZKPbfYy6OYBr9qvw== + +"@dicebear/notionists@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/notionists/-/notionists-9.2.1.tgz#70aa14e8846a49096648f891a625a92b04513c75" + integrity sha512-oAyvPlp3xfFnDpW3nXhdAPGVm5WYj6VW6RgdzLAHoRO2EOYDNkQruIXd+d8JYo1DMTLUbgp3onr5AF9UU2OBzw== + +"@dicebear/open-peeps@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/open-peeps/-/open-peeps-9.2.1.tgz#dd9be6721ff7226e3a0286aec699fd6b0e0ad2b2" + integrity sha512-oPA/ljbPtuj2cdM0QtyJu2i24AaEMTIIk/FJbnrBK765WPnQcCZh84w+ZuInTMIfF9gYszNY34gaRD8Z6UiYxQ== + +"@dicebear/personas@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/personas/-/personas-9.2.1.tgz#dfa8291ebba098e8cebdeb04a4fba9e4384c4496" + integrity sha512-OgtyT9dnY8U60sUo0SLKCFVt0+dIr3a4vR0bDs/zwK4Qb/yTdB1VPdfxq0Fwk2q1vfn9YgbDrb0YYRgMRl60qQ== + +"@dicebear/pixel-art-neutral@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/pixel-art-neutral/-/pixel-art-neutral-9.2.1.tgz#457b4e038c9516e75d41c6c1efd9ff9d4099162a" + integrity sha512-GUtxJYX7/9XDgSZhkx24PB+yLcKkLHblDldvRr5xGlGxhgAovTBQFHLgCJxmUJgIaNW7pvSWCw7txguyxbBN9A== + +"@dicebear/pixel-art@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/pixel-art/-/pixel-art-9.2.1.tgz#e50bcc300a43c16ad3f69de1b6bf3dad92528274" + integrity sha512-ftKPKCvnS1cJ2OvuQLmtEIwdb9PzF5C2ofWBdVI/RFvhH1BhYc3OsdQ28o90+ZJQO4fivKwfsh8MPUTaqToQ7Q== + +"@dicebear/rings@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/rings/-/rings-9.2.1.tgz#ed6e930c02b09ebbd774fc104cffc5c23ffa6b4c" + integrity sha512-BlFYCaKB+wdpWWS28ZnQ/MvHeuNSRvkvWRoiw7pgS653LXx4kz/erVMmeVMSAr82y4BV+K8He2Rl2dMjuLyrXw== + +"@dicebear/shapes@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/shapes/-/shapes-9.2.1.tgz#e076b9aabd67bf611e9bbf7149919000e3d74197" + integrity sha512-cQzTcYimtuiAun55uPdIIhK53QTyjWqF/YN7LqEBGBqrJuGqHZBm1HXCcj7wPpoQ3zSy/2u8Rp0Etv7+5XFzyw== + +"@dicebear/thumbs@9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@dicebear/thumbs/-/thumbs-9.2.1.tgz#174be721256e5ff97e19586a54bde8fd25468e74" + integrity sha512-ziX5HFmhiApO2k7QKj41+dGXbMdmQUUgFBYPyzTwnubhkDldJk7tpRoa5u2OsyTVDQCcPMv5mFSQpbANfmFwMg== + "@esbuild/android-arm64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz#bafb75234a5d3d1b690e7c2956a599345e84a2fd" @@ -2600,7 +2793,7 @@ expect "^29.0.0" pretty-format "^29.0.0" -"@types/json-schema@^7.0.12": +"@types/json-schema@^7.0.11", "@types/json-schema@^7.0.12": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== From c3c3b46ef693de21ef32b2c71c4f0fe2a86e3511 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Mon, 29 Jul 2024 01:38:36 -0700 Subject: [PATCH 048/132] Updated session profile page. Added TypeIcon component. Added useRegionNames hook. --- .../{browsers => browser}/android-webview.png | Bin .../images/{browsers => browser}/android.png | Bin public/images/{browsers => browser}/aol.png | Bin .../images/{browsers => browser}/beaker.png | Bin .../{browsers => browser}/blackberry.png | Bin public/images/{browsers => browser}/brave.png | Bin .../images/{browsers => browser}/chrome.png | Bin .../chromium-webview.png | Bin public/images/{browsers => browser}/crios.png | Bin public/images/{browsers => browser}/curl.png | Bin .../{browsers => browser}/edge-chromium.png | Bin .../images/{browsers => browser}/edge-ios.png | Bin public/images/{browsers => browser}/edge.png | Bin .../images/{browsers => browser}/facebook.png | Bin .../images/{browsers => browser}/firefox.png | Bin public/images/{browsers => browser}/fxios.png | Bin public/images/{browsers => browser}/ie.png | Bin .../{browsers => browser}/instagram.png | Bin .../{browsers => browser}/ios-webview.png | Bin public/images/{browsers => browser}/ios.png | Bin .../{browsers => browser}/kakaotalk.png | Bin public/images/{browsers => browser}/miui.png | Bin .../{browsers => browser}/opera-mini.png | Bin public/images/{browsers => browser}/opera.png | Bin .../images/{browsers => browser}/safari.png | Bin .../images/{browsers => browser}/samsung.png | Bin .../{browsers => browser}/searchbot.png | Bin public/images/{browsers => browser}/silk.png | Bin .../images/{browsers => browser}/unknown.png | Bin .../{browsers => browser}/yandexbrowser.png | Bin public/images/{flags => country}/ad.png | Bin public/images/{flags => country}/ae.png | Bin public/images/{flags => country}/af.png | Bin public/images/{flags => country}/ag.png | Bin public/images/{flags => country}/ai.png | Bin public/images/{flags => country}/al.png | Bin public/images/{flags => country}/am.png | Bin public/images/{flags => country}/ao.png | Bin public/images/{flags => country}/aq.png | Bin public/images/{flags => country}/ar.png | Bin public/images/{flags => country}/as.png | Bin public/images/{flags => country}/at.png | Bin public/images/{flags => country}/au.png | Bin public/images/{flags => country}/aw.png | Bin public/images/{flags => country}/ax.png | Bin public/images/{flags => country}/az.png | Bin public/images/{flags => country}/ba.png | Bin public/images/{flags => country}/bb.png | Bin public/images/{flags => country}/bd.png | Bin public/images/{flags => country}/be.png | Bin public/images/{flags => country}/bf.png | Bin public/images/{flags => country}/bg.png | Bin public/images/{flags => country}/bh.png | Bin public/images/{flags => country}/bi.png | Bin public/images/{flags => country}/bj.png | Bin public/images/{flags => country}/bl.png | Bin public/images/{flags => country}/bm.png | Bin public/images/{flags => country}/bn.png | Bin public/images/{flags => country}/bo.png | Bin public/images/{flags => country}/bq.png | Bin public/images/{flags => country}/br.png | Bin public/images/{flags => country}/bs.png | Bin public/images/{flags => country}/bt.png | Bin public/images/{flags => country}/bv.png | Bin public/images/{flags => country}/bw.png | Bin public/images/{flags => country}/by.png | Bin public/images/{flags => country}/bz.png | Bin public/images/{flags => country}/ca.png | Bin public/images/{flags => country}/cc.png | Bin public/images/{flags => country}/cd.png | Bin public/images/{flags => country}/cf.png | Bin public/images/{flags => country}/cg.png | Bin public/images/{flags => country}/ch.png | Bin public/images/{flags => country}/ci.png | Bin public/images/{flags => country}/ck.png | Bin public/images/{flags => country}/cl.png | Bin public/images/{flags => country}/cm.png | Bin public/images/{flags => country}/cn.png | Bin public/images/{flags => country}/co.png | Bin public/images/{flags => country}/cr.png | Bin public/images/{flags => country}/cu.png | Bin public/images/{flags => country}/cv.png | Bin public/images/{flags => country}/cw.png | Bin public/images/{flags => country}/cx.png | Bin public/images/{flags => country}/cy.png | Bin public/images/{flags => country}/cz.png | Bin public/images/{flags => country}/de.png | Bin public/images/{flags => country}/dj.png | Bin public/images/{flags => country}/dk.png | Bin public/images/{flags => country}/dm.png | Bin public/images/{flags => country}/do.png | Bin public/images/{flags => country}/dz.png | Bin public/images/{flags => country}/ec.png | Bin public/images/{flags => country}/ee.png | Bin public/images/{flags => country}/eg.png | Bin public/images/{flags => country}/eh.png | Bin public/images/{flags => country}/er.png | Bin public/images/{flags => country}/es.png | Bin public/images/{flags => country}/et.png | Bin public/images/{flags => country}/fi.png | Bin public/images/{flags => country}/fj.png | Bin public/images/{flags => country}/fk.png | Bin public/images/{flags => country}/fm.png | Bin public/images/{flags => country}/fo.png | Bin public/images/{flags => country}/fr.png | Bin public/images/{flags => country}/ga.png | Bin public/images/{flags => country}/gb-eng.png | Bin public/images/{flags => country}/gb-nir.png | Bin public/images/{flags => country}/gb-sct.png | Bin public/images/{flags => country}/gb-wls.png | Bin public/images/{flags => country}/gb.png | Bin public/images/{flags => country}/gd.png | Bin public/images/{flags => country}/ge.png | Bin public/images/{flags => country}/gf.png | Bin public/images/{flags => country}/gg.png | Bin public/images/{flags => country}/gh.png | Bin public/images/{flags => country}/gi.png | Bin public/images/{flags => country}/gl.png | Bin public/images/{flags => country}/gm.png | Bin public/images/{flags => country}/gn.png | Bin public/images/{flags => country}/gp.png | Bin public/images/{flags => country}/gq.png | Bin public/images/{flags => country}/gr.png | Bin public/images/{flags => country}/gs.png | Bin public/images/{flags => country}/gt.png | Bin public/images/{flags => country}/gu.png | Bin public/images/{flags => country}/gw.png | Bin public/images/{flags => country}/gy.png | Bin public/images/{flags => country}/hk.png | Bin public/images/{flags => country}/hm.png | Bin public/images/{flags => country}/hn.png | Bin public/images/{flags => country}/hr.png | Bin public/images/{flags => country}/ht.png | Bin public/images/{flags => country}/hu.png | Bin public/images/{flags => country}/id.png | Bin public/images/{flags => country}/ie.png | Bin public/images/{flags => country}/il.png | Bin public/images/{flags => country}/im.png | Bin public/images/{flags => country}/in.png | Bin public/images/{flags => country}/io.png | Bin public/images/{flags => country}/iq.png | Bin public/images/{flags => country}/ir.png | Bin public/images/{flags => country}/is.png | Bin public/images/{flags => country}/it.png | Bin public/images/{flags => country}/je.png | Bin public/images/{flags => country}/jm.png | Bin public/images/{flags => country}/jo.png | Bin public/images/{flags => country}/jp.png | Bin public/images/{flags => country}/ke.png | Bin public/images/{flags => country}/kg.png | Bin public/images/{flags => country}/kh.png | Bin public/images/{flags => country}/ki.png | Bin public/images/{flags => country}/km.png | Bin public/images/{flags => country}/kn.png | Bin public/images/{flags => country}/kp.png | Bin public/images/{flags => country}/kr.png | Bin public/images/{flags => country}/kw.png | Bin public/images/{flags => country}/ky.png | Bin public/images/{flags => country}/kz.png | Bin public/images/{flags => country}/la.png | Bin public/images/{flags => country}/lb.png | Bin public/images/{flags => country}/lc.png | Bin public/images/{flags => country}/li.png | Bin public/images/{flags => country}/lk.png | Bin public/images/{flags => country}/lr.png | Bin public/images/{flags => country}/ls.png | Bin public/images/{flags => country}/lt.png | Bin public/images/{flags => country}/lu.png | Bin public/images/{flags => country}/lv.png | Bin public/images/{flags => country}/ly.png | Bin public/images/{flags => country}/ma.png | Bin public/images/{flags => country}/mc.png | Bin public/images/{flags => country}/md.png | Bin public/images/{flags => country}/me.png | Bin public/images/{flags => country}/mf.png | Bin public/images/{flags => country}/mg.png | Bin public/images/{flags => country}/mh.png | Bin public/images/{flags => country}/mk.png | Bin public/images/{flags => country}/ml.png | Bin public/images/{flags => country}/mm.png | Bin public/images/{flags => country}/mn.png | Bin public/images/{flags => country}/mo.png | Bin public/images/{flags => country}/mp.png | Bin public/images/{flags => country}/mq.png | Bin public/images/{flags => country}/mr.png | Bin public/images/{flags => country}/ms.png | Bin public/images/{flags => country}/mt.png | Bin public/images/{flags => country}/mu.png | Bin public/images/{flags => country}/mv.png | Bin public/images/{flags => country}/mw.png | Bin public/images/{flags => country}/mx.png | Bin public/images/{flags => country}/my.png | Bin public/images/{flags => country}/mz.png | Bin public/images/{flags => country}/na.png | Bin public/images/{flags => country}/nc.png | Bin public/images/{flags => country}/ne.png | Bin public/images/{flags => country}/nf.png | Bin public/images/{flags => country}/ng.png | Bin public/images/{flags => country}/ni.png | Bin public/images/{flags => country}/nl.png | Bin public/images/{flags => country}/no.png | Bin public/images/{flags => country}/np.png | Bin public/images/{flags => country}/nr.png | Bin public/images/{flags => country}/nu.png | Bin public/images/{flags => country}/nz.png | Bin public/images/{flags => country}/om.png | Bin public/images/{flags => country}/pa.png | Bin public/images/{flags => country}/pe.png | Bin public/images/{flags => country}/pf.png | Bin public/images/{flags => country}/pg.png | Bin public/images/{flags => country}/ph.png | Bin public/images/{flags => country}/pk.png | Bin public/images/{flags => country}/pl.png | Bin public/images/{flags => country}/pm.png | Bin public/images/{flags => country}/pn.png | Bin public/images/{flags => country}/pr.png | Bin public/images/{flags => country}/ps.png | Bin public/images/{flags => country}/pt.png | Bin public/images/{flags => country}/pw.png | Bin public/images/{flags => country}/py.png | Bin public/images/{flags => country}/qa.png | Bin public/images/{flags => country}/re.png | Bin public/images/{flags => country}/ro.png | Bin public/images/{flags => country}/rs.png | Bin public/images/{flags => country}/ru.png | Bin public/images/{flags => country}/rw.png | Bin public/images/{flags => country}/sa.png | Bin public/images/{flags => country}/sb.png | Bin public/images/{flags => country}/sc.png | Bin public/images/{flags => country}/sd.png | Bin public/images/{flags => country}/se.png | Bin public/images/{flags => country}/sg.png | Bin public/images/{flags => country}/sh.png | Bin public/images/{flags => country}/si.png | Bin public/images/{flags => country}/sj.png | Bin public/images/{flags => country}/sk.png | Bin public/images/{flags => country}/sl.png | Bin public/images/{flags => country}/sm.png | Bin public/images/{flags => country}/sn.png | Bin public/images/{flags => country}/so.png | Bin public/images/{flags => country}/sr.png | Bin public/images/{flags => country}/ss.png | Bin public/images/{flags => country}/st.png | Bin public/images/{flags => country}/sv.png | Bin public/images/{flags => country}/sx.png | Bin public/images/{flags => country}/sy.png | Bin public/images/{flags => country}/sz.png | Bin public/images/{flags => country}/tc.png | Bin public/images/{flags => country}/td.png | Bin public/images/{flags => country}/tf.png | Bin public/images/{flags => country}/tg.png | Bin public/images/{flags => country}/th.png | Bin public/images/{flags => country}/tj.png | Bin public/images/{flags => country}/tk.png | Bin public/images/{flags => country}/tl.png | Bin public/images/{flags => country}/tm.png | Bin public/images/{flags => country}/tn.png | Bin public/images/{flags => country}/to.png | Bin public/images/{flags => country}/tr.png | Bin public/images/{flags => country}/tt.png | Bin public/images/{flags => country}/tv.png | Bin public/images/{flags => country}/tw.png | Bin public/images/{flags => country}/tz.png | Bin public/images/{flags => country}/ua.png | Bin public/images/{flags => country}/ug.png | Bin public/images/{flags => country}/um.png | Bin .../{flags/xx.png => country/unknown.png} | Bin public/images/{flags => country}/us.png | Bin public/images/{flags => country}/uy.png | Bin public/images/{flags => country}/uz.png | Bin public/images/{flags => country}/va.png | Bin public/images/{flags => country}/vc.png | Bin public/images/{flags => country}/ve.png | Bin public/images/{flags => country}/vg.png | Bin public/images/{flags => country}/vi.png | Bin public/images/{flags => country}/vn.png | Bin public/images/{flags => country}/vu.png | Bin public/images/{flags => country}/wf.png | Bin public/images/{flags => country}/ws.png | Bin public/images/{flags => country}/xk.png | Bin public/images/{flags => country}/ye.png | Bin public/images/{flags => country}/yt.png | Bin public/images/{flags => country}/za.png | Bin public/images/{flags => country}/zm.png | Bin public/images/{flags => country}/zw.png | Bin .../[websiteId]/WebsiteDetailsPage.tsx | 5 +- .../websites/[websiteId]/WebsiteProvider.tsx | 1 + .../compare/WebsiteComparePage.tsx | 5 +- .../(main)/websites/[websiteId]/layout.tsx | 17 +++++ .../realtime/RealtimeCountries.tsx | 8 +-- .../[websiteId]/realtime/RealtimeLog.tsx | 2 +- .../realtime/WebsiteRealtimePage.tsx | 5 +- .../[websiteId]/sessions/SessionsTable.tsx | 13 ++-- .../[sessionId]/SessionDetailsPage.module.css | 15 +++++ .../[sessionId]/SessionDetailsPage.tsx | 13 +++- .../[sessionId]/SessionInfo.module.css | 17 +++++ .../sessions/[sessionId]/SessionInfo.tsx | 63 +++++++++++++++--- src/components/common/TypeIcon.tsx | 18 +++++ src/components/hooks/index.ts | 1 + src/components/hooks/useCountryNames.ts | 2 +- src/components/hooks/useFormat.ts | 2 +- src/components/hooks/useRegionNames.ts | 19 ++++++ src/components/icons.ts | 2 + src/components/messages.ts | 2 + src/components/metrics/BrowsersTable.tsx | 8 +-- src/components/metrics/CitiesTable.tsx | 10 +-- src/components/metrics/CountriesTable.tsx | 8 +-- src/components/metrics/DevicesTable.tsx | 10 +-- src/components/metrics/OSTable.tsx | 10 +-- src/components/metrics/RegionsTable.tsx | 20 ++---- src/components/metrics/WorldMap.tsx | 2 +- src/queries/analytics/sessions/getSession.ts | 6 +- src/queries/analytics/sessions/getSessions.ts | 7 +- 313 files changed, 197 insertions(+), 94 deletions(-) rename public/images/{browsers => browser}/android-webview.png (100%) rename public/images/{browsers => browser}/android.png (100%) rename public/images/{browsers => browser}/aol.png (100%) rename public/images/{browsers => browser}/beaker.png (100%) rename public/images/{browsers => browser}/blackberry.png (100%) rename public/images/{browsers => browser}/brave.png (100%) rename public/images/{browsers => browser}/chrome.png (100%) rename public/images/{browsers => browser}/chromium-webview.png (100%) rename public/images/{browsers => browser}/crios.png (100%) rename public/images/{browsers => browser}/curl.png (100%) rename public/images/{browsers => browser}/edge-chromium.png (100%) rename public/images/{browsers => browser}/edge-ios.png (100%) rename public/images/{browsers => browser}/edge.png (100%) rename public/images/{browsers => browser}/facebook.png (100%) rename public/images/{browsers => browser}/firefox.png (100%) rename public/images/{browsers => browser}/fxios.png (100%) rename public/images/{browsers => browser}/ie.png (100%) rename public/images/{browsers => browser}/instagram.png (100%) rename public/images/{browsers => browser}/ios-webview.png (100%) rename public/images/{browsers => browser}/ios.png (100%) rename public/images/{browsers => browser}/kakaotalk.png (100%) rename public/images/{browsers => browser}/miui.png (100%) rename public/images/{browsers => browser}/opera-mini.png (100%) rename public/images/{browsers => browser}/opera.png (100%) rename public/images/{browsers => browser}/safari.png (100%) rename public/images/{browsers => browser}/samsung.png (100%) rename public/images/{browsers => browser}/searchbot.png (100%) rename public/images/{browsers => browser}/silk.png (100%) rename public/images/{browsers => browser}/unknown.png (100%) rename public/images/{browsers => browser}/yandexbrowser.png (100%) rename public/images/{flags => country}/ad.png (100%) rename public/images/{flags => country}/ae.png (100%) rename public/images/{flags => country}/af.png (100%) rename public/images/{flags => country}/ag.png (100%) rename public/images/{flags => country}/ai.png (100%) rename public/images/{flags => country}/al.png (100%) rename public/images/{flags => country}/am.png (100%) rename public/images/{flags => country}/ao.png (100%) rename public/images/{flags => country}/aq.png (100%) rename public/images/{flags => country}/ar.png (100%) rename public/images/{flags => country}/as.png (100%) rename public/images/{flags => country}/at.png (100%) rename public/images/{flags => country}/au.png (100%) rename public/images/{flags => country}/aw.png (100%) rename public/images/{flags => country}/ax.png (100%) rename public/images/{flags => country}/az.png (100%) rename public/images/{flags => country}/ba.png (100%) rename public/images/{flags => country}/bb.png (100%) rename public/images/{flags => country}/bd.png (100%) rename public/images/{flags => country}/be.png (100%) rename public/images/{flags => country}/bf.png (100%) rename public/images/{flags => country}/bg.png (100%) rename public/images/{flags => country}/bh.png (100%) rename public/images/{flags => country}/bi.png (100%) rename public/images/{flags => country}/bj.png (100%) rename public/images/{flags => country}/bl.png (100%) rename public/images/{flags => country}/bm.png (100%) rename public/images/{flags => country}/bn.png (100%) rename public/images/{flags => country}/bo.png (100%) rename public/images/{flags => country}/bq.png (100%) rename public/images/{flags => country}/br.png (100%) rename public/images/{flags => country}/bs.png (100%) rename public/images/{flags => country}/bt.png (100%) rename public/images/{flags => country}/bv.png (100%) rename public/images/{flags => country}/bw.png (100%) rename public/images/{flags => country}/by.png (100%) rename public/images/{flags => country}/bz.png (100%) rename public/images/{flags => country}/ca.png (100%) rename public/images/{flags => country}/cc.png (100%) rename public/images/{flags => country}/cd.png (100%) rename public/images/{flags => country}/cf.png (100%) rename public/images/{flags => country}/cg.png (100%) rename public/images/{flags => country}/ch.png (100%) rename public/images/{flags => country}/ci.png (100%) rename public/images/{flags => country}/ck.png (100%) rename public/images/{flags => country}/cl.png (100%) rename public/images/{flags => country}/cm.png (100%) rename public/images/{flags => country}/cn.png (100%) rename public/images/{flags => country}/co.png (100%) rename public/images/{flags => country}/cr.png (100%) rename public/images/{flags => country}/cu.png (100%) rename public/images/{flags => country}/cv.png (100%) rename public/images/{flags => country}/cw.png (100%) rename public/images/{flags => country}/cx.png (100%) rename public/images/{flags => country}/cy.png (100%) rename public/images/{flags => country}/cz.png (100%) rename public/images/{flags => country}/de.png (100%) rename public/images/{flags => country}/dj.png (100%) rename public/images/{flags => country}/dk.png (100%) rename public/images/{flags => country}/dm.png (100%) rename public/images/{flags => country}/do.png (100%) rename public/images/{flags => country}/dz.png (100%) rename public/images/{flags => country}/ec.png (100%) rename public/images/{flags => country}/ee.png (100%) rename public/images/{flags => country}/eg.png (100%) rename public/images/{flags => country}/eh.png (100%) rename public/images/{flags => country}/er.png (100%) rename public/images/{flags => country}/es.png (100%) rename public/images/{flags => country}/et.png (100%) rename public/images/{flags => country}/fi.png (100%) rename public/images/{flags => country}/fj.png (100%) rename public/images/{flags => country}/fk.png (100%) rename public/images/{flags => country}/fm.png (100%) rename public/images/{flags => country}/fo.png (100%) rename public/images/{flags => country}/fr.png (100%) rename public/images/{flags => country}/ga.png (100%) rename public/images/{flags => country}/gb-eng.png (100%) rename public/images/{flags => country}/gb-nir.png (100%) rename public/images/{flags => country}/gb-sct.png (100%) rename public/images/{flags => country}/gb-wls.png (100%) rename public/images/{flags => country}/gb.png (100%) rename public/images/{flags => country}/gd.png (100%) rename public/images/{flags => country}/ge.png (100%) rename public/images/{flags => country}/gf.png (100%) rename public/images/{flags => country}/gg.png (100%) rename public/images/{flags => country}/gh.png (100%) rename public/images/{flags => country}/gi.png (100%) rename public/images/{flags => country}/gl.png (100%) rename public/images/{flags => country}/gm.png (100%) rename public/images/{flags => country}/gn.png (100%) rename public/images/{flags => country}/gp.png (100%) rename public/images/{flags => country}/gq.png (100%) rename public/images/{flags => country}/gr.png (100%) rename public/images/{flags => country}/gs.png (100%) rename public/images/{flags => country}/gt.png (100%) rename public/images/{flags => country}/gu.png (100%) rename public/images/{flags => country}/gw.png (100%) rename public/images/{flags => country}/gy.png (100%) rename public/images/{flags => country}/hk.png (100%) rename public/images/{flags => country}/hm.png (100%) rename public/images/{flags => country}/hn.png (100%) rename public/images/{flags => country}/hr.png (100%) rename public/images/{flags => country}/ht.png (100%) rename public/images/{flags => country}/hu.png (100%) rename public/images/{flags => country}/id.png (100%) rename public/images/{flags => country}/ie.png (100%) rename public/images/{flags => country}/il.png (100%) rename public/images/{flags => country}/im.png (100%) rename public/images/{flags => country}/in.png (100%) rename public/images/{flags => country}/io.png (100%) rename public/images/{flags => country}/iq.png (100%) rename public/images/{flags => country}/ir.png (100%) rename public/images/{flags => country}/is.png (100%) rename public/images/{flags => country}/it.png (100%) rename public/images/{flags => country}/je.png (100%) rename public/images/{flags => country}/jm.png (100%) rename public/images/{flags => country}/jo.png (100%) rename public/images/{flags => country}/jp.png (100%) rename public/images/{flags => country}/ke.png (100%) rename public/images/{flags => country}/kg.png (100%) rename public/images/{flags => country}/kh.png (100%) rename public/images/{flags => country}/ki.png (100%) rename public/images/{flags => country}/km.png (100%) rename public/images/{flags => country}/kn.png (100%) rename public/images/{flags => country}/kp.png (100%) rename public/images/{flags => country}/kr.png (100%) rename public/images/{flags => country}/kw.png (100%) rename public/images/{flags => country}/ky.png (100%) rename public/images/{flags => country}/kz.png (100%) rename public/images/{flags => country}/la.png (100%) rename public/images/{flags => country}/lb.png (100%) rename public/images/{flags => country}/lc.png (100%) rename public/images/{flags => country}/li.png (100%) rename public/images/{flags => country}/lk.png (100%) rename public/images/{flags => country}/lr.png (100%) rename public/images/{flags => country}/ls.png (100%) rename public/images/{flags => country}/lt.png (100%) rename public/images/{flags => country}/lu.png (100%) rename public/images/{flags => country}/lv.png (100%) rename public/images/{flags => country}/ly.png (100%) rename public/images/{flags => country}/ma.png (100%) rename public/images/{flags => country}/mc.png (100%) rename public/images/{flags => country}/md.png (100%) rename public/images/{flags => country}/me.png (100%) rename public/images/{flags => country}/mf.png (100%) rename public/images/{flags => country}/mg.png (100%) rename public/images/{flags => country}/mh.png (100%) rename public/images/{flags => country}/mk.png (100%) rename public/images/{flags => country}/ml.png (100%) rename public/images/{flags => country}/mm.png (100%) rename public/images/{flags => country}/mn.png (100%) rename public/images/{flags => country}/mo.png (100%) rename public/images/{flags => country}/mp.png (100%) rename public/images/{flags => country}/mq.png (100%) rename public/images/{flags => country}/mr.png (100%) rename public/images/{flags => country}/ms.png (100%) rename public/images/{flags => country}/mt.png (100%) rename public/images/{flags => country}/mu.png (100%) rename public/images/{flags => country}/mv.png (100%) rename public/images/{flags => country}/mw.png (100%) rename public/images/{flags => country}/mx.png (100%) rename public/images/{flags => country}/my.png (100%) rename public/images/{flags => country}/mz.png (100%) rename public/images/{flags => country}/na.png (100%) rename public/images/{flags => country}/nc.png (100%) rename public/images/{flags => country}/ne.png (100%) rename public/images/{flags => country}/nf.png (100%) rename public/images/{flags => country}/ng.png (100%) rename public/images/{flags => country}/ni.png (100%) rename public/images/{flags => country}/nl.png (100%) rename public/images/{flags => country}/no.png (100%) rename public/images/{flags => country}/np.png (100%) rename public/images/{flags => country}/nr.png (100%) rename public/images/{flags => country}/nu.png (100%) rename public/images/{flags => country}/nz.png (100%) rename public/images/{flags => country}/om.png (100%) rename public/images/{flags => country}/pa.png (100%) rename public/images/{flags => country}/pe.png (100%) rename public/images/{flags => country}/pf.png (100%) rename public/images/{flags => country}/pg.png (100%) rename public/images/{flags => country}/ph.png (100%) rename public/images/{flags => country}/pk.png (100%) rename public/images/{flags => country}/pl.png (100%) rename public/images/{flags => country}/pm.png (100%) rename public/images/{flags => country}/pn.png (100%) rename public/images/{flags => country}/pr.png (100%) rename public/images/{flags => country}/ps.png (100%) rename public/images/{flags => country}/pt.png (100%) rename public/images/{flags => country}/pw.png (100%) rename public/images/{flags => country}/py.png (100%) rename public/images/{flags => country}/qa.png (100%) rename public/images/{flags => country}/re.png (100%) rename public/images/{flags => country}/ro.png (100%) rename public/images/{flags => country}/rs.png (100%) rename public/images/{flags => country}/ru.png (100%) rename public/images/{flags => country}/rw.png (100%) rename public/images/{flags => country}/sa.png (100%) rename public/images/{flags => country}/sb.png (100%) rename public/images/{flags => country}/sc.png (100%) rename public/images/{flags => country}/sd.png (100%) rename public/images/{flags => country}/se.png (100%) rename public/images/{flags => country}/sg.png (100%) rename public/images/{flags => country}/sh.png (100%) rename public/images/{flags => country}/si.png (100%) rename public/images/{flags => country}/sj.png (100%) rename public/images/{flags => country}/sk.png (100%) rename public/images/{flags => country}/sl.png (100%) rename public/images/{flags => country}/sm.png (100%) rename public/images/{flags => country}/sn.png (100%) rename public/images/{flags => country}/so.png (100%) rename public/images/{flags => country}/sr.png (100%) rename public/images/{flags => country}/ss.png (100%) rename public/images/{flags => country}/st.png (100%) rename public/images/{flags => country}/sv.png (100%) rename public/images/{flags => country}/sx.png (100%) rename public/images/{flags => country}/sy.png (100%) rename public/images/{flags => country}/sz.png (100%) rename public/images/{flags => country}/tc.png (100%) rename public/images/{flags => country}/td.png (100%) rename public/images/{flags => country}/tf.png (100%) rename public/images/{flags => country}/tg.png (100%) rename public/images/{flags => country}/th.png (100%) rename public/images/{flags => country}/tj.png (100%) rename public/images/{flags => country}/tk.png (100%) rename public/images/{flags => country}/tl.png (100%) rename public/images/{flags => country}/tm.png (100%) rename public/images/{flags => country}/tn.png (100%) rename public/images/{flags => country}/to.png (100%) rename public/images/{flags => country}/tr.png (100%) rename public/images/{flags => country}/tt.png (100%) rename public/images/{flags => country}/tv.png (100%) rename public/images/{flags => country}/tw.png (100%) rename public/images/{flags => country}/tz.png (100%) rename public/images/{flags => country}/ua.png (100%) rename public/images/{flags => country}/ug.png (100%) rename public/images/{flags => country}/um.png (100%) rename public/images/{flags/xx.png => country/unknown.png} (100%) rename public/images/{flags => country}/us.png (100%) rename public/images/{flags => country}/uy.png (100%) rename public/images/{flags => country}/uz.png (100%) rename public/images/{flags => country}/va.png (100%) rename public/images/{flags => country}/vc.png (100%) rename public/images/{flags => country}/ve.png (100%) rename public/images/{flags => country}/vg.png (100%) rename public/images/{flags => country}/vi.png (100%) rename public/images/{flags => country}/vn.png (100%) rename public/images/{flags => country}/vu.png (100%) rename public/images/{flags => country}/wf.png (100%) rename public/images/{flags => country}/ws.png (100%) rename public/images/{flags => country}/xk.png (100%) rename public/images/{flags => country}/ye.png (100%) rename public/images/{flags => country}/yt.png (100%) rename public/images/{flags => country}/za.png (100%) rename public/images/{flags => country}/zm.png (100%) rename public/images/{flags => country}/zw.png (100%) create mode 100644 src/app/(main)/websites/[websiteId]/layout.tsx create mode 100644 src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.module.css create mode 100644 src/components/common/TypeIcon.tsx create mode 100644 src/components/hooks/useRegionNames.ts diff --git a/public/images/browsers/android-webview.png b/public/images/browser/android-webview.png similarity index 100% rename from public/images/browsers/android-webview.png rename to public/images/browser/android-webview.png diff --git a/public/images/browsers/android.png b/public/images/browser/android.png similarity index 100% rename from public/images/browsers/android.png rename to public/images/browser/android.png diff --git a/public/images/browsers/aol.png b/public/images/browser/aol.png similarity index 100% rename from public/images/browsers/aol.png rename to public/images/browser/aol.png diff --git a/public/images/browsers/beaker.png b/public/images/browser/beaker.png similarity index 100% rename from public/images/browsers/beaker.png rename to public/images/browser/beaker.png diff --git a/public/images/browsers/blackberry.png b/public/images/browser/blackberry.png similarity index 100% rename from public/images/browsers/blackberry.png rename to public/images/browser/blackberry.png diff --git a/public/images/browsers/brave.png b/public/images/browser/brave.png similarity index 100% rename from public/images/browsers/brave.png rename to public/images/browser/brave.png diff --git a/public/images/browsers/chrome.png b/public/images/browser/chrome.png similarity index 100% rename from public/images/browsers/chrome.png rename to public/images/browser/chrome.png diff --git a/public/images/browsers/chromium-webview.png b/public/images/browser/chromium-webview.png similarity index 100% rename from public/images/browsers/chromium-webview.png rename to public/images/browser/chromium-webview.png diff --git a/public/images/browsers/crios.png b/public/images/browser/crios.png similarity index 100% rename from public/images/browsers/crios.png rename to public/images/browser/crios.png diff --git a/public/images/browsers/curl.png b/public/images/browser/curl.png similarity index 100% rename from public/images/browsers/curl.png rename to public/images/browser/curl.png diff --git a/public/images/browsers/edge-chromium.png b/public/images/browser/edge-chromium.png similarity index 100% rename from public/images/browsers/edge-chromium.png rename to public/images/browser/edge-chromium.png diff --git a/public/images/browsers/edge-ios.png b/public/images/browser/edge-ios.png similarity index 100% rename from public/images/browsers/edge-ios.png rename to public/images/browser/edge-ios.png diff --git a/public/images/browsers/edge.png b/public/images/browser/edge.png similarity index 100% rename from public/images/browsers/edge.png rename to public/images/browser/edge.png diff --git a/public/images/browsers/facebook.png b/public/images/browser/facebook.png similarity index 100% rename from public/images/browsers/facebook.png rename to public/images/browser/facebook.png diff --git a/public/images/browsers/firefox.png b/public/images/browser/firefox.png similarity index 100% rename from public/images/browsers/firefox.png rename to public/images/browser/firefox.png diff --git a/public/images/browsers/fxios.png b/public/images/browser/fxios.png similarity index 100% rename from public/images/browsers/fxios.png rename to public/images/browser/fxios.png diff --git a/public/images/browsers/ie.png b/public/images/browser/ie.png similarity index 100% rename from public/images/browsers/ie.png rename to public/images/browser/ie.png diff --git a/public/images/browsers/instagram.png b/public/images/browser/instagram.png similarity index 100% rename from public/images/browsers/instagram.png rename to public/images/browser/instagram.png diff --git a/public/images/browsers/ios-webview.png b/public/images/browser/ios-webview.png similarity index 100% rename from public/images/browsers/ios-webview.png rename to public/images/browser/ios-webview.png diff --git a/public/images/browsers/ios.png b/public/images/browser/ios.png similarity index 100% rename from public/images/browsers/ios.png rename to public/images/browser/ios.png diff --git a/public/images/browsers/kakaotalk.png b/public/images/browser/kakaotalk.png similarity index 100% rename from public/images/browsers/kakaotalk.png rename to public/images/browser/kakaotalk.png diff --git a/public/images/browsers/miui.png b/public/images/browser/miui.png similarity index 100% rename from public/images/browsers/miui.png rename to public/images/browser/miui.png diff --git a/public/images/browsers/opera-mini.png b/public/images/browser/opera-mini.png similarity index 100% rename from public/images/browsers/opera-mini.png rename to public/images/browser/opera-mini.png diff --git a/public/images/browsers/opera.png b/public/images/browser/opera.png similarity index 100% rename from public/images/browsers/opera.png rename to public/images/browser/opera.png diff --git a/public/images/browsers/safari.png b/public/images/browser/safari.png similarity index 100% rename from public/images/browsers/safari.png rename to public/images/browser/safari.png diff --git a/public/images/browsers/samsung.png b/public/images/browser/samsung.png similarity index 100% rename from public/images/browsers/samsung.png rename to public/images/browser/samsung.png diff --git a/public/images/browsers/searchbot.png b/public/images/browser/searchbot.png similarity index 100% rename from public/images/browsers/searchbot.png rename to public/images/browser/searchbot.png diff --git a/public/images/browsers/silk.png b/public/images/browser/silk.png similarity index 100% rename from public/images/browsers/silk.png rename to public/images/browser/silk.png diff --git a/public/images/browsers/unknown.png b/public/images/browser/unknown.png similarity index 100% rename from public/images/browsers/unknown.png rename to public/images/browser/unknown.png diff --git a/public/images/browsers/yandexbrowser.png b/public/images/browser/yandexbrowser.png similarity index 100% rename from public/images/browsers/yandexbrowser.png rename to public/images/browser/yandexbrowser.png diff --git a/public/images/flags/ad.png b/public/images/country/ad.png similarity index 100% rename from public/images/flags/ad.png rename to public/images/country/ad.png diff --git a/public/images/flags/ae.png b/public/images/country/ae.png similarity index 100% rename from public/images/flags/ae.png rename to public/images/country/ae.png diff --git a/public/images/flags/af.png b/public/images/country/af.png similarity index 100% rename from public/images/flags/af.png rename to public/images/country/af.png diff --git a/public/images/flags/ag.png b/public/images/country/ag.png similarity index 100% rename from public/images/flags/ag.png rename to public/images/country/ag.png diff --git a/public/images/flags/ai.png b/public/images/country/ai.png similarity index 100% rename from public/images/flags/ai.png rename to public/images/country/ai.png diff --git a/public/images/flags/al.png b/public/images/country/al.png similarity index 100% rename from public/images/flags/al.png rename to public/images/country/al.png diff --git a/public/images/flags/am.png b/public/images/country/am.png similarity index 100% rename from public/images/flags/am.png rename to public/images/country/am.png diff --git a/public/images/flags/ao.png b/public/images/country/ao.png similarity index 100% rename from public/images/flags/ao.png rename to public/images/country/ao.png diff --git a/public/images/flags/aq.png b/public/images/country/aq.png similarity index 100% rename from public/images/flags/aq.png rename to public/images/country/aq.png diff --git a/public/images/flags/ar.png b/public/images/country/ar.png similarity index 100% rename from public/images/flags/ar.png rename to public/images/country/ar.png diff --git a/public/images/flags/as.png b/public/images/country/as.png similarity index 100% rename from public/images/flags/as.png rename to public/images/country/as.png diff --git a/public/images/flags/at.png b/public/images/country/at.png similarity index 100% rename from public/images/flags/at.png rename to public/images/country/at.png diff --git a/public/images/flags/au.png b/public/images/country/au.png similarity index 100% rename from public/images/flags/au.png rename to public/images/country/au.png diff --git a/public/images/flags/aw.png b/public/images/country/aw.png similarity index 100% rename from public/images/flags/aw.png rename to public/images/country/aw.png diff --git a/public/images/flags/ax.png b/public/images/country/ax.png similarity index 100% rename from public/images/flags/ax.png rename to public/images/country/ax.png diff --git a/public/images/flags/az.png b/public/images/country/az.png similarity index 100% rename from public/images/flags/az.png rename to public/images/country/az.png diff --git a/public/images/flags/ba.png b/public/images/country/ba.png similarity index 100% rename from public/images/flags/ba.png rename to public/images/country/ba.png diff --git a/public/images/flags/bb.png b/public/images/country/bb.png similarity index 100% rename from public/images/flags/bb.png rename to public/images/country/bb.png diff --git a/public/images/flags/bd.png b/public/images/country/bd.png similarity index 100% rename from public/images/flags/bd.png rename to public/images/country/bd.png diff --git a/public/images/flags/be.png b/public/images/country/be.png similarity index 100% rename from public/images/flags/be.png rename to public/images/country/be.png diff --git a/public/images/flags/bf.png b/public/images/country/bf.png similarity index 100% rename from public/images/flags/bf.png rename to public/images/country/bf.png diff --git a/public/images/flags/bg.png b/public/images/country/bg.png similarity index 100% rename from public/images/flags/bg.png rename to public/images/country/bg.png diff --git a/public/images/flags/bh.png b/public/images/country/bh.png similarity index 100% rename from public/images/flags/bh.png rename to public/images/country/bh.png diff --git a/public/images/flags/bi.png b/public/images/country/bi.png similarity index 100% rename from public/images/flags/bi.png rename to public/images/country/bi.png diff --git a/public/images/flags/bj.png b/public/images/country/bj.png similarity index 100% rename from public/images/flags/bj.png rename to public/images/country/bj.png diff --git a/public/images/flags/bl.png b/public/images/country/bl.png similarity index 100% rename from public/images/flags/bl.png rename to public/images/country/bl.png diff --git a/public/images/flags/bm.png b/public/images/country/bm.png similarity index 100% rename from public/images/flags/bm.png rename to public/images/country/bm.png diff --git a/public/images/flags/bn.png b/public/images/country/bn.png similarity index 100% rename from public/images/flags/bn.png rename to public/images/country/bn.png diff --git a/public/images/flags/bo.png b/public/images/country/bo.png similarity index 100% rename from public/images/flags/bo.png rename to public/images/country/bo.png diff --git a/public/images/flags/bq.png b/public/images/country/bq.png similarity index 100% rename from public/images/flags/bq.png rename to public/images/country/bq.png diff --git a/public/images/flags/br.png b/public/images/country/br.png similarity index 100% rename from public/images/flags/br.png rename to public/images/country/br.png diff --git a/public/images/flags/bs.png b/public/images/country/bs.png similarity index 100% rename from public/images/flags/bs.png rename to public/images/country/bs.png diff --git a/public/images/flags/bt.png b/public/images/country/bt.png similarity index 100% rename from public/images/flags/bt.png rename to public/images/country/bt.png diff --git a/public/images/flags/bv.png b/public/images/country/bv.png similarity index 100% rename from public/images/flags/bv.png rename to public/images/country/bv.png diff --git a/public/images/flags/bw.png b/public/images/country/bw.png similarity index 100% rename from public/images/flags/bw.png rename to public/images/country/bw.png diff --git a/public/images/flags/by.png b/public/images/country/by.png similarity index 100% rename from public/images/flags/by.png rename to public/images/country/by.png diff --git a/public/images/flags/bz.png b/public/images/country/bz.png similarity index 100% rename from public/images/flags/bz.png rename to public/images/country/bz.png diff --git a/public/images/flags/ca.png b/public/images/country/ca.png similarity index 100% rename from public/images/flags/ca.png rename to public/images/country/ca.png diff --git a/public/images/flags/cc.png b/public/images/country/cc.png similarity index 100% rename from public/images/flags/cc.png rename to public/images/country/cc.png diff --git a/public/images/flags/cd.png b/public/images/country/cd.png similarity index 100% rename from public/images/flags/cd.png rename to public/images/country/cd.png diff --git a/public/images/flags/cf.png b/public/images/country/cf.png similarity index 100% rename from public/images/flags/cf.png rename to public/images/country/cf.png diff --git a/public/images/flags/cg.png b/public/images/country/cg.png similarity index 100% rename from public/images/flags/cg.png rename to public/images/country/cg.png diff --git a/public/images/flags/ch.png b/public/images/country/ch.png similarity index 100% rename from public/images/flags/ch.png rename to public/images/country/ch.png diff --git a/public/images/flags/ci.png b/public/images/country/ci.png similarity index 100% rename from public/images/flags/ci.png rename to public/images/country/ci.png diff --git a/public/images/flags/ck.png b/public/images/country/ck.png similarity index 100% rename from public/images/flags/ck.png rename to public/images/country/ck.png diff --git a/public/images/flags/cl.png b/public/images/country/cl.png similarity index 100% rename from public/images/flags/cl.png rename to public/images/country/cl.png diff --git a/public/images/flags/cm.png b/public/images/country/cm.png similarity index 100% rename from public/images/flags/cm.png rename to public/images/country/cm.png diff --git a/public/images/flags/cn.png b/public/images/country/cn.png similarity index 100% rename from public/images/flags/cn.png rename to public/images/country/cn.png diff --git a/public/images/flags/co.png b/public/images/country/co.png similarity index 100% rename from public/images/flags/co.png rename to public/images/country/co.png diff --git a/public/images/flags/cr.png b/public/images/country/cr.png similarity index 100% rename from public/images/flags/cr.png rename to public/images/country/cr.png diff --git a/public/images/flags/cu.png b/public/images/country/cu.png similarity index 100% rename from public/images/flags/cu.png rename to public/images/country/cu.png diff --git a/public/images/flags/cv.png b/public/images/country/cv.png similarity index 100% rename from public/images/flags/cv.png rename to public/images/country/cv.png diff --git a/public/images/flags/cw.png b/public/images/country/cw.png similarity index 100% rename from public/images/flags/cw.png rename to public/images/country/cw.png diff --git a/public/images/flags/cx.png b/public/images/country/cx.png similarity index 100% rename from public/images/flags/cx.png rename to public/images/country/cx.png diff --git a/public/images/flags/cy.png b/public/images/country/cy.png similarity index 100% rename from public/images/flags/cy.png rename to public/images/country/cy.png diff --git a/public/images/flags/cz.png b/public/images/country/cz.png similarity index 100% rename from public/images/flags/cz.png rename to public/images/country/cz.png diff --git a/public/images/flags/de.png b/public/images/country/de.png similarity index 100% rename from public/images/flags/de.png rename to public/images/country/de.png diff --git a/public/images/flags/dj.png b/public/images/country/dj.png similarity index 100% rename from public/images/flags/dj.png rename to public/images/country/dj.png diff --git a/public/images/flags/dk.png b/public/images/country/dk.png similarity index 100% rename from public/images/flags/dk.png rename to public/images/country/dk.png diff --git a/public/images/flags/dm.png b/public/images/country/dm.png similarity index 100% rename from public/images/flags/dm.png rename to public/images/country/dm.png diff --git a/public/images/flags/do.png b/public/images/country/do.png similarity index 100% rename from public/images/flags/do.png rename to public/images/country/do.png diff --git a/public/images/flags/dz.png b/public/images/country/dz.png similarity index 100% rename from public/images/flags/dz.png rename to public/images/country/dz.png diff --git a/public/images/flags/ec.png b/public/images/country/ec.png similarity index 100% rename from public/images/flags/ec.png rename to public/images/country/ec.png diff --git a/public/images/flags/ee.png b/public/images/country/ee.png similarity index 100% rename from public/images/flags/ee.png rename to public/images/country/ee.png diff --git a/public/images/flags/eg.png b/public/images/country/eg.png similarity index 100% rename from public/images/flags/eg.png rename to public/images/country/eg.png diff --git a/public/images/flags/eh.png b/public/images/country/eh.png similarity index 100% rename from public/images/flags/eh.png rename to public/images/country/eh.png diff --git a/public/images/flags/er.png b/public/images/country/er.png similarity index 100% rename from public/images/flags/er.png rename to public/images/country/er.png diff --git a/public/images/flags/es.png b/public/images/country/es.png similarity index 100% rename from public/images/flags/es.png rename to public/images/country/es.png diff --git a/public/images/flags/et.png b/public/images/country/et.png similarity index 100% rename from public/images/flags/et.png rename to public/images/country/et.png diff --git a/public/images/flags/fi.png b/public/images/country/fi.png similarity index 100% rename from public/images/flags/fi.png rename to public/images/country/fi.png diff --git a/public/images/flags/fj.png b/public/images/country/fj.png similarity index 100% rename from public/images/flags/fj.png rename to public/images/country/fj.png diff --git a/public/images/flags/fk.png b/public/images/country/fk.png similarity index 100% rename from public/images/flags/fk.png rename to public/images/country/fk.png diff --git a/public/images/flags/fm.png b/public/images/country/fm.png similarity index 100% rename from public/images/flags/fm.png rename to public/images/country/fm.png diff --git a/public/images/flags/fo.png b/public/images/country/fo.png similarity index 100% rename from public/images/flags/fo.png rename to public/images/country/fo.png diff --git a/public/images/flags/fr.png b/public/images/country/fr.png similarity index 100% rename from public/images/flags/fr.png rename to public/images/country/fr.png diff --git a/public/images/flags/ga.png b/public/images/country/ga.png similarity index 100% rename from public/images/flags/ga.png rename to public/images/country/ga.png diff --git a/public/images/flags/gb-eng.png b/public/images/country/gb-eng.png similarity index 100% rename from public/images/flags/gb-eng.png rename to public/images/country/gb-eng.png diff --git a/public/images/flags/gb-nir.png b/public/images/country/gb-nir.png similarity index 100% rename from public/images/flags/gb-nir.png rename to public/images/country/gb-nir.png diff --git a/public/images/flags/gb-sct.png b/public/images/country/gb-sct.png similarity index 100% rename from public/images/flags/gb-sct.png rename to public/images/country/gb-sct.png diff --git a/public/images/flags/gb-wls.png b/public/images/country/gb-wls.png similarity index 100% rename from public/images/flags/gb-wls.png rename to public/images/country/gb-wls.png diff --git a/public/images/flags/gb.png b/public/images/country/gb.png similarity index 100% rename from public/images/flags/gb.png rename to public/images/country/gb.png diff --git a/public/images/flags/gd.png b/public/images/country/gd.png similarity index 100% rename from public/images/flags/gd.png rename to public/images/country/gd.png diff --git a/public/images/flags/ge.png b/public/images/country/ge.png similarity index 100% rename from public/images/flags/ge.png rename to public/images/country/ge.png diff --git a/public/images/flags/gf.png b/public/images/country/gf.png similarity index 100% rename from public/images/flags/gf.png rename to public/images/country/gf.png diff --git a/public/images/flags/gg.png b/public/images/country/gg.png similarity index 100% rename from public/images/flags/gg.png rename to public/images/country/gg.png diff --git a/public/images/flags/gh.png b/public/images/country/gh.png similarity index 100% rename from public/images/flags/gh.png rename to public/images/country/gh.png diff --git a/public/images/flags/gi.png b/public/images/country/gi.png similarity index 100% rename from public/images/flags/gi.png rename to public/images/country/gi.png diff --git a/public/images/flags/gl.png b/public/images/country/gl.png similarity index 100% rename from public/images/flags/gl.png rename to public/images/country/gl.png diff --git a/public/images/flags/gm.png b/public/images/country/gm.png similarity index 100% rename from public/images/flags/gm.png rename to public/images/country/gm.png diff --git a/public/images/flags/gn.png b/public/images/country/gn.png similarity index 100% rename from public/images/flags/gn.png rename to public/images/country/gn.png diff --git a/public/images/flags/gp.png b/public/images/country/gp.png similarity index 100% rename from public/images/flags/gp.png rename to public/images/country/gp.png diff --git a/public/images/flags/gq.png b/public/images/country/gq.png similarity index 100% rename from public/images/flags/gq.png rename to public/images/country/gq.png diff --git a/public/images/flags/gr.png b/public/images/country/gr.png similarity index 100% rename from public/images/flags/gr.png rename to public/images/country/gr.png diff --git a/public/images/flags/gs.png b/public/images/country/gs.png similarity index 100% rename from public/images/flags/gs.png rename to public/images/country/gs.png diff --git a/public/images/flags/gt.png b/public/images/country/gt.png similarity index 100% rename from public/images/flags/gt.png rename to public/images/country/gt.png diff --git a/public/images/flags/gu.png b/public/images/country/gu.png similarity index 100% rename from public/images/flags/gu.png rename to public/images/country/gu.png diff --git a/public/images/flags/gw.png b/public/images/country/gw.png similarity index 100% rename from public/images/flags/gw.png rename to public/images/country/gw.png diff --git a/public/images/flags/gy.png b/public/images/country/gy.png similarity index 100% rename from public/images/flags/gy.png rename to public/images/country/gy.png diff --git a/public/images/flags/hk.png b/public/images/country/hk.png similarity index 100% rename from public/images/flags/hk.png rename to public/images/country/hk.png diff --git a/public/images/flags/hm.png b/public/images/country/hm.png similarity index 100% rename from public/images/flags/hm.png rename to public/images/country/hm.png diff --git a/public/images/flags/hn.png b/public/images/country/hn.png similarity index 100% rename from public/images/flags/hn.png rename to public/images/country/hn.png diff --git a/public/images/flags/hr.png b/public/images/country/hr.png similarity index 100% rename from public/images/flags/hr.png rename to public/images/country/hr.png diff --git a/public/images/flags/ht.png b/public/images/country/ht.png similarity index 100% rename from public/images/flags/ht.png rename to public/images/country/ht.png diff --git a/public/images/flags/hu.png b/public/images/country/hu.png similarity index 100% rename from public/images/flags/hu.png rename to public/images/country/hu.png diff --git a/public/images/flags/id.png b/public/images/country/id.png similarity index 100% rename from public/images/flags/id.png rename to public/images/country/id.png diff --git a/public/images/flags/ie.png b/public/images/country/ie.png similarity index 100% rename from public/images/flags/ie.png rename to public/images/country/ie.png diff --git a/public/images/flags/il.png b/public/images/country/il.png similarity index 100% rename from public/images/flags/il.png rename to public/images/country/il.png diff --git a/public/images/flags/im.png b/public/images/country/im.png similarity index 100% rename from public/images/flags/im.png rename to public/images/country/im.png diff --git a/public/images/flags/in.png b/public/images/country/in.png similarity index 100% rename from public/images/flags/in.png rename to public/images/country/in.png diff --git a/public/images/flags/io.png b/public/images/country/io.png similarity index 100% rename from public/images/flags/io.png rename to public/images/country/io.png diff --git a/public/images/flags/iq.png b/public/images/country/iq.png similarity index 100% rename from public/images/flags/iq.png rename to public/images/country/iq.png diff --git a/public/images/flags/ir.png b/public/images/country/ir.png similarity index 100% rename from public/images/flags/ir.png rename to public/images/country/ir.png diff --git a/public/images/flags/is.png b/public/images/country/is.png similarity index 100% rename from public/images/flags/is.png rename to public/images/country/is.png diff --git a/public/images/flags/it.png b/public/images/country/it.png similarity index 100% rename from public/images/flags/it.png rename to public/images/country/it.png diff --git a/public/images/flags/je.png b/public/images/country/je.png similarity index 100% rename from public/images/flags/je.png rename to public/images/country/je.png diff --git a/public/images/flags/jm.png b/public/images/country/jm.png similarity index 100% rename from public/images/flags/jm.png rename to public/images/country/jm.png diff --git a/public/images/flags/jo.png b/public/images/country/jo.png similarity index 100% rename from public/images/flags/jo.png rename to public/images/country/jo.png diff --git a/public/images/flags/jp.png b/public/images/country/jp.png similarity index 100% rename from public/images/flags/jp.png rename to public/images/country/jp.png diff --git a/public/images/flags/ke.png b/public/images/country/ke.png similarity index 100% rename from public/images/flags/ke.png rename to public/images/country/ke.png diff --git a/public/images/flags/kg.png b/public/images/country/kg.png similarity index 100% rename from public/images/flags/kg.png rename to public/images/country/kg.png diff --git a/public/images/flags/kh.png b/public/images/country/kh.png similarity index 100% rename from public/images/flags/kh.png rename to public/images/country/kh.png diff --git a/public/images/flags/ki.png b/public/images/country/ki.png similarity index 100% rename from public/images/flags/ki.png rename to public/images/country/ki.png diff --git a/public/images/flags/km.png b/public/images/country/km.png similarity index 100% rename from public/images/flags/km.png rename to public/images/country/km.png diff --git a/public/images/flags/kn.png b/public/images/country/kn.png similarity index 100% rename from public/images/flags/kn.png rename to public/images/country/kn.png diff --git a/public/images/flags/kp.png b/public/images/country/kp.png similarity index 100% rename from public/images/flags/kp.png rename to public/images/country/kp.png diff --git a/public/images/flags/kr.png b/public/images/country/kr.png similarity index 100% rename from public/images/flags/kr.png rename to public/images/country/kr.png diff --git a/public/images/flags/kw.png b/public/images/country/kw.png similarity index 100% rename from public/images/flags/kw.png rename to public/images/country/kw.png diff --git a/public/images/flags/ky.png b/public/images/country/ky.png similarity index 100% rename from public/images/flags/ky.png rename to public/images/country/ky.png diff --git a/public/images/flags/kz.png b/public/images/country/kz.png similarity index 100% rename from public/images/flags/kz.png rename to public/images/country/kz.png diff --git a/public/images/flags/la.png b/public/images/country/la.png similarity index 100% rename from public/images/flags/la.png rename to public/images/country/la.png diff --git a/public/images/flags/lb.png b/public/images/country/lb.png similarity index 100% rename from public/images/flags/lb.png rename to public/images/country/lb.png diff --git a/public/images/flags/lc.png b/public/images/country/lc.png similarity index 100% rename from public/images/flags/lc.png rename to public/images/country/lc.png diff --git a/public/images/flags/li.png b/public/images/country/li.png similarity index 100% rename from public/images/flags/li.png rename to public/images/country/li.png diff --git a/public/images/flags/lk.png b/public/images/country/lk.png similarity index 100% rename from public/images/flags/lk.png rename to public/images/country/lk.png diff --git a/public/images/flags/lr.png b/public/images/country/lr.png similarity index 100% rename from public/images/flags/lr.png rename to public/images/country/lr.png diff --git a/public/images/flags/ls.png b/public/images/country/ls.png similarity index 100% rename from public/images/flags/ls.png rename to public/images/country/ls.png diff --git a/public/images/flags/lt.png b/public/images/country/lt.png similarity index 100% rename from public/images/flags/lt.png rename to public/images/country/lt.png diff --git a/public/images/flags/lu.png b/public/images/country/lu.png similarity index 100% rename from public/images/flags/lu.png rename to public/images/country/lu.png diff --git a/public/images/flags/lv.png b/public/images/country/lv.png similarity index 100% rename from public/images/flags/lv.png rename to public/images/country/lv.png diff --git a/public/images/flags/ly.png b/public/images/country/ly.png similarity index 100% rename from public/images/flags/ly.png rename to public/images/country/ly.png diff --git a/public/images/flags/ma.png b/public/images/country/ma.png similarity index 100% rename from public/images/flags/ma.png rename to public/images/country/ma.png diff --git a/public/images/flags/mc.png b/public/images/country/mc.png similarity index 100% rename from public/images/flags/mc.png rename to public/images/country/mc.png diff --git a/public/images/flags/md.png b/public/images/country/md.png similarity index 100% rename from public/images/flags/md.png rename to public/images/country/md.png diff --git a/public/images/flags/me.png b/public/images/country/me.png similarity index 100% rename from public/images/flags/me.png rename to public/images/country/me.png diff --git a/public/images/flags/mf.png b/public/images/country/mf.png similarity index 100% rename from public/images/flags/mf.png rename to public/images/country/mf.png diff --git a/public/images/flags/mg.png b/public/images/country/mg.png similarity index 100% rename from public/images/flags/mg.png rename to public/images/country/mg.png diff --git a/public/images/flags/mh.png b/public/images/country/mh.png similarity index 100% rename from public/images/flags/mh.png rename to public/images/country/mh.png diff --git a/public/images/flags/mk.png b/public/images/country/mk.png similarity index 100% rename from public/images/flags/mk.png rename to public/images/country/mk.png diff --git a/public/images/flags/ml.png b/public/images/country/ml.png similarity index 100% rename from public/images/flags/ml.png rename to public/images/country/ml.png diff --git a/public/images/flags/mm.png b/public/images/country/mm.png similarity index 100% rename from public/images/flags/mm.png rename to public/images/country/mm.png diff --git a/public/images/flags/mn.png b/public/images/country/mn.png similarity index 100% rename from public/images/flags/mn.png rename to public/images/country/mn.png diff --git a/public/images/flags/mo.png b/public/images/country/mo.png similarity index 100% rename from public/images/flags/mo.png rename to public/images/country/mo.png diff --git a/public/images/flags/mp.png b/public/images/country/mp.png similarity index 100% rename from public/images/flags/mp.png rename to public/images/country/mp.png diff --git a/public/images/flags/mq.png b/public/images/country/mq.png similarity index 100% rename from public/images/flags/mq.png rename to public/images/country/mq.png diff --git a/public/images/flags/mr.png b/public/images/country/mr.png similarity index 100% rename from public/images/flags/mr.png rename to public/images/country/mr.png diff --git a/public/images/flags/ms.png b/public/images/country/ms.png similarity index 100% rename from public/images/flags/ms.png rename to public/images/country/ms.png diff --git a/public/images/flags/mt.png b/public/images/country/mt.png similarity index 100% rename from public/images/flags/mt.png rename to public/images/country/mt.png diff --git a/public/images/flags/mu.png b/public/images/country/mu.png similarity index 100% rename from public/images/flags/mu.png rename to public/images/country/mu.png diff --git a/public/images/flags/mv.png b/public/images/country/mv.png similarity index 100% rename from public/images/flags/mv.png rename to public/images/country/mv.png diff --git a/public/images/flags/mw.png b/public/images/country/mw.png similarity index 100% rename from public/images/flags/mw.png rename to public/images/country/mw.png diff --git a/public/images/flags/mx.png b/public/images/country/mx.png similarity index 100% rename from public/images/flags/mx.png rename to public/images/country/mx.png diff --git a/public/images/flags/my.png b/public/images/country/my.png similarity index 100% rename from public/images/flags/my.png rename to public/images/country/my.png diff --git a/public/images/flags/mz.png b/public/images/country/mz.png similarity index 100% rename from public/images/flags/mz.png rename to public/images/country/mz.png diff --git a/public/images/flags/na.png b/public/images/country/na.png similarity index 100% rename from public/images/flags/na.png rename to public/images/country/na.png diff --git a/public/images/flags/nc.png b/public/images/country/nc.png similarity index 100% rename from public/images/flags/nc.png rename to public/images/country/nc.png diff --git a/public/images/flags/ne.png b/public/images/country/ne.png similarity index 100% rename from public/images/flags/ne.png rename to public/images/country/ne.png diff --git a/public/images/flags/nf.png b/public/images/country/nf.png similarity index 100% rename from public/images/flags/nf.png rename to public/images/country/nf.png diff --git a/public/images/flags/ng.png b/public/images/country/ng.png similarity index 100% rename from public/images/flags/ng.png rename to public/images/country/ng.png diff --git a/public/images/flags/ni.png b/public/images/country/ni.png similarity index 100% rename from public/images/flags/ni.png rename to public/images/country/ni.png diff --git a/public/images/flags/nl.png b/public/images/country/nl.png similarity index 100% rename from public/images/flags/nl.png rename to public/images/country/nl.png diff --git a/public/images/flags/no.png b/public/images/country/no.png similarity index 100% rename from public/images/flags/no.png rename to public/images/country/no.png diff --git a/public/images/flags/np.png b/public/images/country/np.png similarity index 100% rename from public/images/flags/np.png rename to public/images/country/np.png diff --git a/public/images/flags/nr.png b/public/images/country/nr.png similarity index 100% rename from public/images/flags/nr.png rename to public/images/country/nr.png diff --git a/public/images/flags/nu.png b/public/images/country/nu.png similarity index 100% rename from public/images/flags/nu.png rename to public/images/country/nu.png diff --git a/public/images/flags/nz.png b/public/images/country/nz.png similarity index 100% rename from public/images/flags/nz.png rename to public/images/country/nz.png diff --git a/public/images/flags/om.png b/public/images/country/om.png similarity index 100% rename from public/images/flags/om.png rename to public/images/country/om.png diff --git a/public/images/flags/pa.png b/public/images/country/pa.png similarity index 100% rename from public/images/flags/pa.png rename to public/images/country/pa.png diff --git a/public/images/flags/pe.png b/public/images/country/pe.png similarity index 100% rename from public/images/flags/pe.png rename to public/images/country/pe.png diff --git a/public/images/flags/pf.png b/public/images/country/pf.png similarity index 100% rename from public/images/flags/pf.png rename to public/images/country/pf.png diff --git a/public/images/flags/pg.png b/public/images/country/pg.png similarity index 100% rename from public/images/flags/pg.png rename to public/images/country/pg.png diff --git a/public/images/flags/ph.png b/public/images/country/ph.png similarity index 100% rename from public/images/flags/ph.png rename to public/images/country/ph.png diff --git a/public/images/flags/pk.png b/public/images/country/pk.png similarity index 100% rename from public/images/flags/pk.png rename to public/images/country/pk.png diff --git a/public/images/flags/pl.png b/public/images/country/pl.png similarity index 100% rename from public/images/flags/pl.png rename to public/images/country/pl.png diff --git a/public/images/flags/pm.png b/public/images/country/pm.png similarity index 100% rename from public/images/flags/pm.png rename to public/images/country/pm.png diff --git a/public/images/flags/pn.png b/public/images/country/pn.png similarity index 100% rename from public/images/flags/pn.png rename to public/images/country/pn.png diff --git a/public/images/flags/pr.png b/public/images/country/pr.png similarity index 100% rename from public/images/flags/pr.png rename to public/images/country/pr.png diff --git a/public/images/flags/ps.png b/public/images/country/ps.png similarity index 100% rename from public/images/flags/ps.png rename to public/images/country/ps.png diff --git a/public/images/flags/pt.png b/public/images/country/pt.png similarity index 100% rename from public/images/flags/pt.png rename to public/images/country/pt.png diff --git a/public/images/flags/pw.png b/public/images/country/pw.png similarity index 100% rename from public/images/flags/pw.png rename to public/images/country/pw.png diff --git a/public/images/flags/py.png b/public/images/country/py.png similarity index 100% rename from public/images/flags/py.png rename to public/images/country/py.png diff --git a/public/images/flags/qa.png b/public/images/country/qa.png similarity index 100% rename from public/images/flags/qa.png rename to public/images/country/qa.png diff --git a/public/images/flags/re.png b/public/images/country/re.png similarity index 100% rename from public/images/flags/re.png rename to public/images/country/re.png diff --git a/public/images/flags/ro.png b/public/images/country/ro.png similarity index 100% rename from public/images/flags/ro.png rename to public/images/country/ro.png diff --git a/public/images/flags/rs.png b/public/images/country/rs.png similarity index 100% rename from public/images/flags/rs.png rename to public/images/country/rs.png diff --git a/public/images/flags/ru.png b/public/images/country/ru.png similarity index 100% rename from public/images/flags/ru.png rename to public/images/country/ru.png diff --git a/public/images/flags/rw.png b/public/images/country/rw.png similarity index 100% rename from public/images/flags/rw.png rename to public/images/country/rw.png diff --git a/public/images/flags/sa.png b/public/images/country/sa.png similarity index 100% rename from public/images/flags/sa.png rename to public/images/country/sa.png diff --git a/public/images/flags/sb.png b/public/images/country/sb.png similarity index 100% rename from public/images/flags/sb.png rename to public/images/country/sb.png diff --git a/public/images/flags/sc.png b/public/images/country/sc.png similarity index 100% rename from public/images/flags/sc.png rename to public/images/country/sc.png diff --git a/public/images/flags/sd.png b/public/images/country/sd.png similarity index 100% rename from public/images/flags/sd.png rename to public/images/country/sd.png diff --git a/public/images/flags/se.png b/public/images/country/se.png similarity index 100% rename from public/images/flags/se.png rename to public/images/country/se.png diff --git a/public/images/flags/sg.png b/public/images/country/sg.png similarity index 100% rename from public/images/flags/sg.png rename to public/images/country/sg.png diff --git a/public/images/flags/sh.png b/public/images/country/sh.png similarity index 100% rename from public/images/flags/sh.png rename to public/images/country/sh.png diff --git a/public/images/flags/si.png b/public/images/country/si.png similarity index 100% rename from public/images/flags/si.png rename to public/images/country/si.png diff --git a/public/images/flags/sj.png b/public/images/country/sj.png similarity index 100% rename from public/images/flags/sj.png rename to public/images/country/sj.png diff --git a/public/images/flags/sk.png b/public/images/country/sk.png similarity index 100% rename from public/images/flags/sk.png rename to public/images/country/sk.png diff --git a/public/images/flags/sl.png b/public/images/country/sl.png similarity index 100% rename from public/images/flags/sl.png rename to public/images/country/sl.png diff --git a/public/images/flags/sm.png b/public/images/country/sm.png similarity index 100% rename from public/images/flags/sm.png rename to public/images/country/sm.png diff --git a/public/images/flags/sn.png b/public/images/country/sn.png similarity index 100% rename from public/images/flags/sn.png rename to public/images/country/sn.png diff --git a/public/images/flags/so.png b/public/images/country/so.png similarity index 100% rename from public/images/flags/so.png rename to public/images/country/so.png diff --git a/public/images/flags/sr.png b/public/images/country/sr.png similarity index 100% rename from public/images/flags/sr.png rename to public/images/country/sr.png diff --git a/public/images/flags/ss.png b/public/images/country/ss.png similarity index 100% rename from public/images/flags/ss.png rename to public/images/country/ss.png diff --git a/public/images/flags/st.png b/public/images/country/st.png similarity index 100% rename from public/images/flags/st.png rename to public/images/country/st.png diff --git a/public/images/flags/sv.png b/public/images/country/sv.png similarity index 100% rename from public/images/flags/sv.png rename to public/images/country/sv.png diff --git a/public/images/flags/sx.png b/public/images/country/sx.png similarity index 100% rename from public/images/flags/sx.png rename to public/images/country/sx.png diff --git a/public/images/flags/sy.png b/public/images/country/sy.png similarity index 100% rename from public/images/flags/sy.png rename to public/images/country/sy.png diff --git a/public/images/flags/sz.png b/public/images/country/sz.png similarity index 100% rename from public/images/flags/sz.png rename to public/images/country/sz.png diff --git a/public/images/flags/tc.png b/public/images/country/tc.png similarity index 100% rename from public/images/flags/tc.png rename to public/images/country/tc.png diff --git a/public/images/flags/td.png b/public/images/country/td.png similarity index 100% rename from public/images/flags/td.png rename to public/images/country/td.png diff --git a/public/images/flags/tf.png b/public/images/country/tf.png similarity index 100% rename from public/images/flags/tf.png rename to public/images/country/tf.png diff --git a/public/images/flags/tg.png b/public/images/country/tg.png similarity index 100% rename from public/images/flags/tg.png rename to public/images/country/tg.png diff --git a/public/images/flags/th.png b/public/images/country/th.png similarity index 100% rename from public/images/flags/th.png rename to public/images/country/th.png diff --git a/public/images/flags/tj.png b/public/images/country/tj.png similarity index 100% rename from public/images/flags/tj.png rename to public/images/country/tj.png diff --git a/public/images/flags/tk.png b/public/images/country/tk.png similarity index 100% rename from public/images/flags/tk.png rename to public/images/country/tk.png diff --git a/public/images/flags/tl.png b/public/images/country/tl.png similarity index 100% rename from public/images/flags/tl.png rename to public/images/country/tl.png diff --git a/public/images/flags/tm.png b/public/images/country/tm.png similarity index 100% rename from public/images/flags/tm.png rename to public/images/country/tm.png diff --git a/public/images/flags/tn.png b/public/images/country/tn.png similarity index 100% rename from public/images/flags/tn.png rename to public/images/country/tn.png diff --git a/public/images/flags/to.png b/public/images/country/to.png similarity index 100% rename from public/images/flags/to.png rename to public/images/country/to.png diff --git a/public/images/flags/tr.png b/public/images/country/tr.png similarity index 100% rename from public/images/flags/tr.png rename to public/images/country/tr.png diff --git a/public/images/flags/tt.png b/public/images/country/tt.png similarity index 100% rename from public/images/flags/tt.png rename to public/images/country/tt.png diff --git a/public/images/flags/tv.png b/public/images/country/tv.png similarity index 100% rename from public/images/flags/tv.png rename to public/images/country/tv.png diff --git a/public/images/flags/tw.png b/public/images/country/tw.png similarity index 100% rename from public/images/flags/tw.png rename to public/images/country/tw.png diff --git a/public/images/flags/tz.png b/public/images/country/tz.png similarity index 100% rename from public/images/flags/tz.png rename to public/images/country/tz.png diff --git a/public/images/flags/ua.png b/public/images/country/ua.png similarity index 100% rename from public/images/flags/ua.png rename to public/images/country/ua.png diff --git a/public/images/flags/ug.png b/public/images/country/ug.png similarity index 100% rename from public/images/flags/ug.png rename to public/images/country/ug.png diff --git a/public/images/flags/um.png b/public/images/country/um.png similarity index 100% rename from public/images/flags/um.png rename to public/images/country/um.png diff --git a/public/images/flags/xx.png b/public/images/country/unknown.png similarity index 100% rename from public/images/flags/xx.png rename to public/images/country/unknown.png diff --git a/public/images/flags/us.png b/public/images/country/us.png similarity index 100% rename from public/images/flags/us.png rename to public/images/country/us.png diff --git a/public/images/flags/uy.png b/public/images/country/uy.png similarity index 100% rename from public/images/flags/uy.png rename to public/images/country/uy.png diff --git a/public/images/flags/uz.png b/public/images/country/uz.png similarity index 100% rename from public/images/flags/uz.png rename to public/images/country/uz.png diff --git a/public/images/flags/va.png b/public/images/country/va.png similarity index 100% rename from public/images/flags/va.png rename to public/images/country/va.png diff --git a/public/images/flags/vc.png b/public/images/country/vc.png similarity index 100% rename from public/images/flags/vc.png rename to public/images/country/vc.png diff --git a/public/images/flags/ve.png b/public/images/country/ve.png similarity index 100% rename from public/images/flags/ve.png rename to public/images/country/ve.png diff --git a/public/images/flags/vg.png b/public/images/country/vg.png similarity index 100% rename from public/images/flags/vg.png rename to public/images/country/vg.png diff --git a/public/images/flags/vi.png b/public/images/country/vi.png similarity index 100% rename from public/images/flags/vi.png rename to public/images/country/vi.png diff --git a/public/images/flags/vn.png b/public/images/country/vn.png similarity index 100% rename from public/images/flags/vn.png rename to public/images/country/vn.png diff --git a/public/images/flags/vu.png b/public/images/country/vu.png similarity index 100% rename from public/images/flags/vu.png rename to public/images/country/vu.png diff --git a/public/images/flags/wf.png b/public/images/country/wf.png similarity index 100% rename from public/images/flags/wf.png rename to public/images/country/wf.png diff --git a/public/images/flags/ws.png b/public/images/country/ws.png similarity index 100% rename from public/images/flags/ws.png rename to public/images/country/ws.png diff --git a/public/images/flags/xk.png b/public/images/country/xk.png similarity index 100% rename from public/images/flags/xk.png rename to public/images/country/xk.png diff --git a/public/images/flags/ye.png b/public/images/country/ye.png similarity index 100% rename from public/images/flags/ye.png rename to public/images/country/ye.png diff --git a/public/images/flags/yt.png b/public/images/country/yt.png similarity index 100% rename from public/images/flags/yt.png rename to public/images/country/yt.png diff --git a/public/images/flags/za.png b/public/images/country/za.png similarity index 100% rename from public/images/flags/za.png rename to public/images/country/za.png diff --git a/public/images/flags/zm.png b/public/images/country/zm.png similarity index 100% rename from public/images/flags/zm.png rename to public/images/country/zm.png diff --git a/public/images/flags/zw.png b/public/images/country/zw.png similarity index 100% rename from public/images/flags/zw.png rename to public/images/country/zw.png diff --git a/src/app/(main)/websites/[websiteId]/WebsiteDetailsPage.tsx b/src/app/(main)/websites/[websiteId]/WebsiteDetailsPage.tsx index 2439040af3..3eeeb18fdb 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteDetailsPage.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteDetailsPage.tsx @@ -7,7 +7,6 @@ import WebsiteExpandedView from './WebsiteExpandedView'; import WebsiteHeader from './WebsiteHeader'; import WebsiteMetricsBar from './WebsiteMetricsBar'; import WebsiteTableView from './WebsiteTableView'; -import WebsiteProvider from './WebsiteProvider'; import { FILTER_COLUMNS } from 'lib/constants'; export default function WebsiteDetailsPage({ websiteId }: { websiteId: string }) { @@ -25,13 +24,13 @@ export default function WebsiteDetailsPage({ websiteId }: { websiteId: string }) }, {}); return ( - + <> {!view && } {view && } - + ); } diff --git a/src/app/(main)/websites/[websiteId]/WebsiteProvider.tsx b/src/app/(main)/websites/[websiteId]/WebsiteProvider.tsx index 76d40b704f..3cdfdd5df2 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteProvider.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteProvider.tsx @@ -1,3 +1,4 @@ +'use client'; import { createContext, ReactNode, useEffect } from 'react'; import { useModified, useWebsite } from 'components/hooks'; import { Loading } from 'react-basics'; diff --git a/src/app/(main)/websites/[websiteId]/compare/WebsiteComparePage.tsx b/src/app/(main)/websites/[websiteId]/compare/WebsiteComparePage.tsx index 1455b76fac..21cd65972f 100644 --- a/src/app/(main)/websites/[websiteId]/compare/WebsiteComparePage.tsx +++ b/src/app/(main)/websites/[websiteId]/compare/WebsiteComparePage.tsx @@ -6,7 +6,6 @@ import { useNavigation } from 'components/hooks'; import { FILTER_COLUMNS } from 'lib/constants'; import WebsiteChart from '../WebsiteChart'; import WebsiteCompareTables from './WebsiteCompareTables'; -import WebsiteProvider from '../WebsiteProvider'; export function WebsiteComparePage({ websiteId }) { const { query } = useNavigation(); @@ -19,13 +18,13 @@ export function WebsiteComparePage({ websiteId }) { }, {}); return ( - + <> - + ); } diff --git a/src/app/(main)/websites/[websiteId]/layout.tsx b/src/app/(main)/websites/[websiteId]/layout.tsx new file mode 100644 index 0000000000..1729e3ed9f --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/layout.tsx @@ -0,0 +1,17 @@ +import { Metadata } from 'next'; +import WebsiteProvider from './WebsiteProvider'; + +export default function ({ children, params: { websiteId } }) { + if (process.env.cloudMode) { + return null; + } + + return {children}; +} + +export const metadata: Metadata = { + title: { + template: '%s | Umami', + default: 'Websites | Umami', + }, +}; diff --git a/src/app/(main)/websites/[websiteId]/realtime/RealtimeCountries.tsx b/src/app/(main)/websites/[websiteId]/realtime/RealtimeCountries.tsx index 2dd69146d7..ae4967dab9 100644 --- a/src/app/(main)/websites/[websiteId]/realtime/RealtimeCountries.tsx +++ b/src/app/(main)/websites/[websiteId]/realtime/RealtimeCountries.tsx @@ -3,19 +3,17 @@ import ListTable from 'components/metrics/ListTable'; import { useLocale, useCountryNames, useMessages } from 'components/hooks'; import classNames from 'classnames'; import styles from './RealtimeCountries.module.css'; +import TypeIcon from 'components/common/TypeIcon'; export function RealtimeCountries({ data }) { const { formatMessage, labels } = useMessages(); const { locale } = useLocale(); - const countryNames = useCountryNames(locale); + const { countryNames } = useCountryNames(locale); const renderCountryName = useCallback( ({ x: code }) => ( - {code} + {countryNames[code]} ), diff --git a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx index 97fb16597b..ad0314e7ad 100644 --- a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx +++ b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx @@ -32,7 +32,7 @@ export function RealtimeLog({ data }: { data: RealtimeData }) { const { formatMessage, labels, messages, FormattedMessage } = useMessages(); const { formatValue } = useFormat(); const { locale } = useLocale(); - const countryNames = useCountryNames(locale); + const { countryNames } = useCountryNames(locale); const [filter, setFilter] = useState(TYPE_ALL); const buttons = [ diff --git a/src/app/(main)/websites/[websiteId]/realtime/WebsiteRealtimePage.tsx b/src/app/(main)/websites/[websiteId]/realtime/WebsiteRealtimePage.tsx index d88695478d..7030cc32e9 100644 --- a/src/app/(main)/websites/[websiteId]/realtime/WebsiteRealtimePage.tsx +++ b/src/app/(main)/websites/[websiteId]/realtime/WebsiteRealtimePage.tsx @@ -10,7 +10,6 @@ import RealtimeHeader from './RealtimeHeader'; import RealtimeUrls from './RealtimeUrls'; import RealtimeCountries from './RealtimeCountries'; import WebsiteHeader from '../WebsiteHeader'; -import WebsiteProvider from '../WebsiteProvider'; import { percentFilter } from 'lib/filters'; export function WebsiteRealtimePage({ websiteId }) { @@ -27,7 +26,7 @@ export function WebsiteRealtimePage({ websiteId }) { ); return ( - + <> @@ -41,7 +40,7 @@ export function WebsiteRealtimePage({ websiteId }) { - + ); } diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx index 7bae8b6838..d4cf827a0a 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx @@ -1,7 +1,6 @@ import Link from 'next/link'; import { GridColumn, GridTable, useBreakpoint } from 'react-basics'; import { useFormat, useMessages } from 'components/hooks'; -import { formatDistanceToNow } from 'date-fns'; import Profile from 'components/common/Profile'; export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean }) { @@ -12,14 +11,10 @@ export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean return ( - {row => } + {row => } - {row => ( - - {row.id} ({row.firstAt !== row.lastAt ? 'YES' : 'NO'}) - - )} + {row => {row.id}} {row => formatValue(row.country, 'country')} @@ -32,8 +27,8 @@ export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean {row => formatValue(row.device, 'device')} - - {row => formatDistanceToNow(new Date(row.createdAt))} + + {row => row.lastAt} ); diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.module.css b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.module.css index 2f71e9a885..21bb011aad 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.module.css +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.module.css @@ -1,3 +1,18 @@ .page { display: grid; + grid-template-columns: 300px 1fr; +} + +.sidebar { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 20px; + padding-right: 20px; + border-right: 1px solid var(--base300); +} + +.content { + padding-left: 20px; } diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx index fc6945ceae..5aaac9525d 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx @@ -3,6 +3,7 @@ import WebsiteHeader from '../../WebsiteHeader'; import SessionInfo from './SessionInfo'; import { useSession } from 'components/hooks'; import { Loading } from 'react-basics'; +import Profile from 'components/common/Profile'; import styles from './SessionDetailsPage.module.css'; export default function SessionDetailsPage({ @@ -19,9 +20,15 @@ export default function SessionDetailsPage({ } return ( -
+ <> - -
+
+
+ + +
+
oh hi.
+
+ ); } diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.module.css b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.module.css new file mode 100644 index 0000000000..c17eadf5f4 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.module.css @@ -0,0 +1,17 @@ +.info { + display: grid; + gap: 10px; +} + +.info dt { + color: var(--font-color200); + font-weight: bold; +} + +.info dd { + display: flex; + gap: 10px; + align-items: center; + margin: 5px 0 28px; + text-align: left; +} diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.tsx index 051703a3eb..cae200ab9e 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.tsx @@ -1,17 +1,62 @@ -import Profile from 'components/common/Profile'; +import { format } from 'date-fns'; +import { useFormat, useLocale, useMessages, useRegionNames } from 'components/hooks'; +import TypeIcon from 'components/common/TypeIcon'; +import { Icon, CopyIcon } from 'react-basics'; +import Icons from 'components/icons'; +import styles from './SessionInfo.module.css'; export default function SessionInfo({ data }) { + const { locale } = useLocale(); + const { formatMessage, labels } = useMessages(); + const { formatValue } = useFormat(); + const { getRegionName } = useRegionNames(locale); + return ( -

- +
ID
-
{data?.id}
-
Country
-
{data?.country}
-
City
-
{data?.city}
+
+ {data?.id} +
+
{formatMessage(labels.firstSeen)}
+
{format(new Date(data?.firstAt), 'PPPpp')}
+
{formatMessage(labels.lastSeen)}
+
{format(new Date(data?.lastAt), 'PPPpp')}
+
{formatMessage(labels.country)}
+
+ + {formatValue(data?.country, 'country')} +
+
{formatMessage(labels.region)}
+
+ + + + {getRegionName(data?.subdivision1)} +
+
{formatMessage(labels.city)}
+
+ + + + {data?.city} +
+
{formatMessage(labels.os)}
+
+ + {formatValue(data?.os, 'os')} +
+
{formatMessage(labels.device)}
+
+ + {formatValue(data?.device, 'device')} +
+
{formatMessage(labels.browser)}
+
+ + {formatValue(data?.browser, 'browser')} +
-

+
); } diff --git a/src/components/common/TypeIcon.tsx b/src/components/common/TypeIcon.tsx new file mode 100644 index 0000000000..8952bb2628 --- /dev/null +++ b/src/components/common/TypeIcon.tsx @@ -0,0 +1,18 @@ +export function TypeIcon({ + type, + value, +}: { + type: 'browser' | 'country' | 'device' | 'os'; + value: string; +}) { + return ( + {value} + ); +} + +export default TypeIcon; diff --git a/src/components/hooks/index.ts b/src/components/hooks/index.ts index b0271ff2cd..515255e675 100644 --- a/src/components/hooks/index.ts +++ b/src/components/hooks/index.ts @@ -32,6 +32,7 @@ export * from './useLocale'; export * from './useMessages'; export * from './useModified'; export * from './useNavigation'; +export * from './useRegionNames'; export * from './useSticky'; export * from './useTeamUrl'; export * from './useTheme'; diff --git a/src/components/hooks/useCountryNames.ts b/src/components/hooks/useCountryNames.ts index 17b594b6f3..2bdaa94e92 100644 --- a/src/components/hooks/useCountryNames.ts +++ b/src/components/hooks/useCountryNames.ts @@ -28,7 +28,7 @@ export function useCountryNames(locale: string) { } }, [locale]); - return list; + return { countryNames: list }; } export default useCountryNames; diff --git a/src/components/hooks/useFormat.ts b/src/components/hooks/useFormat.ts index 57edfa5201..631b3f03bb 100644 --- a/src/components/hooks/useFormat.ts +++ b/src/components/hooks/useFormat.ts @@ -7,7 +7,7 @@ import regions from '../../../public/iso-3166-2.json'; export function useFormat() { const { formatMessage, labels } = useMessages(); const { locale } = useLocale(); - const countryNames = useCountryNames(locale); + const { countryNames } = useCountryNames(locale); const formatOS = (value: string): string => { return OS_NAMES[value] || value; diff --git a/src/components/hooks/useRegionNames.ts b/src/components/hooks/useRegionNames.ts new file mode 100644 index 0000000000..1ba7feaa73 --- /dev/null +++ b/src/components/hooks/useRegionNames.ts @@ -0,0 +1,19 @@ +import useCountryNames from './useCountryNames'; +import regions from '../../../public/iso-3166-2.json'; + +export function useRegionNames(locale: string) { + const { countryNames } = useCountryNames(locale); + + const getRegionName = (regionCode: string, countryCode?: string) => { + if (!countryCode) { + return regions[regionCode]; + } + + const region = regionCode.includes('-') ? regionCode : `${countryCode}-${regionCode}`; + return regions[region] ? `${regions[region]}, ${countryNames[countryCode]}` : region; + }; + + return { regionNames: regions, getRegionName }; +} + +export default useRegionNames; diff --git a/src/components/icons.ts b/src/components/icons.ts index 8e5a481cbf..422f042ab2 100644 --- a/src/components/icons.ts +++ b/src/components/icons.ts @@ -11,6 +11,7 @@ import Dashboard from 'assets/dashboard.svg'; import Eye from 'assets/eye.svg'; import Gear from 'assets/gear.svg'; import Globe from 'assets/globe.svg'; +import Location from 'assets/location.svg'; import Lock from 'assets/lock.svg'; import Logo from 'assets/logo.svg'; import Magnet from 'assets/magnet.svg'; @@ -38,6 +39,7 @@ const icons = { Eye, Gear, Globe, + Location, Lock, Logo, Magnet, diff --git a/src/components/messages.ts b/src/components/messages.ts index 6609076235..764348fae0 100644 --- a/src/components/messages.ts +++ b/src/components/messages.ts @@ -272,6 +272,8 @@ export const labels = defineMessages({ previous: { id: 'label.previous', defaultMessage: 'Previous' }, previousPeriod: { id: 'label.previous-period', defaultMessage: 'Previous period' }, previousYear: { id: 'label.previous-year', defaultMessage: 'Previous year' }, + lastSeen: { id: 'label.last-seen', defaultMessage: 'Last seen' }, + firstSeen: { id: 'label.first-seen', defaultMessage: 'First seen' }, }); export const messages = defineMessages({ diff --git a/src/components/metrics/BrowsersTable.tsx b/src/components/metrics/BrowsersTable.tsx index 004b5e43a5..d0cec12453 100644 --- a/src/components/metrics/BrowsersTable.tsx +++ b/src/components/metrics/BrowsersTable.tsx @@ -2,6 +2,7 @@ import FilterLink from 'components/common/FilterLink'; import MetricsTable, { MetricsTableProps } from 'components/metrics/MetricsTable'; import { useMessages } from 'components/hooks'; import { useFormat } from 'components/hooks'; +import TypeIcon from 'components/common/TypeIcon'; export function BrowsersTable(props: MetricsTableProps) { const { formatMessage, labels } = useMessages(); @@ -10,12 +11,7 @@ export function BrowsersTable(props: MetricsTableProps) { function renderLink({ x: browser }) { return ( - {browser} + ); } diff --git a/src/components/metrics/CitiesTable.tsx b/src/components/metrics/CitiesTable.tsx index ee2d100510..b3573b5c36 100644 --- a/src/components/metrics/CitiesTable.tsx +++ b/src/components/metrics/CitiesTable.tsx @@ -1,6 +1,7 @@ import MetricsTable, { MetricsTableProps } from './MetricsTable'; import { emptyFilter } from 'lib/filters'; import FilterLink from 'components/common/FilterLink'; +import TypeIcon from 'components/common/TypeIcon'; import { useLocale } from 'components/hooks'; import { useMessages } from 'components/hooks'; import { useCountryNames } from 'components/hooks'; @@ -8,7 +9,7 @@ import { useCountryNames } from 'components/hooks'; export function CitiesTable(props: MetricsTableProps) { const { locale } = useLocale(); const { formatMessage, labels } = useMessages(); - const countryNames = useCountryNames(locale); + const { countryNames } = useCountryNames(locale); const renderLabel = (city: string, country: string) => { const countryName = countryNames[country]; @@ -18,12 +19,7 @@ export function CitiesTable(props: MetricsTableProps) { const renderLink = ({ x: city, country }) => { return ( - {country && ( - {country} - )} + {country && } ); }; diff --git a/src/components/metrics/CountriesTable.tsx b/src/components/metrics/CountriesTable.tsx index 3e7c0af016..87a7838e2d 100644 --- a/src/components/metrics/CountriesTable.tsx +++ b/src/components/metrics/CountriesTable.tsx @@ -2,6 +2,7 @@ import FilterLink from 'components/common/FilterLink'; import { useCountryNames } from 'components/hooks'; import { useLocale, useMessages, useFormat } from 'components/hooks'; import MetricsTable, { MetricsTableProps } from './MetricsTable'; +import TypeIcon from 'components/common/TypeIcon'; export function CountriesTable({ onDataLoad, @@ -10,7 +11,7 @@ export function CountriesTable({ onDataLoad: (data: any) => void; } & MetricsTableProps) { const { locale } = useLocale(); - const countryNames = useCountryNames(locale); + const { countryNames } = useCountryNames(locale); const { formatMessage, labels } = useMessages(); const { formatCountry } = useFormat(); @@ -26,10 +27,7 @@ export function CountriesTable({ value={countryNames[code] && code} label={formatCountry(code)} > - {code} + ); }; diff --git a/src/components/metrics/DevicesTable.tsx b/src/components/metrics/DevicesTable.tsx index e3db60ea00..f2f3f1aabe 100644 --- a/src/components/metrics/DevicesTable.tsx +++ b/src/components/metrics/DevicesTable.tsx @@ -2,6 +2,7 @@ import MetricsTable, { MetricsTableProps } from './MetricsTable'; import FilterLink from 'components/common/FilterLink'; import { useMessages } from 'components/hooks'; import { useFormat } from 'components/hooks'; +import TypeIcon from 'components/common/TypeIcon'; export function DevicesTable(props: MetricsTableProps) { const { formatMessage, labels } = useMessages(); @@ -10,14 +11,7 @@ export function DevicesTable(props: MetricsTableProps) { function renderLink({ x: device }) { return ( - {device} + ); } diff --git a/src/components/metrics/OSTable.tsx b/src/components/metrics/OSTable.tsx index 7744bf119a..6989504c0d 100644 --- a/src/components/metrics/OSTable.tsx +++ b/src/components/metrics/OSTable.tsx @@ -1,6 +1,7 @@ import MetricsTable, { MetricsTableProps } from './MetricsTable'; import FilterLink from 'components/common/FilterLink'; import { useMessages, useFormat } from 'components/hooks'; +import TypeIcon from 'components/common/TypeIcon'; export function OSTable(props: MetricsTableProps) { const { formatMessage, labels } = useMessages(); @@ -9,14 +10,7 @@ export function OSTable(props: MetricsTableProps) { function renderLink({ x: os }) { return ( - {os} + ); } diff --git a/src/components/metrics/RegionsTable.tsx b/src/components/metrics/RegionsTable.tsx index 6e6d7d96de..215551af8b 100644 --- a/src/components/metrics/RegionsTable.tsx +++ b/src/components/metrics/RegionsTable.tsx @@ -1,28 +1,18 @@ import FilterLink from 'components/common/FilterLink'; import { emptyFilter } from 'lib/filters'; -import { useLocale } from 'components/hooks'; -import { useMessages } from 'components/hooks'; -import { useCountryNames } from 'components/hooks'; +import { useMessages, useLocale, useRegionNames } from 'components/hooks'; import MetricsTable, { MetricsTableProps } from './MetricsTable'; -import regions from '../../../public/iso-3166-2.json'; +import TypeIcon from 'components/common/TypeIcon'; export function RegionsTable(props: MetricsTableProps) { const { locale } = useLocale(); const { formatMessage, labels } = useMessages(); - const countryNames = useCountryNames(locale); - - const renderLabel = (code: string, country: string) => { - const region = code.includes('-') ? code : `${country}-${code}`; - return regions[region] ? `${regions[region]}, ${countryNames[country]}` : region; - }; + const { getRegionName } = useRegionNames(locale); const renderLink = ({ x: code, country }) => { return ( - - {code} + + ); }; diff --git a/src/components/metrics/WorldMap.tsx b/src/components/metrics/WorldMap.tsx index 27d0f57c06..54483270c6 100644 --- a/src/components/metrics/WorldMap.tsx +++ b/src/components/metrics/WorldMap.tsx @@ -17,7 +17,7 @@ export function WorldMap({ data = [], className }: { data?: any[]; className?: s const { theme, colors } = useTheme(); const { locale } = useLocale(); const { formatMessage, labels } = useMessages(); - const countryNames = useCountryNames(locale); + const { countryNames } = useCountryNames(locale); const visitorsLabel = formatMessage(labels.visitors).toLocaleLowerCase(locale); const metrics = useMemo(() => (data ? percentFilter(data) : []), [data]); diff --git a/src/queries/analytics/sessions/getSession.ts b/src/queries/analytics/sessions/getSession.ts index 2c008e30cc..9cdc45d69c 100644 --- a/src/queries/analytics/sessions/getSession.ts +++ b/src/queries/analytics/sessions/getSession.ts @@ -25,8 +25,6 @@ async function clickhouseQuery(websiteId: string, sessionId: string) { select session_id as id, website_id as websiteId, - min(created_at) as firstAt, - max(created_at) as lastAt, hostname, browser, os, @@ -35,7 +33,9 @@ async function clickhouseQuery(websiteId: string, sessionId: string) { language, country, subdivision1, - city + city, + min(created_at) as firstAt, + max(created_at) as lastAt from website_event where website_id = {websiteId:UUID} and session_id = {sessionId:UUID} diff --git a/src/queries/analytics/sessions/getSessions.ts b/src/queries/analytics/sessions/getSessions.ts index e7c9596339..4e632430ae 100644 --- a/src/queries/analytics/sessions/getSessions.ts +++ b/src/queries/analytics/sessions/getSessions.ts @@ -32,7 +32,6 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar select session_id as id, website_id as websiteId, - min(created_at) as createdAt, hostname, browser, os, @@ -41,13 +40,15 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar language, country, subdivision1, - city + city, + min(created_at) as firstAt, + max(created_at) as lastAt from website_event where website_id = {websiteId:UUID} ${dateQuery} ${filterQuery} group by session_id, website_id, hostname, browser, os, device, screen, language, country, subdivision1, city - order by createdAt desc + order by lastAt desc `, params, pageParams, From f32bf0a2e034dfa014e4d92192ee298a75cae3ea Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Mon, 29 Jul 2024 19:09:13 -0700 Subject: [PATCH 049/132] Session details screen. --- .../sessions/SessionsDataTable.tsx | 4 +- .../[sessionId]/SessionActivity.module.css | 20 +++++++ .../sessions/[sessionId]/SessionActivity.tsx | 52 +++++++++++++++++++ .../[sessionId]/SessionDetailsPage.module.css | 13 ++++- .../[sessionId]/SessionDetailsPage.tsx | 13 +++-- .../sessions/[sessionId]/SessionInfo.tsx | 16 ++++-- .../sessions/[sessionId]/SessionStats.tsx | 14 +++++ src/assets/location.svg | 1 + src/components/hooks/index.ts | 5 +- .../hooks/queries/useSessionActivity.ts | 12 +++++ .../{useSession.ts => useWebsiteSession.ts} | 4 +- .../{useSessions.ts => useWebsiteSessions.ts} | 4 +- src/lib/load.ts | 10 ++-- .../sessions/[sessionId]/activity.ts | 42 +++++++++++++++ .../{[sessionId].ts => [sessionId]/index.ts} | 8 +-- .../websites/[websiteId]/sessions/index.ts | 4 +- src/queries/analytics/getRealtimeData.ts | 4 +- .../analytics/sessions/getSessionActivity.ts | 44 ++++++++++++++++ .../{getSession.ts => getWebsiteSession.ts} | 8 ++- .../{getSessions.ts => getWebsiteSessions.ts} | 2 +- src/queries/index.ts | 5 +- 21 files changed, 252 insertions(+), 33 deletions(-) create mode 100644 src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.module.css create mode 100644 src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx create mode 100644 src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx create mode 100644 src/assets/location.svg create mode 100644 src/components/hooks/queries/useSessionActivity.ts rename src/components/hooks/queries/{useSession.ts => useWebsiteSession.ts} (69%) rename src/components/hooks/queries/{useSessions.ts => useWebsiteSessions.ts} (76%) create mode 100644 src/pages/api/websites/[websiteId]/sessions/[sessionId]/activity.ts rename src/pages/api/websites/[websiteId]/sessions/{[sessionId].ts => [sessionId]/index.ts} (78%) create mode 100644 src/queries/analytics/sessions/getSessionActivity.ts rename src/queries/analytics/sessions/{getSession.ts => getWebsiteSession.ts} (80%) rename src/queries/analytics/sessions/{getSessions.ts => getWebsiteSessions.ts} (97%) diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsDataTable.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsDataTable.tsx index 9e9f97e9ef..d69cad121a 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsDataTable.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsDataTable.tsx @@ -1,4 +1,4 @@ -import { useSessions } from 'components/hooks'; +import { useWebsiteSessions } from 'components/hooks'; import SessionsTable from './SessionsTable'; import DataTable from 'components/common/DataTable'; import { ReactNode } from 'react'; @@ -11,7 +11,7 @@ export default function SessionsDataTable({ teamId?: string; children?: ReactNode; }) { - const queryResult = useSessions(websiteId); + const queryResult = useWebsiteSessions(websiteId); if (queryResult?.result?.data?.length === 0) { return children; diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.module.css b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.module.css new file mode 100644 index 0000000000..bcc6868d49 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.module.css @@ -0,0 +1,20 @@ +.timeline { + display: flex; + flex-direction: column; + gap: 20px; +} + +.row { + display: flex; + align-items: center; + gap: 20px; +} + +.time { + color: var(--font-color200); + width: 120px; +} + +.header { + font-weight: bold; +} diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx new file mode 100644 index 0000000000..c644fb8908 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx @@ -0,0 +1,52 @@ +import { formatDate } from 'lib/date'; +import { isSameDay } from 'date-fns'; +import { Loading, Icon, StatusLight } from 'react-basics'; +import Icons from 'components/icons'; +import { useLocale, useSessionActivity } from 'components/hooks'; +import styles from './SessionActivity.module.css'; + +export function SessionActivity({ + websiteId, + sessionId, +}: { + websiteId: string; + sessionId: string; +}) { + const { locale } = useLocale(); + const { data, isLoading } = useSessionActivity(websiteId, sessionId); + + if (isLoading) { + return ; + } + + let lastDay = null; + + return ( +
+

Activity log

+ {data.map(({ eventId, createdAt, urlPath, eventName, visitId }) => { + const showHeader = !lastDay || !isSameDay(new Date(lastDay), new Date(createdAt)); + lastDay = createdAt; + + return ( + <> + {showHeader && ( +
+ {formatDate(new Date(createdAt), 'EEEE, PPP', locale)} +
+ )} +
+
+ + {formatDate(new Date(createdAt), 'h:mm:ss aaa', locale)} + +
+ {eventName ? : } +
{eventName || urlPath}
+
+ + ); + })} +
+ ); +} diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.module.css b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.module.css index 21bb011aad..af5ed20153 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.module.css +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.module.css @@ -1,18 +1,27 @@ .page { display: grid; - grid-template-columns: 300px 1fr; + grid-template-columns: 300px 1fr max-content; + margin-bottom: 40px; } .sidebar { display: flex; flex-direction: column; align-items: center; - justify-content: center; + justify-content: flex-start; gap: 20px; padding-right: 20px; border-right: 1px solid var(--base300); } .content { + padding: 0 20px; +} + +.stats { + display: flex; + flex-direction: column; + gap: 20px; + border-left: 1px solid var(--base300); padding-left: 20px; } diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx index 5aaac9525d..723e6ff788 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx @@ -1,10 +1,12 @@ 'use client'; import WebsiteHeader from '../../WebsiteHeader'; import SessionInfo from './SessionInfo'; -import { useSession } from 'components/hooks'; +import { useWebsiteSession } from 'components/hooks'; import { Loading } from 'react-basics'; import Profile from 'components/common/Profile'; import styles from './SessionDetailsPage.module.css'; +import { SessionActivity } from './SessionActivity'; +import SessionStats from './SessionStats'; export default function SessionDetailsPage({ websiteId, @@ -13,7 +15,7 @@ export default function SessionDetailsPage({ websiteId: string; sessionId: string; }) { - const { data, isLoading } = useSession(websiteId, sessionId); + const { data, isLoading } = useWebsiteSession(websiteId, sessionId); if (isLoading) { return ; @@ -27,7 +29,12 @@ export default function SessionDetailsPage({ -
oh hi.
+
+ +
+
+ +
); diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.tsx index cae200ab9e..4b3e8a06d6 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.tsx @@ -1,4 +1,4 @@ -import { format } from 'date-fns'; +import { formatDate } from 'lib/date'; import { useFormat, useLocale, useMessages, useRegionNames } from 'components/hooks'; import TypeIcon from 'components/common/TypeIcon'; import { Icon, CopyIcon } from 'react-basics'; @@ -18,15 +18,19 @@ export default function SessionInfo({ data }) {
{data?.id}
-
{formatMessage(labels.firstSeen)}
-
{format(new Date(data?.firstAt), 'PPPpp')}
+
{formatMessage(labels.lastSeen)}
-
{format(new Date(data?.lastAt), 'PPPpp')}
+
{formatDate(new Date(data?.lastAt), 'EEEE, PPPpp', locale)}
+ +
{formatMessage(labels.firstSeen)}
+
{formatDate(new Date(data?.firstAt), 'EEEE, PPPpp', locale)}
+
{formatMessage(labels.country)}
{formatValue(data?.country, 'country')}
+
{formatMessage(labels.region)}
@@ -34,6 +38,7 @@ export default function SessionInfo({ data }) { {getRegionName(data?.subdivision1)}
+
{formatMessage(labels.city)}
@@ -41,16 +46,19 @@ export default function SessionInfo({ data }) { {data?.city}
+
{formatMessage(labels.os)}
{formatValue(data?.os, 'os')}
+
{formatMessage(labels.device)}
{formatValue(data?.device, 'device')}
+
{formatMessage(labels.browser)}
diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx new file mode 100644 index 0000000000..574152ec4b --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx @@ -0,0 +1,14 @@ +import MetricCard from 'components/metrics/MetricCard'; +import { useMessages } from 'components/hooks'; + +export default function SessionStats({ data }) { + const { formatMessage, labels } = useMessages(); + + return ( + <> + + + + + ); +} diff --git a/src/assets/location.svg b/src/assets/location.svg new file mode 100644 index 0000000000..b0f9af474c --- /dev/null +++ b/src/assets/location.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/hooks/index.ts b/src/components/hooks/index.ts index 515255e675..4ef8925150 100644 --- a/src/components/hooks/index.ts +++ b/src/components/hooks/index.ts @@ -5,8 +5,9 @@ export * from './queries/useLogin'; export * from './queries/useRealtime'; export * from './queries/useReport'; export * from './queries/useReports'; -export * from './queries/useSession'; -export * from './queries/useSessions'; +export * from './queries/useSessionActivity'; +export * from './queries/useWebsiteSession'; +export * from './queries/useWebsiteSessions'; export * from './queries/useShareToken'; export * from './queries/useTeam'; export * from './queries/useTeams'; diff --git a/src/components/hooks/queries/useSessionActivity.ts b/src/components/hooks/queries/useSessionActivity.ts new file mode 100644 index 0000000000..e6d7ffa0e4 --- /dev/null +++ b/src/components/hooks/queries/useSessionActivity.ts @@ -0,0 +1,12 @@ +import { useApi } from './useApi'; + +export function useSessionActivity(websiteId: string, sessionId: string) { + const { get, useQuery } = useApi(); + + return useQuery({ + queryKey: ['session:activity', { websiteId, sessionId }], + queryFn: () => { + return get(`/websites/${websiteId}/sessions/${sessionId}/activity`); + }, + }); +} diff --git a/src/components/hooks/queries/useSession.ts b/src/components/hooks/queries/useWebsiteSession.ts similarity index 69% rename from src/components/hooks/queries/useSession.ts rename to src/components/hooks/queries/useWebsiteSession.ts index f65efcf45a..64c7be58bf 100644 --- a/src/components/hooks/queries/useSession.ts +++ b/src/components/hooks/queries/useWebsiteSession.ts @@ -1,6 +1,6 @@ import { useApi } from './useApi'; -export function useSession(websiteId: string, sessionId: string) { +export function useWebsiteSession(websiteId: string, sessionId: string) { const { get, useQuery } = useApi(); return useQuery({ @@ -11,4 +11,4 @@ export function useSession(websiteId: string, sessionId: string) { }); } -export default useSession; +export default useWebsiteSession; diff --git a/src/components/hooks/queries/useSessions.ts b/src/components/hooks/queries/useWebsiteSessions.ts similarity index 76% rename from src/components/hooks/queries/useSessions.ts rename to src/components/hooks/queries/useWebsiteSessions.ts index 91f4ea4c64..9430786b1e 100644 --- a/src/components/hooks/queries/useSessions.ts +++ b/src/components/hooks/queries/useWebsiteSessions.ts @@ -2,7 +2,7 @@ import { useApi } from './useApi'; import { useFilterQuery } from './useFilterQuery'; import useModified from '../useModified'; -export function useSessions(websiteId: string, params?: { [key: string]: string | number }) { +export function useWebsiteSessions(websiteId: string, params?: { [key: string]: string | number }) { const { get } = useApi(); const { modified } = useModified(`sessions`); @@ -17,4 +17,4 @@ export function useSessions(websiteId: string, params?: { [key: string]: string }); } -export default useSessions; +export default useWebsiteSessions; diff --git a/src/lib/load.ts b/src/lib/load.ts index 7812ea0db2..970247ddd5 100644 --- a/src/lib/load.ts +++ b/src/lib/load.ts @@ -1,4 +1,4 @@ -import { getSession, getWebsite } from 'queries'; +import { getWebsiteSession, getWebsite } from 'queries'; import { Website, Session } from '@prisma/client'; import redis from '@umami/redis-client'; @@ -22,9 +22,13 @@ export async function fetchSession(sessionId: string): Promise { let session = null; if (redis.enabled) { - session = await redis.client.fetch(`session:${sessionId}`, () => getSession(sessionId), 86400); + session = await redis.client.fetch( + `session:${sessionId}`, + () => getWebsiteSession(sessionId), + 86400, + ); } else { - session = await getSession(sessionId); + session = await getWebsiteSession(sessionId); } if (!session) { diff --git a/src/pages/api/websites/[websiteId]/sessions/[sessionId]/activity.ts b/src/pages/api/websites/[websiteId]/sessions/[sessionId]/activity.ts new file mode 100644 index 0000000000..8d1b234617 --- /dev/null +++ b/src/pages/api/websites/[websiteId]/sessions/[sessionId]/activity.ts @@ -0,0 +1,42 @@ +import * as yup from 'yup'; +import { canViewWebsite } from 'lib/auth'; +import { useAuth, useCors, useValidate } from 'lib/middleware'; +import { NextApiRequestQueryBody, PageParams } from 'lib/types'; +import { NextApiResponse } from 'next'; +import { methodNotAllowed, ok, unauthorized } from 'next-basics'; +import { getSessionActivity } from 'queries'; + +export interface SessionActivityRequestQuery extends PageParams { + websiteId: string; + sessionId: string; +} + +const schema = { + GET: yup.object().shape({ + websiteId: yup.string().uuid().required(), + sessionId: yup.string().uuid().required(), + }), +}; + +export default async ( + req: NextApiRequestQueryBody, + res: NextApiResponse, +) => { + await useCors(req, res); + await useAuth(req, res); + await useValidate(schema, req, res); + + const { websiteId, sessionId } = req.query; + + if (req.method === 'GET') { + if (!(await canViewWebsite(req.auth, websiteId))) { + return unauthorized(res); + } + + const data = await getSessionActivity(websiteId, sessionId); + + return ok(res, data); + } + + return methodNotAllowed(res); +}; diff --git a/src/pages/api/websites/[websiteId]/sessions/[sessionId].ts b/src/pages/api/websites/[websiteId]/sessions/[sessionId]/index.ts similarity index 78% rename from src/pages/api/websites/[websiteId]/sessions/[sessionId].ts rename to src/pages/api/websites/[websiteId]/sessions/[sessionId]/index.ts index 57e99ea376..f627a20858 100644 --- a/src/pages/api/websites/[websiteId]/sessions/[sessionId].ts +++ b/src/pages/api/websites/[websiteId]/sessions/[sessionId]/index.ts @@ -4,9 +4,9 @@ import { useAuth, useCors, useValidate } from 'lib/middleware'; import { NextApiRequestQueryBody, PageParams } from 'lib/types'; import { NextApiResponse } from 'next'; import { methodNotAllowed, ok, unauthorized } from 'next-basics'; -import { getSession } from 'queries'; +import { getWebsiteSession } from 'queries'; -export interface ReportsRequestQuery extends PageParams { +export interface WesiteSessionRequestQuery extends PageParams { websiteId: string; sessionId: string; } @@ -19,7 +19,7 @@ const schema = { }; export default async ( - req: NextApiRequestQueryBody, + req: NextApiRequestQueryBody, res: NextApiResponse, ) => { await useCors(req, res); @@ -33,7 +33,7 @@ export default async ( return unauthorized(res); } - const data = await getSession(websiteId, sessionId); + const data = await getWebsiteSession(websiteId, sessionId); return ok(res, data); } diff --git a/src/pages/api/websites/[websiteId]/sessions/index.ts b/src/pages/api/websites/[websiteId]/sessions/index.ts index 21cacb1c2b..4f242882fb 100644 --- a/src/pages/api/websites/[websiteId]/sessions/index.ts +++ b/src/pages/api/websites/[websiteId]/sessions/index.ts @@ -5,7 +5,7 @@ import { NextApiRequestQueryBody, PageParams } from 'lib/types'; import { NextApiResponse } from 'next'; import { methodNotAllowed, ok, unauthorized } from 'next-basics'; import { pageInfo } from 'lib/schema'; -import { getSessions } from 'queries'; +import { getWebsiteSessions } from 'queries'; export interface ReportsRequestQuery extends PageParams { websiteId: string; @@ -33,7 +33,7 @@ export default async ( return unauthorized(res); } - const data = await getSessions(websiteId, {}, req.query); + const data = await getWebsiteSessions(websiteId, {}, req.query); return ok(res, data); } diff --git a/src/queries/analytics/getRealtimeData.ts b/src/queries/analytics/getRealtimeData.ts index 5a9c5a36c0..e25cc86617 100644 --- a/src/queries/analytics/getRealtimeData.ts +++ b/src/queries/analytics/getRealtimeData.ts @@ -1,4 +1,4 @@ -import { getSessions, getEvents, getPageviewStats, getSessionStats } from 'queries/index'; +import { getWebsiteSessions, getEvents, getPageviewStats, getSessionStats } from 'queries/index'; const MAX_SIZE = 50; @@ -20,7 +20,7 @@ export async function getRealtimeData( const filters = { startDate, endDate: new Date(), unit: 'minute', timezone }; const [events, sessions, pageviews, sessionviews] = await Promise.all([ getEvents(websiteId, { startDate, timezone }, { pageSize: 10000 }), - getSessions(websiteId, { startDate, timezone }, { pageSize: 10000 }), + getWebsiteSessions(websiteId, { startDate, timezone }, { pageSize: 10000 }), getPageviewStats(websiteId, filters), getSessionStats(websiteId, filters), ]); diff --git a/src/queries/analytics/sessions/getSessionActivity.ts b/src/queries/analytics/sessions/getSessionActivity.ts new file mode 100644 index 0000000000..bc1f9acaa4 --- /dev/null +++ b/src/queries/analytics/sessions/getSessionActivity.ts @@ -0,0 +1,44 @@ +import prisma from 'lib/prisma'; +import clickhouse from 'lib/clickhouse'; +import { runQuery, PRISMA, CLICKHOUSE } from 'lib/db'; + +export async function getSessionActivity(...args: [websiteId: string, sessionId: string]) { + return runQuery({ + [PRISMA]: () => relationalQuery(...args), + [CLICKHOUSE]: () => clickhouseQuery(...args), + }); +} + +async function relationalQuery(websiteId: string, sessionId: string) { + return prisma.client.websiteEvent.findMany({ + where: { + id: sessionId, + websiteId, + }, + }); +} + +async function clickhouseQuery(websiteId: string, sessionId: string) { + const { rawQuery } = clickhouse; + + return rawQuery( + ` + select + session_id as id, + website_id as websiteId, + created_at as createdAt, + url_path as urlPath, + url_query as urlQuery, + referrer_domain as referrerDomain, + event_id as eventId, + event_type as eventType, + event_name as eventName, + visit_id as visitId + from website_event + where website_id = {websiteId:UUID} + and session_id = {sessionId:UUID} + order by created_at desc + `, + { websiteId, sessionId }, + ); +} diff --git a/src/queries/analytics/sessions/getSession.ts b/src/queries/analytics/sessions/getWebsiteSession.ts similarity index 80% rename from src/queries/analytics/sessions/getSession.ts rename to src/queries/analytics/sessions/getWebsiteSession.ts index 9cdc45d69c..2e6e8eca05 100644 --- a/src/queries/analytics/sessions/getSession.ts +++ b/src/queries/analytics/sessions/getWebsiteSession.ts @@ -2,7 +2,7 @@ import prisma from 'lib/prisma'; import clickhouse from 'lib/clickhouse'; import { runQuery, PRISMA, CLICKHOUSE } from 'lib/db'; -export async function getSession(...args: [websiteId: string, sessionId: string]) { +export async function getWebsiteSession(...args: [websiteId: string, sessionId: string]) { return runQuery({ [PRISMA]: () => relationalQuery(...args), [CLICKHOUSE]: () => clickhouseQuery(...args), @@ -13,6 +13,7 @@ async function relationalQuery(websiteId: string, sessionId: string) { return prisma.client.session.findUnique({ where: { id: sessionId, + websiteId, }, }); } @@ -35,7 +36,10 @@ async function clickhouseQuery(websiteId: string, sessionId: string) { subdivision1, city, min(created_at) as firstAt, - max(created_at) as lastAt + max(created_at) as lastAt, + uniq(visit_id) as "visits", + sumIf(1, event_type = 1) as "views", + sumIf(1, event_type = 2) as "events" from website_event where website_id = {websiteId:UUID} and session_id = {sessionId:UUID} diff --git a/src/queries/analytics/sessions/getSessions.ts b/src/queries/analytics/sessions/getWebsiteSessions.ts similarity index 97% rename from src/queries/analytics/sessions/getSessions.ts rename to src/queries/analytics/sessions/getWebsiteSessions.ts index 4e632430ae..2094c8a452 100644 --- a/src/queries/analytics/sessions/getSessions.ts +++ b/src/queries/analytics/sessions/getWebsiteSessions.ts @@ -3,7 +3,7 @@ import clickhouse from 'lib/clickhouse'; import { runQuery, PRISMA, CLICKHOUSE } from 'lib/db'; import { PageParams, QueryFilters } from 'lib/types'; -export async function getSessions( +export async function getWebsiteSessions( ...args: [websiteId: string, filters?: QueryFilters, pageParams?: PageParams] ) { return runQuery({ diff --git a/src/queries/index.ts b/src/queries/index.ts index 796adea6f4..cd7ea280e7 100644 --- a/src/queries/index.ts +++ b/src/queries/index.ts @@ -19,9 +19,10 @@ export * from './analytics/reports/getUTM'; export * from './analytics/pageviews/getPageviewMetrics'; export * from './analytics/pageviews/getPageviewStats'; export * from './analytics/sessions/createSession'; -export * from './analytics/sessions/getSession'; +export * from './analytics/sessions/getWebsiteSession'; export * from './analytics/sessions/getSessionMetrics'; -export * from './analytics/sessions/getSessions'; +export * from './analytics/sessions/getWebsiteSessions'; +export * from './analytics/sessions/getSessionActivity'; export * from './analytics/sessions/getSessionStats'; export * from './analytics/sessions/saveSessionData'; export * from './analytics/getActiveVisitors'; From b3e6e524734e83507758bf1fccd577bda5794ec4 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Tue, 30 Jul 2024 21:22:19 -0700 Subject: [PATCH 050/132] Added session data display. --- .../sessions/SessionsTable.module.css | 5 +++ .../[websiteId]/sessions/SessionsTable.tsx | 20 +++++---- .../sessions/[sessionId]/SessionActivity.tsx | 1 - .../[sessionId]/SessionData.module.css | 39 +++++++++++++++++ .../sessions/[sessionId]/SessionData.tsx | 34 +++++++++++++++ .../[sessionId]/SessionDetailsPage.module.css | 25 +++++++++-- .../[sessionId]/SessionDetailsPage.tsx | 10 +++-- .../sessions/[sessionId]/SessionStats.tsx | 7 ++-- src/components/common/Profile.tsx | 6 +-- src/components/hooks/index.ts | 1 + .../hooks/queries/useSessionData.ts | 12 ++++++ src/components/messages.ts | 1 + src/pages/api/sessions/[sessionId]/data.ts | 42 +++++++++++++++++++ .../getEventDataEvents.ts | 0 .../getEventDataFields.ts | 0 .../getEventDataStats.ts | 0 .../getEventDataUsage.ts | 0 src/queries/analytics/events/saveEvent.ts | 2 +- .../{eventData => events}/saveEventData.ts | 0 .../analytics/sessions/getSessionData.ts | 41 ++++++++++++++++++ .../analytics/sessions/getWebsiteSession.ts | 6 +-- .../analytics/sessions/getWebsiteSessions.ts | 8 +++- src/queries/index.ts | 11 ++--- 23 files changed, 238 insertions(+), 33 deletions(-) create mode 100644 src/app/(main)/websites/[websiteId]/sessions/SessionsTable.module.css create mode 100644 src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.module.css create mode 100644 src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.tsx create mode 100644 src/components/hooks/queries/useSessionData.ts create mode 100644 src/pages/api/sessions/[sessionId]/data.ts rename src/queries/analytics/{eventData => events}/getEventDataEvents.ts (100%) rename src/queries/analytics/{eventData => events}/getEventDataFields.ts (100%) rename src/queries/analytics/{eventData => events}/getEventDataStats.ts (100%) rename src/queries/analytics/{eventData => events}/getEventDataUsage.ts (100%) rename src/queries/analytics/{eventData => events}/saveEventData.ts (100%) create mode 100644 src/queries/analytics/sessions/getSessionData.ts diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.module.css b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.module.css new file mode 100644 index 0000000000..140ad0bb72 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.module.css @@ -0,0 +1,5 @@ +.link { + display: flex; + align-items: center; + gap: 20px; +} diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx index d4cf827a0a..73148b835a 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx @@ -1,21 +1,27 @@ import Link from 'next/link'; import { GridColumn, GridTable, useBreakpoint } from 'react-basics'; -import { useFormat, useMessages } from 'components/hooks'; +import { useFormat, useLocale, useMessages } from 'components/hooks'; import Profile from 'components/common/Profile'; +import styles from './SessionsTable.module.css'; +import { formatDate } from 'lib/date'; export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean }) { + const { locale } = useLocale(); const { formatMessage, labels } = useMessages(); const breakpoint = useBreakpoint(); const { formatValue } = useFormat(); return ( - - {row => } - - - {row => {row.id}} + + {row => ( + + + {row.id} + + )} + {row => formatValue(row.country, 'country')} @@ -28,7 +34,7 @@ export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean {row => formatValue(row.device, 'device')} - {row => row.lastAt} + {row => formatDate(new Date(row.lastAt), 'PPPpp', locale)} ); diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx index c644fb8908..0a15430c0e 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx @@ -23,7 +23,6 @@ export function SessionActivity({ return (
-

Activity log

{data.map(({ eventId, createdAt, urlPath, eventName, visitId }) => { const showHeader = !lastDay || !isSameDay(new Date(lastDay), new Date(createdAt)); lastDay = createdAt; diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.module.css b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.module.css new file mode 100644 index 0000000000..2057622be8 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.module.css @@ -0,0 +1,39 @@ +.data { + display: flex; + flex-direction: column; + gap: 20px; + width: 200px; + position: relative; +} + +.header { + font-weight: bold; +} + +.empty { + color: var(--font-color300); + text-align: center; +} + +.label { + display: flex; + align-items: center; + justify-content: space-between; +} + +.type { + font-size: 10px; + text-transform: uppercase; + padding: 0 6px; + border-radius: 4px; + border: 1px solid var(--base300); +} + +.name { + color: var(--font-color200); + font-weight: bold; +} + +.value { + margin: 5px 0; +} diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.tsx new file mode 100644 index 0000000000..2d99f8b349 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.tsx @@ -0,0 +1,34 @@ +import { Loading, TextOverflow } from 'react-basics'; +import { useMessages, useSessionData } from 'components/hooks'; +import Empty from 'components/common/Empty'; +import styles from './SessionData.module.css'; +import { DATA_TYPES } from 'lib/constants'; + +export function SessionData({ websiteId, sessionId }: { websiteId: string; sessionId: string }) { + const { formatMessage, labels } = useMessages(); + const { data, isLoading } = useSessionData(websiteId, sessionId); + + if (isLoading) { + return ; + } + + return ( +
+
{formatMessage(labels.properties)}
+ {!data?.length && } + {data?.map(({ dataKey, dataType, stringValue }) => { + return ( +
+
+
+ {dataKey} +
+
{DATA_TYPES[dataType]}
+
+
{stringValue}
+
+ ); + })} +
+ ); +} diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.module.css b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.module.css index af5ed20153..8e2667f427 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.module.css +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.module.css @@ -2,6 +2,7 @@ display: grid; grid-template-columns: 300px 1fr max-content; margin-bottom: 40px; + position: relative; } .sidebar { @@ -12,16 +13,32 @@ gap: 20px; padding-right: 20px; border-right: 1px solid var(--base300); + position: relative; } .content { + display: flex; + flex-direction: column; + gap: 30px; padding: 0 20px; + position: relative; } -.stats { - display: flex; - flex-direction: column; - gap: 20px; +.data { border-left: 1px solid var(--base300); padding-left: 20px; + position: relative; + transition: width 200ms ease-in-out; +} + +@media screen and (max-width: 992px) { + .page { + grid-template-columns: 1fr; + gap: 30px; + } + + .sidebar, + .data { + border: 0; + } } diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx index 723e6ff788..d4ed503e77 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx @@ -4,9 +4,10 @@ import SessionInfo from './SessionInfo'; import { useWebsiteSession } from 'components/hooks'; import { Loading } from 'react-basics'; import Profile from 'components/common/Profile'; -import styles from './SessionDetailsPage.module.css'; import { SessionActivity } from './SessionActivity'; -import SessionStats from './SessionStats'; +import { SessionStats } from './SessionStats'; +import { SessionData } from './SessionData'; +import styles from './SessionDetailsPage.module.css'; export default function SessionDetailsPage({ websiteId, @@ -30,10 +31,11 @@ export default function SessionDetailsPage({
+
-
- +
+
diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx index 574152ec4b..a25f7af3b4 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx @@ -1,14 +1,15 @@ import MetricCard from 'components/metrics/MetricCard'; import { useMessages } from 'components/hooks'; +import MetricsBar from 'components/metrics/MetricsBar'; -export default function SessionStats({ data }) { +export function SessionStats({ data }) { const { formatMessage, labels } = useMessages(); return ( - <> + - + ); } diff --git a/src/components/common/Profile.tsx b/src/components/common/Profile.tsx index 63a59c1687..4054499475 100644 --- a/src/components/common/Profile.tsx +++ b/src/components/common/Profile.tsx @@ -10,9 +10,9 @@ function convertToPastel(hexColor: string, pastelFactor: number = 0.5) { hexColor = hexColor.replace(/^#/, ''); // Convert hex to RGB - let r = parseInt(hexColor.substr(0, 2), 16); - let g = parseInt(hexColor.substr(2, 2), 16); - let b = parseInt(hexColor.substr(4, 2), 16); + let r = parseInt(hexColor.substring(0, 2), 16); + let g = parseInt(hexColor.substring(2, 4), 16); + let b = parseInt(hexColor.substring(4, 6), 16); // Calculate pastel version (mix with white) //const pastelFactor = 0.5; // Adjust this value to control pastel intensity diff --git a/src/components/hooks/index.ts b/src/components/hooks/index.ts index 4ef8925150..e06a0414cd 100644 --- a/src/components/hooks/index.ts +++ b/src/components/hooks/index.ts @@ -6,6 +6,7 @@ export * from './queries/useRealtime'; export * from './queries/useReport'; export * from './queries/useReports'; export * from './queries/useSessionActivity'; +export * from './queries/useSessionData'; export * from './queries/useWebsiteSession'; export * from './queries/useWebsiteSessions'; export * from './queries/useShareToken'; diff --git a/src/components/hooks/queries/useSessionData.ts b/src/components/hooks/queries/useSessionData.ts new file mode 100644 index 0000000000..f2e8f5241c --- /dev/null +++ b/src/components/hooks/queries/useSessionData.ts @@ -0,0 +1,12 @@ +import { useApi } from './useApi'; + +export function useSessionData(websiteId: string, sessionId: string) { + const { get, useQuery } = useApi(); + + return useQuery({ + queryKey: ['session:data', { websiteId, sessionId }], + queryFn: () => { + return get(`/sessions/${sessionId}/data`, { websiteId }); + }, + }); +} diff --git a/src/components/messages.ts b/src/components/messages.ts index 764348fae0..2dfe61ee65 100644 --- a/src/components/messages.ts +++ b/src/components/messages.ts @@ -274,6 +274,7 @@ export const labels = defineMessages({ previousYear: { id: 'label.previous-year', defaultMessage: 'Previous year' }, lastSeen: { id: 'label.last-seen', defaultMessage: 'Last seen' }, firstSeen: { id: 'label.first-seen', defaultMessage: 'First seen' }, + properties: { id: 'label.properties', defaultMessage: 'Properties' }, }); export const messages = defineMessages({ diff --git a/src/pages/api/sessions/[sessionId]/data.ts b/src/pages/api/sessions/[sessionId]/data.ts new file mode 100644 index 0000000000..c0c200648d --- /dev/null +++ b/src/pages/api/sessions/[sessionId]/data.ts @@ -0,0 +1,42 @@ +import { canViewWebsite } from 'lib/auth'; +import { useAuth, useCors, useValidate } from 'lib/middleware'; +import { NextApiRequestQueryBody } from 'lib/types'; +import { NextApiResponse } from 'next'; +import { methodNotAllowed, ok, unauthorized } from 'next-basics'; +import { getSessionData } from 'queries'; +import * as yup from 'yup'; + +export interface SessionDataRequestQuery { + sessionId: string; + websiteId: string; +} + +const schema = { + GET: yup.object().shape({ + sessionId: yup.string().uuid().required(), + websiteId: yup.string().uuid().required(), + }), +}; + +export default async ( + req: NextApiRequestQueryBody, + res: NextApiResponse, +) => { + await useCors(req, res); + await useAuth(req, res); + await useValidate(schema, req, res); + + if (req.method === 'GET') { + const { websiteId, sessionId } = req.query; + + if (!(await canViewWebsite(req.auth, websiteId))) { + return unauthorized(res); + } + + const data = await getSessionData(websiteId, sessionId); + + return ok(res, data); + } + + return methodNotAllowed(res); +}; diff --git a/src/queries/analytics/eventData/getEventDataEvents.ts b/src/queries/analytics/events/getEventDataEvents.ts similarity index 100% rename from src/queries/analytics/eventData/getEventDataEvents.ts rename to src/queries/analytics/events/getEventDataEvents.ts diff --git a/src/queries/analytics/eventData/getEventDataFields.ts b/src/queries/analytics/events/getEventDataFields.ts similarity index 100% rename from src/queries/analytics/eventData/getEventDataFields.ts rename to src/queries/analytics/events/getEventDataFields.ts diff --git a/src/queries/analytics/eventData/getEventDataStats.ts b/src/queries/analytics/events/getEventDataStats.ts similarity index 100% rename from src/queries/analytics/eventData/getEventDataStats.ts rename to src/queries/analytics/events/getEventDataStats.ts diff --git a/src/queries/analytics/eventData/getEventDataUsage.ts b/src/queries/analytics/events/getEventDataUsage.ts similarity index 100% rename from src/queries/analytics/eventData/getEventDataUsage.ts rename to src/queries/analytics/events/getEventDataUsage.ts diff --git a/src/queries/analytics/events/saveEvent.ts b/src/queries/analytics/events/saveEvent.ts index cd41b7a318..fa4805df36 100644 --- a/src/queries/analytics/events/saveEvent.ts +++ b/src/queries/analytics/events/saveEvent.ts @@ -4,7 +4,7 @@ import clickhouse from 'lib/clickhouse'; import kafka from 'lib/kafka'; import prisma from 'lib/prisma'; import { uuid } from 'lib/crypto'; -import { saveEventData } from 'queries/analytics/eventData/saveEventData'; +import { saveEventData } from './saveEventData'; export async function saveEvent(args: { websiteId: string; diff --git a/src/queries/analytics/eventData/saveEventData.ts b/src/queries/analytics/events/saveEventData.ts similarity index 100% rename from src/queries/analytics/eventData/saveEventData.ts rename to src/queries/analytics/events/saveEventData.ts diff --git a/src/queries/analytics/sessions/getSessionData.ts b/src/queries/analytics/sessions/getSessionData.ts new file mode 100644 index 0000000000..e3106b2753 --- /dev/null +++ b/src/queries/analytics/sessions/getSessionData.ts @@ -0,0 +1,41 @@ +import prisma from 'lib/prisma'; +import clickhouse from 'lib/clickhouse'; +import { runQuery, PRISMA, CLICKHOUSE } from 'lib/db'; + +export async function getSessionData(...args: [websiteId: string, sessionId: string]) { + return runQuery({ + [PRISMA]: () => relationalQuery(...args), + [CLICKHOUSE]: () => clickhouseQuery(...args), + }); +} + +async function relationalQuery(websiteId: string, sessionId: string) { + return prisma.client.sessionData.findMany({ + where: { + id: sessionId, + websiteId, + }, + }); +} + +async function clickhouseQuery(websiteId: string, sessionId: string) { + const { rawQuery } = clickhouse; + + return rawQuery( + ` + select + website_id as websiteId, + session_id as sessionId, + data_key as dataKey, + data_type as dataType, + string_value as stringValue, + number_value as numberValue, + date_value as dateValue, + created_at as createdAt + from session_data + where website_id = {websiteId:UUID} + and session_id = {sessionId:UUID} + `, + { websiteId, sessionId }, + ); +} diff --git a/src/queries/analytics/sessions/getWebsiteSession.ts b/src/queries/analytics/sessions/getWebsiteSession.ts index 2e6e8eca05..df3e8a04ad 100644 --- a/src/queries/analytics/sessions/getWebsiteSession.ts +++ b/src/queries/analytics/sessions/getWebsiteSession.ts @@ -37,9 +37,9 @@ async function clickhouseQuery(websiteId: string, sessionId: string) { city, min(created_at) as firstAt, max(created_at) as lastAt, - uniq(visit_id) as "visits", - sumIf(1, event_type = 1) as "views", - sumIf(1, event_type = 2) as "events" + uniq(visit_id) as visits, + sumIf(1, event_type = 1) as views, + sumIf(1, event_type = 2) as events from website_event where website_id = {websiteId:UUID} and session_id = {sessionId:UUID} diff --git a/src/queries/analytics/sessions/getWebsiteSessions.ts b/src/queries/analytics/sessions/getWebsiteSessions.ts index 2094c8a452..c5fe6cec99 100644 --- a/src/queries/analytics/sessions/getWebsiteSessions.ts +++ b/src/queries/analytics/sessions/getWebsiteSessions.ts @@ -42,7 +42,8 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar subdivision1, city, min(created_at) as firstAt, - max(created_at) as lastAt + max(created_at) as lastAt, + uniq(visit_id) as visits from website_event where website_id = {websiteId:UUID} ${dateQuery} @@ -52,5 +53,8 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar `, params, pageParams, - ); + ).then((result: any) => ({ + ...result, + visits: Number(result.visits), + })); } diff --git a/src/queries/index.ts b/src/queries/index.ts index cd7ea280e7..82d40ee5d9 100644 --- a/src/queries/index.ts +++ b/src/queries/index.ts @@ -3,13 +3,13 @@ export * from 'queries/prisma/team'; export * from 'queries/prisma/teamUser'; export * from 'queries/prisma/user'; export * from 'queries/prisma/website'; +export * from './analytics/events/getEventDataEvents'; +export * from './analytics/events/getEventDataFields'; +export * from './analytics/events/getEventDataStats'; +export * from './analytics/events/getEventDataUsage'; export * from './analytics/events/getEventMetrics'; -export * from './analytics/events/getEventUsage'; export * from './analytics/events/getEvents'; -export * from './analytics/eventData/getEventDataEvents'; -export * from './analytics/eventData/getEventDataFields'; -export * from './analytics/eventData/getEventDataStats'; -export * from './analytics/eventData/getEventDataUsage'; +export * from './analytics/events/getEventUsage'; export * from './analytics/events/saveEvent'; export * from './analytics/reports/getFunnel'; export * from './analytics/reports/getJourney'; @@ -20,6 +20,7 @@ export * from './analytics/pageviews/getPageviewMetrics'; export * from './analytics/pageviews/getPageviewStats'; export * from './analytics/sessions/createSession'; export * from './analytics/sessions/getWebsiteSession'; +export * from './analytics/sessions/getSessionData'; export * from './analytics/sessions/getSessionMetrics'; export * from './analytics/sessions/getWebsiteSessions'; export * from './analytics/sessions/getSessionActivity'; From 10f65cae685314fb857ea27813ab8bc771790f92 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Tue, 30 Jul 2024 22:10:32 -0700 Subject: [PATCH 051/132] Responsive styles for session page. --- .../sessions/[sessionId]/SessionData.module.css | 6 ++---- .../[websiteId]/sessions/[sessionId]/SessionData.tsx | 2 +- .../[sessionId]/SessionDetailsPage.module.css | 5 ++++- .../sessions/[sessionId]/SessionInfo.module.css | 4 ++++ .../[websiteId]/sessions/[sessionId]/index.ts | 2 +- src/queries/analytics/sessions/getSessionData.ts | 1 + src/queries/analytics/sessions/getWebsiteSession.ts | 12 +++++------- 7 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.module.css b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.module.css index 2057622be8..072172f6dd 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.module.css +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.module.css @@ -2,7 +2,6 @@ display: flex; flex-direction: column; gap: 20px; - width: 200px; position: relative; } @@ -22,11 +21,10 @@ } .type { - font-size: 10px; - text-transform: uppercase; + font-size: 11px; padding: 0 6px; border-radius: 4px; - border: 1px solid var(--base300); + border: 1px solid var(--base400); } .name { diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.tsx index 2d99f8b349..7e583f185b 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.tsx @@ -1,8 +1,8 @@ import { Loading, TextOverflow } from 'react-basics'; import { useMessages, useSessionData } from 'components/hooks'; import Empty from 'components/common/Empty'; -import styles from './SessionData.module.css'; import { DATA_TYPES } from 'lib/constants'; +import styles from './SessionData.module.css'; export function SessionData({ websiteId, sessionId }: { websiteId: string; sessionId: string }) { const { formatMessage, labels } = useMessages(); diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.module.css b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.module.css index 8e2667f427..7058cd7939 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.module.css +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.module.css @@ -1,6 +1,6 @@ .page { display: grid; - grid-template-columns: 300px 1fr max-content; + grid-template-columns: max-content 1fr max-content; margin-bottom: 40px; position: relative; } @@ -11,6 +11,7 @@ align-items: center; justify-content: flex-start; gap: 20px; + width: 300px; padding-right: 20px; border-right: 1px solid var(--base300); position: relative; @@ -25,6 +26,7 @@ } .data { + width: 300px; border-left: 1px solid var(--base300); padding-left: 20px; position: relative; @@ -40,5 +42,6 @@ .sidebar, .data { border: 0; + width: auto; } } diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.module.css b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.module.css index c17eadf5f4..de6e796f7b 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.module.css +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.module.css @@ -3,6 +3,10 @@ gap: 10px; } +.info dl { + width: 100%; +} + .info dt { color: var(--font-color200); font-weight: bold; diff --git a/src/pages/api/websites/[websiteId]/sessions/[sessionId]/index.ts b/src/pages/api/websites/[websiteId]/sessions/[sessionId]/index.ts index f627a20858..b2665c7cf6 100644 --- a/src/pages/api/websites/[websiteId]/sessions/[sessionId]/index.ts +++ b/src/pages/api/websites/[websiteId]/sessions/[sessionId]/index.ts @@ -33,7 +33,7 @@ export default async ( return unauthorized(res); } - const data = await getWebsiteSession(websiteId, sessionId); + const data = await getWebsiteSession(sessionId); return ok(res, data); } diff --git a/src/queries/analytics/sessions/getSessionData.ts b/src/queries/analytics/sessions/getSessionData.ts index e3106b2753..8b78fc8623 100644 --- a/src/queries/analytics/sessions/getSessionData.ts +++ b/src/queries/analytics/sessions/getSessionData.ts @@ -35,6 +35,7 @@ async function clickhouseQuery(websiteId: string, sessionId: string) { from session_data where website_id = {websiteId:UUID} and session_id = {sessionId:UUID} + order by data_key asc `, { websiteId, sessionId }, ); diff --git a/src/queries/analytics/sessions/getWebsiteSession.ts b/src/queries/analytics/sessions/getWebsiteSession.ts index df3e8a04ad..38bc230c8c 100644 --- a/src/queries/analytics/sessions/getWebsiteSession.ts +++ b/src/queries/analytics/sessions/getWebsiteSession.ts @@ -2,23 +2,22 @@ import prisma from 'lib/prisma'; import clickhouse from 'lib/clickhouse'; import { runQuery, PRISMA, CLICKHOUSE } from 'lib/db'; -export async function getWebsiteSession(...args: [websiteId: string, sessionId: string]) { +export async function getWebsiteSession(...args: [sessionId: string]) { return runQuery({ [PRISMA]: () => relationalQuery(...args), [CLICKHOUSE]: () => clickhouseQuery(...args), }); } -async function relationalQuery(websiteId: string, sessionId: string) { +async function relationalQuery(sessionId: string) { return prisma.client.session.findUnique({ where: { id: sessionId, - websiteId, }, }); } -async function clickhouseQuery(websiteId: string, sessionId: string) { +async function clickhouseQuery(sessionId: string) { const { rawQuery } = clickhouse; return rawQuery( @@ -41,10 +40,9 @@ async function clickhouseQuery(websiteId: string, sessionId: string) { sumIf(1, event_type = 1) as views, sumIf(1, event_type = 2) as events from website_event - where website_id = {websiteId:UUID} - and session_id = {sessionId:UUID} + where session_id = {sessionId:UUID} group by session_id, website_id, hostname, browser, os, device, screen, language, country, subdivision1, city `, - { websiteId, sessionId }, + { sessionId }, ).then(result => result?.[0]); } From 1e5616a872e4e19dc9afe5f3ae96115a016750d2 Mon Sep 17 00:00:00 2001 From: beforetech Date: Wed, 31 Jul 2024 22:21:58 +0800 Subject: [PATCH 052/132] chore: fix comment Signed-off-by: beforetech --- src/components/messages.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/messages.ts b/src/components/messages.ts index fa515c665f..6609076235 100644 --- a/src/components/messages.ts +++ b/src/components/messages.ts @@ -265,7 +265,7 @@ export const labels = defineMessages({ journey: { id: 'label.journey', defaultMessage: 'Journey' }, journeyDescription: { id: 'label.journey-description', - defaultMessage: 'Understand how users nagivate through your website.', + defaultMessage: 'Understand how users navigate through your website.', }, compare: { id: 'label.compare', defaultMessage: 'Compare' }, current: { id: 'label.current', defaultMessage: 'Current' }, From cb4368e12ca16c2c1c658d715c60383089424ec5 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Wed, 31 Jul 2024 09:35:29 -0700 Subject: [PATCH 053/132] template multiple queries for filtering --- db/clickhouse/schema.sql | 16 +++++------- src/queries/analytics/getWebsiteStats.ts | 25 +++++++++++++++++-- .../analytics/pageviews/getPageviewMetrics.ts | 18 +++++++++++++ .../analytics/pageviews/getPageviewStats.ts | 21 ++++++++++++++-- 4 files changed, 66 insertions(+), 14 deletions(-) diff --git a/db/clickhouse/schema.sql b/db/clickhouse/schema.sql index 6179bfb04c..aadc5402b6 100644 --- a/db/clickhouse/schema.sql +++ b/db/clickhouse/schema.sql @@ -30,7 +30,9 @@ CREATE TABLE umami.website_event job_id Nullable(UUID) ) engine = MergeTree - ORDER BY (website_id, session_id, created_at) + PARTITION BY toYYYYMM(created_at) + ORDER BY (toStartOfHour(created_at), website_id, session_id, visit_id, created_at) + PRIMARY KEY (toStartOfHour(created_at), website_id, session_id, visit_id) SETTINGS index_granularity = 8192; CREATE TABLE umami.event_data @@ -97,15 +99,9 @@ CREATE TABLE umami.website_event_stats_hourly 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); + PARTITION BY toYYYYMM(created_at) + ORDER BY (toStartOfDay(created_at), website_id, session_id, visit_id, created_at) + PRIMARY KEY (toStartOfDay(created_at), website_id, session_id, visit_id) CREATE MATERIALIZED VIEW umami.website_event_stats_hourly_mv TO umami.website_event_stats_hourly diff --git a/src/queries/analytics/getWebsiteStats.ts b/src/queries/analytics/getWebsiteStats.ts index ebe711a03f..7d8a76f69b 100644 --- a/src/queries/analytics/getWebsiteStats.ts +++ b/src/queries/analytics/getWebsiteStats.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-unused-vars, @typescript-eslint/no-unused-vars */ import clickhouse from 'lib/clickhouse'; import { EVENT_TYPE } from 'lib/constants'; import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; @@ -69,8 +68,30 @@ async function clickhouseQuery( }); return rawQuery( + // ` + // select + // sum(t.c) as "pageviews", + // count(distinct t.session_id) as "visitors", + // count(distinct t.visit_id) as "visits", + // sum(if(t.c = 1, 1, 0)) as "bounces", + // sum(max_time-min_time) as "totaltime" + // from ( + // select + // session_id, + // visit_id, + // count(*) c, + // min(created_at) min_time, + // max(created_at) max_time + // from website_event + // where website_id = {websiteId:UUID} + // and created_at between {startDate:DateTime64} and {endDate:DateTime64} + // and event_type = {eventType:UInt32} + // ${filterQuery} + // group by session_id, visit_id + // ) as t; + // `, ` - select + select sum(views) as "pageviews", uniq(session_id) as "visitors", uniq(visit_id) as "visits", diff --git a/src/queries/analytics/pageviews/getPageviewMetrics.ts b/src/queries/analytics/pageviews/getPageviewMetrics.ts index ccfe4ef032..4ac101b2cc 100644 --- a/src/queries/analytics/pageviews/getPageviewMetrics.ts +++ b/src/queries/analytics/pageviews/getPageviewMetrics.ts @@ -111,7 +111,25 @@ async function clickhouseQuery( groupByQuery = 'group by x'; } + // let excludeDomain = ''; + // if (column === 'referrer_domain') { + // excludeDomain = `and referrer_domain != {websiteDomain:String} and referrer_domain != ''`; + // } + return rawQuery( + // ` + // select ${column} x, count(*) y + // from website_event + // where website_id = {websiteId:UUID} + // and created_at between {startDate:DateTime64} and {endDate:DateTime64} + // and event_type = {eventType:UInt32} + // ${excludeDomain} + // ${filterQuery} + // group by x + // order by y desc + // limit ${limit} + // offset ${offset} + // `, ` select g.t as x, count(*) as y diff --git a/src/queries/analytics/pageviews/getPageviewStats.ts b/src/queries/analytics/pageviews/getPageviewStats.ts index f6ea1e08fc..c378d48a6b 100644 --- a/src/queries/analytics/pageviews/getPageviewStats.ts +++ b/src/queries/analytics/pageviews/getPageviewStats.ts @@ -51,12 +51,29 @@ async function clickhouseQuery( const columnQuery = unit === 'minute' ? 'count(*)' : 'sum(views)'; return rawQuery( + // ` + // select + // ${getDateStringSQL('g.t', unit)} as x, + // g.y as y + // from ( + // select + // ${getDateSQL('created_at', unit, timezone)} as t, + // count(*) as y + // from website_event + // where website_id = {websiteId:UUID} + // and created_at between {startDate:DateTime64} and {endDate:DateTime64} + // and event_type = {eventType:UInt32} + // ${filterQuery} + // group by t + // ) as g + // order by t + // `, ` select - ${getDateStringSQL('g.t', unit)} as x, + ${getDateStringSQL('g.t', unit)} as x, g.y as y from ( - select + select ${getDateSQL('created_at', unit, timezone)} as t, ${columnQuery} as y from ${table} website_event From 9882ff24f6fecd317159705d0f6f32244ac382cd Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Thu, 1 Aug 2024 15:32:49 -0700 Subject: [PATCH 054/132] route dashboard queries based on filters selected --- src/lib/clickhouse.ts | 42 +++++++- src/lib/constants.ts | 15 +-- .../analytics/events/getEventMetrics.ts | 34 +++++-- src/queries/analytics/getWebsiteStats.ts | 59 ++++++------ .../analytics/pageviews/getPageviewMetrics.ts | 95 +++++++++++-------- .../analytics/pageviews/getPageviewStats.ts | 55 +++++------ .../analytics/sessions/getSessionMetrics.ts | 4 +- .../analytics/sessions/getSessionStats.ts | 4 +- 8 files changed, 191 insertions(+), 117 deletions(-) diff --git a/src/lib/clickhouse.ts b/src/lib/clickhouse.ts index 2bda9bfa85..4c544a71d9 100644 --- a/src/lib/clickhouse.ts +++ b/src/lib/clickhouse.ts @@ -3,7 +3,7 @@ import dateFormat from 'dateformat'; import debug from 'debug'; import { CLICKHOUSE } from 'lib/db'; import { PageParams, QueryFilters, QueryOptions } from './types'; -import { DEFAULT_PAGE_SIZE, OPERATORS } from './constants'; +import { EVENT_COLUMNS, DEFAULT_PAGE_SIZE, OPERATORS } from './constants'; import { fetchWebsite } from './load'; import { maxDate } from './date'; import { filtersToArray } from './params'; @@ -100,6 +100,26 @@ function getFilterQuery(filters: QueryFilters = {}, options: QueryOptions = {}) return query.join('\n'); } +function getSessionFilterQuery(filters: QueryFilters = {}, options: QueryOptions = {}) { + const query = filtersToArray(filters, options).reduce((arr, { name, column, operator }) => { + if (column) { + if (EVENT_COLUMNS.includes(name)) { + arr.push(`and has(${column}, {${name}:String})`); + + if (name === 'referrer') { + arr.push('and not has(referrer_domain, {websiteDomain:String})'); + } + } else { + arr.push(`and ${mapFilter(column, operator, name)}`); + } + } + + return arr; + }, []); + + return query.join('\n'); +} + function getDateQuery(filters: QueryFilters = {}) { const { startDate, endDate } = filters; @@ -139,6 +159,25 @@ async function parseFilters(websiteId: string, filters: QueryFilters = {}, optio }; } +async function parseSessionFilters( + websiteId: string, + filters: QueryFilters = {}, + options?: QueryOptions, +) { + const website = await fetchWebsite(websiteId); + + return { + filterQuery: getSessionFilterQuery(filters, options), + dateQuery: getDateQuery(filters), + params: { + ...getFilterParams(filters), + websiteId, + startDate: maxDate(filters.startDate, new Date(website?.resetAt)), + websiteDomain: website.domain, + }, + }; +} + async function pagedQuery( query: string, queryParams: { [key: string]: any }, @@ -221,6 +260,7 @@ export default { getDateFormat, getFilterQuery, parseFilters, + parseSessionFilters, pagedQuery, findUnique, findFirst, diff --git a/src/lib/constants.ts b/src/lib/constants.ts index aa1b3c0f23..9d696f114c 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -33,16 +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', - 'host', -]; +export const EVENT_COLUMNS = ['url', 'entry', 'exit', 'referrer', 'title', 'query', 'event']; export const SESSION_COLUMNS = [ 'browser', @@ -58,8 +49,8 @@ export const SESSION_COLUMNS = [ export const FILTER_COLUMNS = { url: 'url_path', - entry: 'entry_url', - exit: 'exit_url', + entry: 'url_path', + exit: 'url_path', referrer: 'referrer_domain', host: 'hostname', title: 'page_title', diff --git a/src/queries/analytics/events/getEventMetrics.ts b/src/queries/analytics/events/getEventMetrics.ts index 3623213584..504cea119a 100644 --- a/src/queries/analytics/events/getEventMetrics.ts +++ b/src/queries/analytics/events/getEventMetrics.ts @@ -1,8 +1,8 @@ -import prisma from 'lib/prisma'; import clickhouse from 'lib/clickhouse'; -import { runQuery, CLICKHOUSE, PRISMA } from 'lib/db'; -import { WebsiteEventMetric, QueryFilters } from 'lib/types'; import { EVENT_TYPE } from 'lib/constants'; +import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; +import prisma from 'lib/prisma'; +import { QueryFilters, WebsiteEventMetric } from 'lib/types'; export async function getEventMetrics( ...args: [websiteId: string, filters: QueryFilters] @@ -51,8 +51,24 @@ async function clickhouseQuery( eventType: EVENT_TYPE.customEvent, }); - return rawQuery( - ` + let sql = ''; + + if (filterQuery) { + sql = ` + select + event_name x, + ${getDateSQL('created_at', unit, timezone)} t, + count(*) y + from website_event + where website_id = {websiteId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} + and event_type = {eventType:UInt32} + ${filterQuery} + group by x, t + order by t + `; + } else { + sql = ` select event_name x, ${getDateSQL('created_at', unit, timezone)} t, @@ -64,13 +80,13 @@ async function clickhouseQuery( where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type = {eventType:UInt32} - ${filterQuery} ) as g group by x, t order by t - `, - params, - ).then(a => { + `; + } + + return rawQuery(sql, params).then(a => { return Object.values(a).map(a => { return { x: a.x, t: a.t, y: Number(a.y) }; }); diff --git a/src/queries/analytics/getWebsiteStats.ts b/src/queries/analytics/getWebsiteStats.ts index 7d8a76f69b..09eebb919f 100644 --- a/src/queries/analytics/getWebsiteStats.ts +++ b/src/queries/analytics/getWebsiteStats.ts @@ -3,6 +3,7 @@ import { EVENT_TYPE } from 'lib/constants'; import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; import prisma from 'lib/prisma'; import { QueryFilters } from 'lib/types'; +import { EVENT_COLUMNS } from 'lib/constants'; export async function getWebsiteStats( ...args: [websiteId: string, filters: QueryFilters] @@ -67,30 +68,33 @@ async function clickhouseQuery( eventType: EVENT_TYPE.pageView, }); - return rawQuery( - // ` - // select - // sum(t.c) as "pageviews", - // count(distinct t.session_id) as "visitors", - // count(distinct t.visit_id) as "visits", - // sum(if(t.c = 1, 1, 0)) as "bounces", - // sum(max_time-min_time) as "totaltime" - // from ( - // select - // session_id, - // visit_id, - // count(*) c, - // min(created_at) min_time, - // max(created_at) max_time - // from website_event - // where website_id = {websiteId:UUID} - // and created_at between {startDate:DateTime64} and {endDate:DateTime64} - // and event_type = {eventType:UInt32} - // ${filterQuery} - // group by session_id, visit_id - // ) as t; - // `, - ` + let sql = ''; + + if (EVENT_COLUMNS.some(item => Object.keys(filters).includes(item))) { + sql = ` + select + sum(t.c) as "pageviews", + count(distinct t.session_id) as "visitors", + count(distinct t.visit_id) as "visits", + sum(if(t.c = 1, 1, 0)) as "bounces", + sum(max_time-min_time) as "totaltime" + from ( + select + session_id, + visit_id, + count(*) c, + min(created_at) min_time, + max(created_at) max_time + from website_event + where website_id = {websiteId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} + and event_type = {eventType:UInt32} + ${filterQuery} + group by session_id, visit_id + ) as t; + `; + } else { + sql = ` select sum(views) as "pageviews", uniq(session_id) as "visitors", @@ -102,9 +106,10 @@ async function clickhouseQuery( and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type = {eventType:UInt32} ${filterQuery}; - `, - params, - ).then(result => { + `; + } + + return rawQuery(sql, params).then(result => { return Object.values(result).map((a: any) => { return { pageviews: Number(a.pageviews), diff --git a/src/queries/analytics/pageviews/getPageviewMetrics.ts b/src/queries/analytics/pageviews/getPageviewMetrics.ts index 4ac101b2cc..f734b1ddc4 100644 --- a/src/queries/analytics/pageviews/getPageviewMetrics.ts +++ b/src/queries/analytics/pageviews/getPageviewMetrics.ts @@ -1,5 +1,5 @@ import clickhouse from 'lib/clickhouse'; -import { EVENT_TYPE, FILTER_COLUMNS, SESSION_COLUMNS } from 'lib/constants'; +import { EVENT_COLUMNS, EVENT_TYPE, FILTER_COLUMNS, SESSION_COLUMNS } from 'lib/constants'; import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; import prisma from 'lib/prisma'; import { QueryFilters } from 'lib/types'; @@ -91,46 +91,66 @@ async function clickhouseQuery( }); let excludeDomain = ''; - let groupByQuery = ''; + let sql = ''; - if (column === 'referrer_domain') { - excludeDomain = `and t != {websiteDomain:String} and t != ''`; - } + if (EVENT_COLUMNS.some(item => Object.keys(filters).includes(item))) { + let entryExitQuery = ''; - let columnQuery = `arrayJoin(${column})`; + if (column === 'referrer_domain') { + excludeDomain = `and referrer_domain != {websiteDomain:String} and referrer_domain != ''`; + } - if (type === 'entry') { - columnQuery = `visit_id x, argMinMerge(${column})`; - } + if (type === 'entry' || type === 'exit') { + const aggregrate = type === 'entry' ? 'min' : 'max'; - if (type === 'exit') { - columnQuery = `visit_id x, argMaxMerge(${column})`; - } + entryExitQuery = ` + JOIN (select visit_id, + ${aggregrate}(created_at) target_created_at + from website_event + where website_id = {websiteId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} + and event_type = {eventType:UInt32} + group by visit_id) x + ON x.visit_id = website_event.visit_id + and x.target_created_at = website_event.created_at`; + } - if (type === 'entry' || type === 'exit') { - groupByQuery = 'group by x'; - } + sql = ` + select ${column} x, count(*) y + from website_event + ${entryExitQuery} + where website_id = {websiteId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} + and event_type = {eventType:UInt32} + ${excludeDomain} + ${filterQuery} + group by x + order by y desc + limit ${limit} + offset ${offset} + `; + } else { + let groupByQuery = ''; - // let excludeDomain = ''; - // if (column === 'referrer_domain') { - // excludeDomain = `and referrer_domain != {websiteDomain:String} and referrer_domain != ''`; - // } + if (column === 'referrer_domain') { + excludeDomain = `and t != {websiteDomain:String} and t != ''`; + } - return rawQuery( - // ` - // select ${column} x, count(*) y - // from website_event - // where website_id = {websiteId:UUID} - // and created_at between {startDate:DateTime64} and {endDate:DateTime64} - // and event_type = {eventType:UInt32} - // ${excludeDomain} - // ${filterQuery} - // group by x - // order by y desc - // limit ${limit} - // offset ${offset} - // `, - ` + let columnQuery = `arrayJoin(${column})`; + + if (type === 'entry') { + columnQuery = `visit_id x, argMinMerge(entry_url)`; + } + + if (type === 'exit') { + columnQuery = `visit_id x, argMaxMerge(exit_url)`; + } + + if (type === 'entry' || type === 'exit') { + groupByQuery = 'group by x'; + } + + sql = ` select g.t as x, count(*) as y from ( @@ -146,9 +166,10 @@ async function clickhouseQuery( order by y desc limit ${limit} offset ${offset} - `, - params, - ).then((result: any) => { + `; + } + + return rawQuery(sql, params).then((result: any) => { return Object.values(result).map((a: any) => { return { x: a.x, y: Number(a.y) }; }); diff --git a/src/queries/analytics/pageviews/getPageviewStats.ts b/src/queries/analytics/pageviews/getPageviewStats.ts index c378d48a6b..1d027e794f 100644 --- a/src/queries/analytics/pageviews/getPageviewStats.ts +++ b/src/queries/analytics/pageviews/getPageviewStats.ts @@ -1,7 +1,7 @@ import clickhouse from 'lib/clickhouse'; import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; import prisma from 'lib/prisma'; -import { EVENT_TYPE } from 'lib/constants'; +import { EVENT_COLUMNS, EVENT_TYPE } from 'lib/constants'; import { QueryFilters } from 'lib/types'; export async function getPageviewStats(...args: [websiteId: string, filters: QueryFilters]) { @@ -47,36 +47,18 @@ async function clickhouseQuery( eventType: EVENT_TYPE.pageView, }); - const table = unit === 'minute' ? 'website_event' : 'website_event_stats_hourly'; - const columnQuery = unit === 'minute' ? 'count(*)' : 'sum(views)'; + let sql = ''; - return rawQuery( - // ` - // select - // ${getDateStringSQL('g.t', unit)} as x, - // g.y as y - // from ( - // select - // ${getDateSQL('created_at', unit, timezone)} as t, - // count(*) as y - // from website_event - // where website_id = {websiteId:UUID} - // and created_at between {startDate:DateTime64} and {endDate:DateTime64} - // and event_type = {eventType:UInt32} - // ${filterQuery} - // group by t - // ) as g - // order by t - // `, - ` + if (EVENT_COLUMNS.some(item => Object.keys(filters).includes(item)) || unit === 'minute') { + sql = ` select ${getDateStringSQL('g.t', unit)} as x, g.y as y from ( select ${getDateSQL('created_at', unit, timezone)} as t, - ${columnQuery} as y - from ${table} website_event + count(*) as y + from website_event where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type = {eventType:UInt32} @@ -84,9 +66,28 @@ async function clickhouseQuery( group by t ) as g order by t - `, - params, - ).then(result => { + `; + } else { + sql = ` + select + ${getDateStringSQL('g.t', unit)} as x, + g.y as y + from ( + select + ${getDateSQL('created_at', unit, timezone)} as t, + sum(views)as y + from website_event_stats_hourly website_event + where website_id = {websiteId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} + and event_type = {eventType:UInt32} + ${filterQuery} + group by t + ) as g + order by t + `; + } + + return rawQuery(sql, params).then(result => { return Object.values(result).map((a: any) => { return { x: a.x, y: Number(a.y) }; }); diff --git a/src/queries/analytics/sessions/getSessionMetrics.ts b/src/queries/analytics/sessions/getSessionMetrics.ts index 9baf2a5c07..3e6f53c0af 100644 --- a/src/queries/analytics/sessions/getSessionMetrics.ts +++ b/src/queries/analytics/sessions/getSessionMetrics.ts @@ -64,8 +64,8 @@ async function clickhouseQuery( offset: number = 0, ): Promise<{ x: string; y: number }[]> { const column = FILTER_COLUMNS[type] || type; - const { parseFilters, rawQuery } = clickhouse; - const { filterQuery, params } = await parseFilters(websiteId, { + const { parseSessionFilters, rawQuery } = clickhouse; + const { filterQuery, params } = await parseSessionFilters(websiteId, { ...filters, eventType: EVENT_TYPE.pageView, }); diff --git a/src/queries/analytics/sessions/getSessionStats.ts b/src/queries/analytics/sessions/getSessionStats.ts index 7bba14df1b..fa0ed6a96d 100644 --- a/src/queries/analytics/sessions/getSessionStats.ts +++ b/src/queries/analytics/sessions/getSessionStats.ts @@ -41,8 +41,8 @@ async function clickhouseQuery( filters: QueryFilters, ): Promise<{ x: string; y: number }[]> { const { timezone = 'UTC', unit = 'day' } = filters; - const { parseFilters, rawQuery, getDateStringSQL, getDateSQL } = clickhouse; - const { filterQuery, params } = await parseFilters(websiteId, { + const { parseSessionFilters, rawQuery, getDateStringSQL, getDateSQL } = clickhouse; + const { filterQuery, params } = await parseSessionFilters(websiteId, { ...filters, eventType: EVENT_TYPE.pageView, }); From 61dfa1391e80558e1939987562e2f4ce95ba33d3 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Thu, 1 Aug 2024 15:34:35 -0700 Subject: [PATCH 055/132] add projection code --- db/clickhouse/schema.sql | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/db/clickhouse/schema.sql b/db/clickhouse/schema.sql index aadc5402b6..8bdcfc0b20 100644 --- a/db/clickhouse/schema.sql +++ b/db/clickhouse/schema.sql @@ -170,4 +170,19 @@ GROUP BY website_id, subdivision1, city, event_type, - timestamp); \ No newline at end of file + timestamp); + +-- projections +ALTER TABLE umami.website_event +ADD PROJECTION website_event_url_path_projection ( +SELECT * ORDER BY toStartOfDay(created_at), website_id, url_path, created_at +); + +ALTER TABLE umami.website_event MATERIALIZE PROJECTION website_event_url_path_projection; + +ALTER TABLE umami.website_event +ADD PROJECTION website_event_referrer_domain_projection ( +SELECT * ORDER BY toStartOfDay(created_at), website_id, referrer_domain, created_at +); + +ALTER TABLE umami.website_event MATERIALIZE PROJECTION website_event_referrer_domain_projection; From 57a23bab2d60a4c592886cb357788bb0cae71cd4 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Thu, 1 Aug 2024 16:16:18 -0700 Subject: [PATCH 056/132] fix hourly order by --- db/clickhouse/schema.sql | 1 - 1 file changed, 1 deletion(-) diff --git a/db/clickhouse/schema.sql b/db/clickhouse/schema.sql index 8bdcfc0b20..ca42134d26 100644 --- a/db/clickhouse/schema.sql +++ b/db/clickhouse/schema.sql @@ -101,7 +101,6 @@ CREATE TABLE umami.website_event_stats_hourly ENGINE = AggregatingMergeTree PARTITION BY toYYYYMM(created_at) ORDER BY (toStartOfDay(created_at), website_id, session_id, visit_id, created_at) - PRIMARY KEY (toStartOfDay(created_at), website_id, session_id, visit_id) CREATE MATERIALIZED VIEW umami.website_event_stats_hourly_mv TO umami.website_event_stats_hourly From 3207b0ce06fd7d9a2f20a8044dc5bf35a412587a Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Thu, 1 Aug 2024 16:40:48 -0700 Subject: [PATCH 057/132] revert AggregatingMergeTree order by --- db/clickhouse/schema.sql | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/db/clickhouse/schema.sql b/db/clickhouse/schema.sql index ca42134d26..02be2e38f3 100644 --- a/db/clickhouse/schema.sql +++ b/db/clickhouse/schema.sql @@ -29,11 +29,11 @@ CREATE TABLE umami.website_event created_at DateTime('UTC'), job_id Nullable(UUID) ) - engine = MergeTree - PARTITION BY toYYYYMM(created_at) - ORDER BY (toStartOfHour(created_at), website_id, session_id, visit_id, created_at) - PRIMARY KEY (toStartOfHour(created_at), website_id, session_id, visit_id) - SETTINGS index_granularity = 8192; +ENGINE = MergeTree + PARTITION BY toYYYYMM(created_at) + ORDER BY (toStartOfHour(created_at), website_id, session_id, visit_id, created_at) + PRIMARY KEY (toStartOfHour(created_at), website_id, session_id, visit_id) + SETTINGS index_granularity = 8192; CREATE TABLE umami.event_data ( @@ -50,9 +50,9 @@ CREATE TABLE umami.event_data created_at DateTime('UTC'), job_id Nullable(UUID) ) - engine = MergeTree - ORDER BY (website_id, event_id, data_key, created_at) - SETTINGS index_granularity = 8192; +ENGINE = MergeTree + ORDER BY (website_id, event_id, data_key, created_at) + SETTINGS index_granularity = 8192; CREATE TABLE umami.session_data ( @@ -66,9 +66,9 @@ CREATE TABLE umami.session_data created_at DateTime('UTC'), job_id Nullable(UUID) ) - engine = MergeTree - ORDER BY (website_id, session_id, data_key, created_at) - SETTINGS index_granularity = 8192; +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 @@ -99,8 +99,15 @@ CREATE TABLE umami.website_event_stats_hourly created_at Datetime('UTC') ) ENGINE = AggregatingMergeTree - PARTITION BY toYYYYMM(created_at) - ORDER BY (toStartOfDay(created_at), website_id, session_id, visit_id, created_at) + 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 From 9c3205784146e06dc50f69bc2a1c8bf169745800 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Thu, 1 Aug 2024 21:05:43 -0700 Subject: [PATCH 058/132] Redo events tab to show all events. --- .../event-data/EventDataParameters.tsx | 2 +- .../[websiteId]/events/EventDataPage.tsx | 12 ------ .../[websiteId]/events/EventDataTable.tsx | 37 ---------------- .../[websiteId]/events/EventsDataTable.tsx | 25 +++++++++++ .../[websiteId]/events/EventsPage.tsx | 12 ++++++ .../[websiteId]/events/EventsTable.tsx | 43 +++++++++++++++++++ .../[websiteId]/events/WebsiteEventData.tsx | 4 +- .../websites/[websiteId]/events/page.tsx | 4 +- src/components/common/DataTable.tsx | 4 +- .../hooks/queries/useFilterQuery.ts | 8 ++-- .../hooks/queries/useWebsiteEvents.ts | 10 +++-- src/components/messages.ts | 2 + .../events.ts => events/[eventId]/data.ts} | 0 .../api/websites/[websiteId]/events/index.ts | 42 ++++++++++++++++++ .../{events.ts => events/series.ts} | 0 .../{getEvents.ts => getWebsiteEvents.ts} | 2 +- src/queries/analytics/getRealtimeData.ts | 9 +++- src/queries/index.ts | 2 +- 18 files changed, 152 insertions(+), 66 deletions(-) delete mode 100644 src/app/(main)/websites/[websiteId]/events/EventDataPage.tsx delete mode 100644 src/app/(main)/websites/[websiteId]/events/EventDataTable.tsx create mode 100644 src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx create mode 100644 src/app/(main)/websites/[websiteId]/events/EventsPage.tsx create mode 100644 src/app/(main)/websites/[websiteId]/events/EventsTable.tsx rename src/pages/api/{event-data/events.ts => events/[eventId]/data.ts} (100%) create mode 100644 src/pages/api/websites/[websiteId]/events/index.ts rename src/pages/api/websites/[websiteId]/{events.ts => events/series.ts} (100%) rename src/queries/analytics/events/{getEvents.ts => getWebsiteEvents.ts} (97%) diff --git a/src/app/(main)/reports/event-data/EventDataParameters.tsx b/src/app/(main)/reports/event-data/EventDataParameters.tsx index e0afda3f5a..7b61c11297 100644 --- a/src/app/(main)/reports/event-data/EventDataParameters.tsx +++ b/src/app/(main)/reports/event-data/EventDataParameters.tsx @@ -48,7 +48,7 @@ export function EventDataParameters() { groups, }; - const handleSubmit = values => { + const handleSubmit = (values: any) => { runReport(values); }; diff --git a/src/app/(main)/websites/[websiteId]/events/EventDataPage.tsx b/src/app/(main)/websites/[websiteId]/events/EventDataPage.tsx deleted file mode 100644 index 77d367e58f..0000000000 --- a/src/app/(main)/websites/[websiteId]/events/EventDataPage.tsx +++ /dev/null @@ -1,12 +0,0 @@ -'use client'; -import WebsiteHeader from '../WebsiteHeader'; -import WebsiteEventData from './WebsiteEventData'; - -export default function EventDataPage({ websiteId }) { - return ( - <> - - - - ); -} diff --git a/src/app/(main)/websites/[websiteId]/events/EventDataTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventDataTable.tsx deleted file mode 100644 index 71c3699222..0000000000 --- a/src/app/(main)/websites/[websiteId]/events/EventDataTable.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import Link from 'next/link'; -import { GridTable, GridColumn } from 'react-basics'; -import { useMessages, useNavigation } from 'components/hooks'; -import Empty from 'components/common/Empty'; -import { DATA_TYPES } from 'lib/constants'; - -export function EventDataTable({ data = [] }) { - const { formatMessage, labels } = useMessages(); - const { renderUrl } = useNavigation(); - - if (data.length === 0) { - return ; - } - - return ( - - - {row => ( - - {row.eventName} - - )} - - - {row => row.fieldName} - - - {row => DATA_TYPES[row.dataType]} - - - {({ total }) => total.toLocaleString()} - - - ); -} - -export default EventDataTable; diff --git a/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx new file mode 100644 index 0000000000..f2bf7cb9d2 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx @@ -0,0 +1,25 @@ +import { useWebsiteEvents } from 'components/hooks'; +import EventsTable from './EventsTable'; +import DataTable from 'components/common/DataTable'; +import { ReactNode } from 'react'; + +export default function EventsDataTable({ + websiteId, + children, +}: { + websiteId?: string; + teamId?: string; + children?: ReactNode; +}) { + const queryResult = useWebsiteEvents(websiteId); + + if (queryResult?.result?.data?.length === 0) { + return children; + } + + return ( + + {({ data }) => } + + ); +} diff --git a/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx b/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx new file mode 100644 index 0000000000..8f793d81ed --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx @@ -0,0 +1,12 @@ +'use client'; +import WebsiteHeader from '../WebsiteHeader'; +import EventsDataTable from './EventsDataTable'; + +export default function EventsPage({ websiteId }) { + return ( + <> + + + + ); +} diff --git a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx new file mode 100644 index 0000000000..5a625ee94c --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx @@ -0,0 +1,43 @@ +import { GridTable, GridColumn } from 'react-basics'; +import { useLocale, useMessages } from 'components/hooks'; +import Empty from 'components/common/Empty'; +import { formatDistance } from 'date-fns'; +import Profile from 'components/common/Profile'; +import Link from 'next/link'; + +export function EventsTable({ data = [] }) { + const { dateLocale } = useLocale(); + const { formatMessage, labels } = useMessages(); + + if (data.length === 0) { + return ; + } + + return ( + + + + {row => ( + + + + )} + + + {row => formatMessage(row.eventName ? labels.triggeredEvent : labels.viewedPage)} + + + + + {row => + formatDistance(new Date(row.createdAt), new Date(), { + addSuffix: true, + locale: dateLocale, + }) + } + + + ); +} + +export default EventsTable; diff --git a/src/app/(main)/websites/[websiteId]/events/WebsiteEventData.tsx b/src/app/(main)/websites/[websiteId]/events/WebsiteEventData.tsx index d7d24ceed1..296c8a66ba 100644 --- a/src/app/(main)/websites/[websiteId]/events/WebsiteEventData.tsx +++ b/src/app/(main)/websites/[websiteId]/events/WebsiteEventData.tsx @@ -1,5 +1,5 @@ import { Flexbox, Loading } from 'react-basics'; -import EventDataTable from './EventDataTable'; +import EventsTable from './EventsTable'; import EventDataValueTable from './EventDataValueTable'; import { EventDataMetricsBar } from './EventDataMetricsBar'; import { useDateRange, useApi, useNavigation } from 'components/hooks'; @@ -33,7 +33,7 @@ export default function WebsiteEventData({ websiteId }) { return ( - {!event && } + {!event && } {isLoading && } {event && data && } diff --git a/src/app/(main)/websites/[websiteId]/events/page.tsx b/src/app/(main)/websites/[websiteId]/events/page.tsx index 24cf4feaab..b5dc4d62ca 100644 --- a/src/app/(main)/websites/[websiteId]/events/page.tsx +++ b/src/app/(main)/websites/[websiteId]/events/page.tsx @@ -1,8 +1,8 @@ import { Metadata } from 'next'; -import EventDataPage from './EventDataPage'; +import EventsPage from './EventsPage'; export default async function ({ params: { websiteId } }) { - return ; + return ; } export const metadata: Metadata = { diff --git a/src/components/common/DataTable.tsx b/src/components/common/DataTable.tsx index ed910def7d..041c2ba5ef 100644 --- a/src/components/common/DataTable.tsx +++ b/src/components/common/DataTable.tsx @@ -1,7 +1,7 @@ import { ReactNode } from 'react'; import classNames from 'classnames'; import { Banner, Loading, SearchField } from 'react-basics'; -import { useMessages } from 'components/hooks'; +import { useMessages, useNavigation } from 'components/hooks'; import Empty from 'components/common/Empty'; import Pager from 'components/common/Pager'; import { FilterQueryResult } from 'lib/types'; @@ -35,6 +35,7 @@ export function DataTable({ const { query } = params || {}; const hasData = Boolean(!isLoading && data?.length); const noResults = Boolean(!isLoading && query && !hasData); + const { router, renderUrl } = useNavigation(); const handleSearch = (query: string) => { setParams({ ...params, query, page: params.page ? page : 1 }); @@ -42,6 +43,7 @@ export function DataTable({ const handlePageChange = (page: number) => { setParams({ ...params, query, page }); + router.push(renderUrl({ page })); }; if (error) { diff --git a/src/components/hooks/queries/useFilterQuery.ts b/src/components/hooks/queries/useFilterQuery.ts index 5963d09974..82fc4d614f 100644 --- a/src/components/hooks/queries/useFilterQuery.ts +++ b/src/components/hooks/queries/useFilterQuery.ts @@ -2,15 +2,17 @@ import { UseQueryOptions } from '@tanstack/react-query'; import { useState } from 'react'; import { useApi } from './useApi'; import { PageResult, PageParams, FilterQueryResult } from 'lib/types'; +import { useNavigation } from '../useNavigation'; export function useFilterQuery({ queryKey, queryFn, ...options }: Omit & { queryFn: (params?: object) => any }): FilterQueryResult { - const [params, setParams] = useState({ + const { query: queryParams } = useNavigation(); + const [params, setParams] = useState({ query: '', - page: 1, + page: +queryParams.page || 1, }); const { useQuery } = useApi(); @@ -21,7 +23,7 @@ export function useFilterQuery({ }); return { - result: data as PageResult, + result: data as PageResult, query, params, setParams, diff --git a/src/components/hooks/queries/useWebsiteEvents.ts b/src/components/hooks/queries/useWebsiteEvents.ts index 588d4fb568..f0132470fa 100644 --- a/src/components/hooks/queries/useWebsiteEvents.ts +++ b/src/components/hooks/queries/useWebsiteEvents.ts @@ -1,17 +1,19 @@ import useApi from './useApi'; -import { useFilterParams } from '../useFilterParams'; import { UseQueryOptions } from '@tanstack/react-query'; +import { useFilterParams } from '../useFilterParams'; +import { useFilterQuery } from 'components/hooks'; export function useWebsiteEvents( websiteId: string, options?: Omit, ) { - const { get, useQuery } = useApi(); + const { get } = useApi(); const params = useFilterParams(websiteId); - return useQuery({ + return useFilterQuery({ queryKey: ['websites:events', { websiteId, ...params }], - queryFn: () => get(`/websites/${websiteId}/events`, params), + queryFn: pageParams => + get(`/websites/${websiteId}/events`, { ...params, ...pageParams, pageSize: 20 }), enabled: !!websiteId, ...options, }); diff --git a/src/components/messages.ts b/src/components/messages.ts index 2dfe61ee65..703ccba3c8 100644 --- a/src/components/messages.ts +++ b/src/components/messages.ts @@ -130,6 +130,7 @@ export const labels = defineMessages({ selectRole: { id: 'label.select-role', defaultMessage: 'Select role' }, selectDate: { id: 'label.select-date', defaultMessage: 'Select date' }, all: { id: 'label.all', defaultMessage: 'All' }, + session: { id: 'label.session', defaultMessage: 'Session' }, sessions: { id: 'label.sessions', defaultMessage: 'Sessions' }, pageNotFound: { id: 'message.page-not-found', defaultMessage: 'Page not found' }, activityLog: { id: 'label.activity-log', defaultMessage: 'Activity log' }, @@ -275,6 +276,7 @@ export const labels = defineMessages({ lastSeen: { id: 'label.last-seen', defaultMessage: 'Last seen' }, firstSeen: { id: 'label.first-seen', defaultMessage: 'First seen' }, properties: { id: 'label.properties', defaultMessage: 'Properties' }, + path: { id: 'label.path', defaultMessage: 'Path' }, }); export const messages = defineMessages({ diff --git a/src/pages/api/event-data/events.ts b/src/pages/api/events/[eventId]/data.ts similarity index 100% rename from src/pages/api/event-data/events.ts rename to src/pages/api/events/[eventId]/data.ts diff --git a/src/pages/api/websites/[websiteId]/events/index.ts b/src/pages/api/websites/[websiteId]/events/index.ts new file mode 100644 index 0000000000..2aa791b11d --- /dev/null +++ b/src/pages/api/websites/[websiteId]/events/index.ts @@ -0,0 +1,42 @@ +import * as yup from 'yup'; +import { canViewWebsite } from 'lib/auth'; +import { useAuth, useCors, useValidate } from 'lib/middleware'; +import { NextApiRequestQueryBody, PageParams } from 'lib/types'; +import { NextApiResponse } from 'next'; +import { methodNotAllowed, ok, unauthorized } from 'next-basics'; +import { pageInfo } from 'lib/schema'; +import { getWebsiteEvents } from 'queries'; + +export interface ReportsRequestQuery extends PageParams { + websiteId: string; +} + +const schema = { + GET: yup.object().shape({ + websiteId: yup.string().uuid().required(), + ...pageInfo, + }), +}; + +export default async ( + req: NextApiRequestQueryBody, + res: NextApiResponse, +) => { + await useCors(req, res); + await useAuth(req, res); + await useValidate(schema, req, res); + + const { websiteId } = req.query; + + if (req.method === 'GET') { + if (!(await canViewWebsite(req.auth, websiteId))) { + return unauthorized(res); + } + + const data = await getWebsiteEvents(websiteId, {}, req.query); + + return ok(res, data); + } + + return methodNotAllowed(res); +}; diff --git a/src/pages/api/websites/[websiteId]/events.ts b/src/pages/api/websites/[websiteId]/events/series.ts similarity index 100% rename from src/pages/api/websites/[websiteId]/events.ts rename to src/pages/api/websites/[websiteId]/events/series.ts diff --git a/src/queries/analytics/events/getEvents.ts b/src/queries/analytics/events/getWebsiteEvents.ts similarity index 97% rename from src/queries/analytics/events/getEvents.ts rename to src/queries/analytics/events/getWebsiteEvents.ts index 540c1a05d8..b76d1ee548 100644 --- a/src/queries/analytics/events/getEvents.ts +++ b/src/queries/analytics/events/getWebsiteEvents.ts @@ -3,7 +3,7 @@ import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; import prisma from 'lib/prisma'; import { PageParams, QueryFilters } from 'lib/types'; -export function getEvents( +export function getWebsiteEvents( ...args: [websiteId: string, filters: QueryFilters, pageParams?: PageParams] ) { return runQuery({ diff --git a/src/queries/analytics/getRealtimeData.ts b/src/queries/analytics/getRealtimeData.ts index e25cc86617..3e691b4edb 100644 --- a/src/queries/analytics/getRealtimeData.ts +++ b/src/queries/analytics/getRealtimeData.ts @@ -1,4 +1,9 @@ -import { getWebsiteSessions, getEvents, getPageviewStats, getSessionStats } from 'queries/index'; +import { + getWebsiteSessions, + getWebsiteEvents, + getPageviewStats, + getSessionStats, +} from 'queries/index'; const MAX_SIZE = 50; @@ -19,7 +24,7 @@ export async function getRealtimeData( const { startDate, timezone } = criteria; const filters = { startDate, endDate: new Date(), unit: 'minute', timezone }; const [events, sessions, pageviews, sessionviews] = await Promise.all([ - getEvents(websiteId, { startDate, timezone }, { pageSize: 10000 }), + getWebsiteEvents(websiteId, { startDate, timezone }, { pageSize: 10000 }), getWebsiteSessions(websiteId, { startDate, timezone }, { pageSize: 10000 }), getPageviewStats(websiteId, filters), getSessionStats(websiteId, filters), diff --git a/src/queries/index.ts b/src/queries/index.ts index 82d40ee5d9..51c1e14b9f 100644 --- a/src/queries/index.ts +++ b/src/queries/index.ts @@ -8,7 +8,7 @@ export * from './analytics/events/getEventDataFields'; export * from './analytics/events/getEventDataStats'; export * from './analytics/events/getEventDataUsage'; export * from './analytics/events/getEventMetrics'; -export * from './analytics/events/getEvents'; +export * from './analytics/events/getWebsiteEvents'; export * from './analytics/events/getEventUsage'; export * from './analytics/events/saveEvent'; export * from './analytics/reports/getFunnel'; From 3262ea02854102b14771ad9e192436f406c61d6d Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Thu, 1 Aug 2024 22:53:17 -0700 Subject: [PATCH 059/132] Updated avatar colors and events table. --- .../[websiteId]/events/EventsTable.tsx | 19 ++-- .../[websiteId]/sessions/SessionsTable.tsx | 4 +- .../[sessionId]/SessionDetailsPage.tsx | 4 +- src/components/common/Avatar.tsx | 88 +++++-------------- src/components/common/Profile.tsx | 41 --------- src/lib/colors.ts | 45 ++++++++++ 6 files changed, 81 insertions(+), 120 deletions(-) delete mode 100644 src/components/common/Profile.tsx create mode 100644 src/lib/colors.ts diff --git a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx index 5a625ee94c..c34ac89b07 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx @@ -2,7 +2,7 @@ import { GridTable, GridColumn } from 'react-basics'; import { useLocale, useMessages } from 'components/hooks'; import Empty from 'components/common/Empty'; import { formatDistance } from 'date-fns'; -import Profile from 'components/common/Profile'; +import Avatar from 'components/common/Avatar'; import Link from 'next/link'; export function EventsTable({ data = [] }) { @@ -15,18 +15,23 @@ export function EventsTable({ data = [] }) { return ( - - + {row => ( - + )} - - {row => formatMessage(row.eventName ? labels.triggeredEvent : labels.viewedPage)} + + {row => { + return ( + <> + {formatMessage(row.eventName ? labels.triggeredEvent : labels.viewedPage)} + {row.eventName} + + ); + }} - {row => diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx index 73148b835a..9dd6a56dda 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx @@ -1,7 +1,7 @@ import Link from 'next/link'; import { GridColumn, GridTable, useBreakpoint } from 'react-basics'; import { useFormat, useLocale, useMessages } from 'components/hooks'; -import Profile from 'components/common/Profile'; +import Avatar from 'components/common/Avatar'; import styles from './SessionsTable.module.css'; import { formatDate } from 'lib/date'; @@ -16,7 +16,7 @@ export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean {row => ( - + {row.id} )} diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx index d4ed503e77..059e041f5a 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx @@ -3,7 +3,7 @@ import WebsiteHeader from '../../WebsiteHeader'; import SessionInfo from './SessionInfo'; import { useWebsiteSession } from 'components/hooks'; import { Loading } from 'react-basics'; -import Profile from 'components/common/Profile'; +import Avatar from 'components/common/Avatar'; import { SessionActivity } from './SessionActivity'; import { SessionStats } from './SessionStats'; import { SessionData } from './SessionData'; @@ -27,7 +27,7 @@ export default function SessionDetailsPage({
- +
diff --git a/src/components/common/Avatar.tsx b/src/components/common/Avatar.tsx index b947704b9a..2e82b0782c 100644 --- a/src/components/common/Avatar.tsx +++ b/src/components/common/Avatar.tsx @@ -1,71 +1,23 @@ -import md5 from 'md5'; -import { colord, extend } from 'colord'; -import harmoniesPlugin from 'colord/plugins/harmonies'; -import mixPlugin from 'colord/plugins/mix'; - -extend([harmoniesPlugin, mixPlugin]); - -const harmonies = [ - //'analogous', - //'complementary', - 'double-split-complementary', - //'rectangle', - 'split-complementary', - 'tetradic', - //'triadic', -]; - -const color = (value: string, invert: boolean = false) => { - const c = colord(value.startsWith('#') ? value : `#${value}`); - - if (invert && c.isDark()) { - return c.invert(); - } - - return c; -}; - -const remix = (hash: string) => { - const a = hash.substring(0, 6); - const b = hash.substring(6, 12); - const c = hash.substring(12, 18); - const d = hash.substring(18, 24); - const e = hash.substring(24, 30); - const f = hash.substring(30, 32); - - const base = [b, c, d, e] - .reduce((acc, val) => { - return acc.mix(color(val), 0.05); - }, color(a)) - .saturate(0.1) - .toHex(); - - const harmony = pick(parseInt(f, 16), harmonies); - - return color(base, true) - .harmonies(harmony) - .map(c => c.toHex()); -}; - -const pick = (num: number, arr: any[]) => { - return arr[num % arr.length]; -}; - -export function Avatar({ value }: { value: string }) { - const hash = md5(value); - const colors = remix(hash); - - return ( - - - - - - - - - - ); +import { useMemo } from 'react'; +import { createAvatar } from '@dicebear/core'; +import { lorelei } from '@dicebear/collection'; +import { getColor, getPastel } from 'lib/colors'; + +const lib = lorelei; + +function Avatar({ seed, size = 128, ...props }: { seed: string; size?: number }) { + const backgroundColor = getPastel(getColor(seed), 4); + + const avatar = useMemo(() => { + return createAvatar(lib, { + ...props, + seed, + size, + backgroundColor: [backgroundColor], + }).toDataUri(); + }, []); + + return Avatar; } export default Avatar; diff --git a/src/components/common/Profile.tsx b/src/components/common/Profile.tsx deleted file mode 100644 index 4054499475..0000000000 --- a/src/components/common/Profile.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { useMemo } from 'react'; -import { createAvatar } from '@dicebear/core'; -import { lorelei } from '@dicebear/collection'; -import md5 from 'md5'; - -const lib = lorelei; - -function convertToPastel(hexColor: string, pastelFactor: number = 0.5) { - // Remove the # if present - hexColor = hexColor.replace(/^#/, ''); - - // Convert hex to RGB - let r = parseInt(hexColor.substring(0, 2), 16); - let g = parseInt(hexColor.substring(2, 4), 16); - let b = parseInt(hexColor.substring(4, 6), 16); - - // Calculate pastel version (mix with white) - //const pastelFactor = 0.5; // Adjust this value to control pastel intensity - - r = Math.floor((r + 255 * pastelFactor) / (1 + pastelFactor)); - g = Math.floor((g + 255 * pastelFactor) / (1 + pastelFactor)); - b = Math.floor((b + 255 * pastelFactor) / (1 + pastelFactor)); - - // Convert back to hex - return `#${((1 << 24) | (r << 16) | (g << 8) | b).toString(16).slice(1)}`; -} - -function Profile({ seed, size = 128, ...props }: { seed: string; size?: number }) { - const avatar = useMemo(() => { - return createAvatar(lib, { - ...props, - seed, - size, - backgroundColor: [convertToPastel(md5(seed).substring(0, 6), 2).replace(/^#/, '')], - }).toDataUri(); - }, []); - - return Avatar; -} - -export default Profile; diff --git a/src/lib/colors.ts b/src/lib/colors.ts new file mode 100644 index 0000000000..ba329805d4 --- /dev/null +++ b/src/lib/colors.ts @@ -0,0 +1,45 @@ +import md5 from 'md5'; + +export const pick = (num: number, arr: any[]) => { + return arr[num % arr.length]; +}; + +export function clamp(num: number, min: number, max: number) { + return num < min ? min : num > max ? max : num; +} + +export function hex2RGB(color: string, min: number = 0, max: number = 255) { + const c = color.replace(/^#/, ''); + const diff = max - min; + + const normalize = (num: number) => { + return Math.floor((num / 255) * diff + min); + }; + + const r = normalize(parseInt(c.substring(0, 2), 16)); + const g = normalize(parseInt(c.substring(2, 4), 16)); + const b = normalize(parseInt(c.substring(4, 6), 16)); + + return { r, g, b }; +} + +export function rgb2Hex(r: number, g: number, b: number, prefix = '') { + return `${prefix}${r.toString(16)}${g.toString(16)}${b.toString(16)}`; +} + +export function getPastel(color: string, factor: number = 0.5, prefix = '') { + let { r, g, b } = hex2RGB(color); + + r = Math.floor((r + 255 * factor) / (1 + factor)); + g = Math.floor((g + 255 * factor) / (1 + factor)); + b = Math.floor((b + 255 * factor) / (1 + factor)); + + return rgb2Hex(r, g, b, prefix); +} + +export function getColor(seed: string, min: number = 0, max: number = 255) { + const color = md5(seed).substring(0, 6); + const { r, g, b } = hex2RGB(color, min, max); + + return rgb2Hex(r, g, b); +} From d02dc6f997855b2b14717b4d7e549d180cf24900 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Thu, 1 Aug 2024 22:57:54 -0700 Subject: [PATCH 060/132] remove parse session filters --- src/lib/clickhouse.ts | 46 ++----------------- src/queries/analytics/getWebsiteStats.ts | 20 +++++--- .../analytics/sessions/getSessionMetrics.ts | 36 +++++++++++---- .../analytics/sessions/getSessionStats.ts | 42 ++++++++++++----- 4 files changed, 75 insertions(+), 69 deletions(-) diff --git a/src/lib/clickhouse.ts b/src/lib/clickhouse.ts index 4c544a71d9..c25eff4e53 100644 --- a/src/lib/clickhouse.ts +++ b/src/lib/clickhouse.ts @@ -2,11 +2,11 @@ import { ClickHouseClient, createClient } from '@clickhouse/client'; import dateFormat from 'dateformat'; import debug from 'debug'; import { CLICKHOUSE } from 'lib/db'; -import { PageParams, QueryFilters, QueryOptions } from './types'; -import { EVENT_COLUMNS, DEFAULT_PAGE_SIZE, OPERATORS } from './constants'; -import { fetchWebsite } from './load'; +import { DEFAULT_PAGE_SIZE, OPERATORS } from './constants'; import { maxDate } from './date'; +import { fetchWebsite } from './load'; import { filtersToArray } from './params'; +import { PageParams, QueryFilters, QueryOptions } from './types'; export const CLICKHOUSE_DATE_FORMATS = { second: '%Y-%m-%dT%H:%i:%S', @@ -100,26 +100,6 @@ function getFilterQuery(filters: QueryFilters = {}, options: QueryOptions = {}) return query.join('\n'); } -function getSessionFilterQuery(filters: QueryFilters = {}, options: QueryOptions = {}) { - const query = filtersToArray(filters, options).reduce((arr, { name, column, operator }) => { - if (column) { - if (EVENT_COLUMNS.includes(name)) { - arr.push(`and has(${column}, {${name}:String})`); - - if (name === 'referrer') { - arr.push('and not has(referrer_domain, {websiteDomain:String})'); - } - } else { - arr.push(`and ${mapFilter(column, operator, name)}`); - } - } - - return arr; - }, []); - - return query.join('\n'); -} - function getDateQuery(filters: QueryFilters = {}) { const { startDate, endDate } = filters; @@ -159,25 +139,6 @@ async function parseFilters(websiteId: string, filters: QueryFilters = {}, optio }; } -async function parseSessionFilters( - websiteId: string, - filters: QueryFilters = {}, - options?: QueryOptions, -) { - const website = await fetchWebsite(websiteId); - - return { - filterQuery: getSessionFilterQuery(filters, options), - dateQuery: getDateQuery(filters), - params: { - ...getFilterParams(filters), - websiteId, - startDate: maxDate(filters.startDate, new Date(website?.resetAt)), - websiteDomain: website.domain, - }, - }; -} - async function pagedQuery( query: string, queryParams: { [key: string]: any }, @@ -260,7 +221,6 @@ export default { getDateFormat, getFilterQuery, parseFilters, - parseSessionFilters, pagedQuery, findUnique, findFirst, diff --git a/src/queries/analytics/getWebsiteStats.ts b/src/queries/analytics/getWebsiteStats.ts index 09eebb919f..c5141d3be7 100644 --- a/src/queries/analytics/getWebsiteStats.ts +++ b/src/queries/analytics/getWebsiteStats.ts @@ -74,8 +74,8 @@ async function clickhouseQuery( sql = ` select sum(t.c) as "pageviews", - count(distinct t.session_id) as "visitors", - count(distinct t.visit_id) as "visits", + uniq(t.session_id) as "visitors", + uniq(t.visit_id) as "visits", sum(if(t.c = 1, 1, 0)) as "bounces", sum(max_time-min_time) as "totaltime" from ( @@ -96,16 +96,24 @@ async function clickhouseQuery( } else { sql = ` select - sum(views) as "pageviews", + sum(t.c) as "pageviews", uniq(session_id) as "visitors", uniq(visit_id) as "visits", - sumIf(1, views = 1) as "bounces", + sumIf(1, t.c = 1) as "bounces", sum(max_time-min_time) as "totaltime" - from website_event_stats_hourly "website_event" + from (select + session_id, + visit_id, + sum(views) c, + min(min_time) min_time, + max(max_time) max_time + from umami.website_event_stats_hourly "website_event" where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type = {eventType:UInt32} - ${filterQuery}; + ${filterQuery} + group by session_id, visit_id + ) as t; `; } diff --git a/src/queries/analytics/sessions/getSessionMetrics.ts b/src/queries/analytics/sessions/getSessionMetrics.ts index 3e6f53c0af..bb8bc4c5c4 100644 --- a/src/queries/analytics/sessions/getSessionMetrics.ts +++ b/src/queries/analytics/sessions/getSessionMetrics.ts @@ -1,5 +1,5 @@ import clickhouse from 'lib/clickhouse'; -import { EVENT_TYPE, FILTER_COLUMNS, SESSION_COLUMNS } from 'lib/constants'; +import { EVENT_COLUMNS, EVENT_TYPE, FILTER_COLUMNS, SESSION_COLUMNS } from 'lib/constants'; import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; import prisma from 'lib/prisma'; import { QueryFilters } from 'lib/types'; @@ -64,15 +64,34 @@ async function clickhouseQuery( offset: number = 0, ): Promise<{ x: string; y: number }[]> { const column = FILTER_COLUMNS[type] || type; - const { parseSessionFilters, rawQuery } = clickhouse; - const { filterQuery, params } = await parseSessionFilters(websiteId, { + const { parseFilters, rawQuery } = clickhouse; + const { filterQuery, params } = await parseFilters(websiteId, { ...filters, eventType: EVENT_TYPE.pageView, }); const includeCountry = column === 'city' || column === 'subdivision1'; - return rawQuery( - ` + let sql = ''; + + if (EVENT_COLUMNS.some(item => Object.keys(filters).includes(item))) { + sql = ` + select + ${column} x, + count(distinct session_id) y + ${includeCountry ? ', country' : ''} + from website_event + where website_id = {websiteId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} + and event_type = {eventType:UInt32} + ${filterQuery} + group by x + ${includeCountry ? ', country' : ''} + order by y desc + limit ${limit} + offset ${offset} + `; + } else { + sql = ` select ${column} x, uniq(session_id) y @@ -87,9 +106,10 @@ async function clickhouseQuery( order by y desc limit ${limit} offset ${offset} - `, - params, - ).then(a => { + `; + } + + return rawQuery(sql, params).then(a => { return Object.values(a).map(a => { return { x: a.x, y: Number(a.y), country: a.country }; }); diff --git a/src/queries/analytics/sessions/getSessionStats.ts b/src/queries/analytics/sessions/getSessionStats.ts index fa0ed6a96d..fa7483333d 100644 --- a/src/queries/analytics/sessions/getSessionStats.ts +++ b/src/queries/analytics/sessions/getSessionStats.ts @@ -1,7 +1,7 @@ import clickhouse from 'lib/clickhouse'; +import { EVENT_COLUMNS, EVENT_TYPE } from 'lib/constants'; import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; import prisma from 'lib/prisma'; -import { EVENT_TYPE } from 'lib/constants'; import { QueryFilters } from 'lib/types'; export async function getSessionStats(...args: [websiteId: string, filters: QueryFilters]) { @@ -41,25 +41,24 @@ async function clickhouseQuery( filters: QueryFilters, ): Promise<{ x: string; y: number }[]> { const { timezone = 'UTC', unit = 'day' } = filters; - const { parseSessionFilters, rawQuery, getDateStringSQL, getDateSQL } = clickhouse; - const { filterQuery, params } = await parseSessionFilters(websiteId, { + const { parseFilters, rawQuery, getDateStringSQL, getDateSQL } = clickhouse; + const { filterQuery, params } = await parseFilters(websiteId, { ...filters, eventType: EVENT_TYPE.pageView, }); - const table = unit === 'minute' ? 'website_event' : 'website_event_stats_hourly'; - const columnQuery = unit === 'minute' ? 'count(distinct session_id)' : 'uniq(session_id)'; + let sql = ''; - return rawQuery( - ` + if (EVENT_COLUMNS.some(item => Object.keys(filters).includes(item)) || unit === 'minute') { + sql = ` select ${getDateStringSQL('g.t', unit)} as x, g.y as y from ( select ${getDateSQL('created_at', unit, timezone)} as t, - ${columnQuery} as y - from ${table} website_event + count(distinct session_id) as y + from website_event where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type = {eventType:UInt32} @@ -67,9 +66,28 @@ async function clickhouseQuery( group by t ) as g order by t - `, - params, - ).then(result => { + `; + } else { + sql = ` + select + ${getDateStringSQL('g.t', unit)} as x, + g.y as y + from ( + select + ${getDateSQL('created_at', unit, timezone)} as t, + uniq(session_id) as y + from website_event_stats_hourly website_event + where website_id = {websiteId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} + and event_type = {eventType:UInt32} + ${filterQuery} + group by t + ) as g + order by t + `; + } + + return rawQuery(sql, params).then(result => { return Object.values(result).map((a: any) => { return { x: a.x, y: Number(a.y) }; }); From 7582670aef1bcb9c5d170b82a2349ab6bc865945 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Fri, 2 Aug 2024 00:14:32 -0700 Subject: [PATCH 061/132] fix retention report invalid date --- src/app/(main)/reports/retention/RetentionTable.tsx | 2 +- src/pages/api/reports/retention.ts | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/app/(main)/reports/retention/RetentionTable.tsx b/src/app/(main)/reports/retention/RetentionTable.tsx index 171c5bd3f1..1770a76468 100644 --- a/src/app/(main)/reports/retention/RetentionTable.tsx +++ b/src/app/(main)/reports/retention/RetentionTable.tsx @@ -52,7 +52,7 @@ export function RetentionTable({ days = DAYS }) { {rows.map(({ date, visitors, records }, rowIndex) => { return (
-
{formatDate(`${date} 00:00:00`, 'PP', locale)}
+
{formatDate(date, 'PP', locale)}
{visitors}
{days.map(day => { if (totalDays - rowIndex < day) { diff --git a/src/pages/api/reports/retention.ts b/src/pages/api/reports/retention.ts index 6ff7bbe1c3..f4d9b7df07 100644 --- a/src/pages/api/reports/retention.ts +++ b/src/pages/api/reports/retention.ts @@ -9,7 +9,8 @@ import * as yup from 'yup'; export interface RetentionRequestBody { websiteId: string; - dateRange: { startDate: string; endDate: string; timezone: string }; + dateRange: { startDate: string; endDate: string }; + timezone: string; } const schema = { @@ -20,9 +21,9 @@ const schema = { .shape({ startDate: yup.date().required(), endDate: yup.date().required(), - timezone: TimezoneTest, }) .required(), + timezone: TimezoneTest, }), }; @@ -37,7 +38,8 @@ export default async ( if (req.method === 'POST') { const { websiteId, - dateRange: { startDate, endDate, timezone }, + dateRange: { startDate, endDate }, + timezone, } = req.body; if (!(await canViewWebsite(req.auth, websiteId))) { From 9d3513256f4b4b92f1e09759a5f7be4279cdd4fd Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 2 Aug 2024 15:25:08 -0700 Subject: [PATCH 062/132] Updated session link. --- src/app/(main)/websites/[websiteId]/events/EventsTable.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx index c34ac89b07..b3414097e2 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx @@ -1,5 +1,5 @@ import { GridTable, GridColumn } from 'react-basics'; -import { useLocale, useMessages } from 'components/hooks'; +import { useLocale, useMessages, useTeamUrl } from 'components/hooks'; import Empty from 'components/common/Empty'; import { formatDistance } from 'date-fns'; import Avatar from 'components/common/Avatar'; @@ -8,6 +8,7 @@ import Link from 'next/link'; export function EventsTable({ data = [] }) { const { dateLocale } = useLocale(); const { formatMessage, labels } = useMessages(); + const { renderTeamUrl } = useTeamUrl(); if (data.length === 0) { return ; @@ -17,7 +18,7 @@ export function EventsTable({ data = [] }) { {row => ( - + )} From 1195695b721639985bd0d93f318a9c718bf08e95 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 2 Aug 2024 17:50:00 -0700 Subject: [PATCH 063/132] Upgrade next. --- package.json | 2 +- scripts/check-env.js | 6 +-- yarn.lock | 124 +++++++++++++++++++++---------------------- 3 files changed, 64 insertions(+), 68 deletions(-) diff --git a/package.json b/package.json index f9513c43c9..186a1cd4c2 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,7 @@ "maxmind": "^4.3.6", "md5": "^2.3.0", "moment-timezone": "^0.5.35", - "next": "14.2.4", + "next": "14.2.5", "next-basics": "^0.39.0", "node-fetch": "^3.2.8", "npm-run-all": "^4.1.5", diff --git a/scripts/check-env.js b/scripts/check-env.js index db39bb79d5..280e7e398f 100644 --- a/scripts/check-env.js +++ b/scripts/check-env.js @@ -22,10 +22,6 @@ if (!process.env.SKIP_DB_CHECK && !process.env.DATABASE_TYPE) { checkMissing(['DATABASE_URL']); } -if (process.env.CLICKHOUSE_URL) { - checkMissing(['KAFKA_BROKER', 'KAFKA_URL', 'REDIS_URL']); -} - if (process.env.CLOUD_MODE) { - checkMissing(['CLOUD_URL']); + checkMissing(['CLOUD_URL', 'KAFKA_BROKER', 'KAFKA_URL', 'REDIS_URL']); } diff --git a/yarn.lock b/yarn.lock index 470cad8fc7..eee877d39c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2192,10 +2192,10 @@ resolved "https://registry.yarnpkg.com/@netlify/plugin-nextjs/-/plugin-nextjs-5.3.3.tgz#414cb0f4c21e6b80d6127b824efb00ae2f732a89" integrity sha512-QhvZLOHhPuTnh6TZ5G0/jtjAJ1Y52A67b39eygKv6znQUPti8p+8y2WulcntpCRzVp2stzEULMNPlnptna1ikg== -"@next/env@14.2.4": - version "14.2.4" - resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.4.tgz#5546813dc4f809884a37d257b254a5ce1b0248d7" - integrity sha512-3EtkY5VDkuV2+lNmKlbkibIJxcO4oIHEhBWne6PaAp+76J9KoSsGvNikp6ivzAT8dhhBMYrm6op2pS1ApG0Hzg== +"@next/env@14.2.5": + version "14.2.5" + resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.5.tgz#1d9328ab828711d3517d0a1d505acb55e5ef7ad0" + integrity sha512-/zZGkrTOsraVfYjGP8uM0p6r0BDT6xWpkjdVbcz66PJVSpwXX3yNiRycxAuDfBKGWBrZBXRuK/YVlkNgxHGwmA== "@next/eslint-plugin-next@14.2.4": version "14.2.4" @@ -2204,50 +2204,50 @@ dependencies: glob "10.3.10" -"@next/swc-darwin-arm64@14.2.4": - version "14.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.4.tgz#da9f04c34a3d5f0b8401ed745768420e4a604036" - integrity sha512-AH3mO4JlFUqsYcwFUHb1wAKlebHU/Hv2u2kb1pAuRanDZ7pD/A/KPD98RHZmwsJpdHQwfEc/06mgpSzwrJYnNg== - -"@next/swc-darwin-x64@14.2.4": - version "14.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.4.tgz#46dedb29ec5503bf171a72a3ecb8aac6e738e9d6" - integrity sha512-QVadW73sWIO6E2VroyUjuAxhWLZWEpiFqHdZdoQ/AMpN9YWGuHV8t2rChr0ahy+irKX5mlDU7OY68k3n4tAZTg== - -"@next/swc-linux-arm64-gnu@14.2.4": - version "14.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.4.tgz#c9697ab9eb422bd1d7ffd0eb0779cc2aefa9d4a1" - integrity sha512-KT6GUrb3oyCfcfJ+WliXuJnD6pCpZiosx2X3k66HLR+DMoilRb76LpWPGb4tZprawTtcnyrv75ElD6VncVamUQ== - -"@next/swc-linux-arm64-musl@14.2.4": - version "14.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.4.tgz#cbbceb2008571c743b5a310a488d2e166d200a75" - integrity sha512-Alv8/XGSs/ytwQcbCHwze1HmiIkIVhDHYLjczSVrf0Wi2MvKn/blt7+S6FJitj3yTlMwMxII1gIJ9WepI4aZ/A== - -"@next/swc-linux-x64-gnu@14.2.4": - version "14.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.4.tgz#d79184223f857bacffb92f643cb2943a43632568" - integrity sha512-ze0ShQDBPCqxLImzw4sCdfnB3lRmN3qGMB2GWDRlq5Wqy4G36pxtNOo2usu/Nm9+V2Rh/QQnrRc2l94kYFXO6Q== - -"@next/swc-linux-x64-musl@14.2.4": - version "14.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.4.tgz#6b6c3e5ac02ca5e63394d280ec8ee607491902df" - integrity sha512-8dwC0UJoc6fC7PX70csdaznVMNr16hQrTDAMPvLPloazlcaWfdPogq+UpZX6Drqb1OBlwowz8iG7WR0Tzk/diQ== - -"@next/swc-win32-arm64-msvc@14.2.4": - version "14.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.4.tgz#dbad3906e870dba84c5883d9d4c4838472e0697f" - integrity sha512-jxyg67NbEWkDyvM+O8UDbPAyYRZqGLQDTPwvrBBeOSyVWW/jFQkQKQ70JDqDSYg1ZDdl+E3nkbFbq8xM8E9x8A== - -"@next/swc-win32-ia32-msvc@14.2.4": - version "14.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.4.tgz#6074529b91ba49132922ce89a2e16d25d2ec235d" - integrity sha512-twrmN753hjXRdcrZmZttb/m5xaCBFa48Dt3FbeEItpJArxriYDunWxJn+QFXdJ3hPkm4u7CKxncVvnmgQMY1ag== - -"@next/swc-win32-x64-msvc@14.2.4": - version "14.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.4.tgz#e65a1c6539a671f97bb86d5183d6e3a1733c29c7" - integrity sha512-tkLrjBzqFTP8DVrAAQmZelEahfR9OxWpFR++vAI9FBhCiIxtwHwBHC23SBHCTURBtwB4kc/x44imVOnkKGNVGg== +"@next/swc-darwin-arm64@14.2.5": + version "14.2.5" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.5.tgz#d0a160cf78c18731c51cc0bff131c706b3e9bb05" + integrity sha512-/9zVxJ+K9lrzSGli1///ujyRfon/ZneeZ+v4ptpiPoOU+GKZnm8Wj8ELWU1Pm7GHltYRBklmXMTUqM/DqQ99FQ== + +"@next/swc-darwin-x64@14.2.5": + version "14.2.5" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.5.tgz#eb832a992407f6e6352eed05a073379f1ce0589c" + integrity sha512-vXHOPCwfDe9qLDuq7U1OYM2wUY+KQ4Ex6ozwsKxp26BlJ6XXbHleOUldenM67JRyBfVjv371oneEvYd3H2gNSA== + +"@next/swc-linux-arm64-gnu@14.2.5": + version "14.2.5" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.5.tgz#098fdab57a4664969bc905f5801ef5a89582c689" + integrity sha512-vlhB8wI+lj8q1ExFW8lbWutA4M2ZazQNvMWuEDqZcuJJc78iUnLdPPunBPX8rC4IgT6lIx/adB+Cwrl99MzNaA== + +"@next/swc-linux-arm64-musl@14.2.5": + version "14.2.5" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.5.tgz#243a1cc1087fb75481726dd289c7b219fa01f2b5" + integrity sha512-NpDB9NUR2t0hXzJJwQSGu1IAOYybsfeB+LxpGsXrRIb7QOrYmidJz3shzY8cM6+rO4Aojuef0N/PEaX18pi9OA== + +"@next/swc-linux-x64-gnu@14.2.5": + version "14.2.5" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.5.tgz#b8a2e436387ee4a52aa9719b718992e0330c4953" + integrity sha512-8XFikMSxWleYNryWIjiCX+gU201YS+erTUidKdyOVYi5qUQo/gRxv/3N1oZFCgqpesN6FPeqGM72Zve+nReVXQ== + +"@next/swc-linux-x64-musl@14.2.5": + version "14.2.5" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.5.tgz#cb8a9adad5fb8df86112cfbd363aab5c6d32757b" + integrity sha512-6QLwi7RaYiQDcRDSU/os40r5o06b5ue7Jsk5JgdRBGGp8l37RZEh9JsLSM8QF0YDsgcosSeHjglgqi25+m04IQ== + +"@next/swc-win32-arm64-msvc@14.2.5": + version "14.2.5" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.5.tgz#81f996c1c38ea0900d4e7719cc8814be8a835da0" + integrity sha512-1GpG2VhbspO+aYoMOQPQiqc/tG3LzmsdBH0LhnDS3JrtDx2QmzXe0B6mSZZiN3Bq7IOMXxv1nlsjzoS1+9mzZw== + +"@next/swc-win32-ia32-msvc@14.2.5": + version "14.2.5" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.5.tgz#f61c74ce823e10b2bc150e648fc192a7056422e0" + integrity sha512-Igh9ZlxwvCDsu6438FXlQTHlRno4gFpJzqPjSIBZooD22tKeI4fE/YMRoHVJHmrQ2P5YL1DoZ0qaOKkbeFWeMg== + +"@next/swc-win32-x64-msvc@14.2.5": + version "14.2.5" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.5.tgz#ed199a920efb510cfe941cd75ed38a7be21e756f" + integrity sha512-tEQ7oinq1/CjSG9uSTerca3v4AZ+dFa+4Yu6ihaG8Ud8ddqLQgFGcnwYls13H5X5CPDPZJdYxyeMui6muOLd4g== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -7853,12 +7853,12 @@ next-basics@^0.39.0: jsonwebtoken "^9.0.0" pure-rand "^6.0.2" -next@14.2.4: - version "14.2.4" - resolved "https://registry.yarnpkg.com/next/-/next-14.2.4.tgz#ef66c39c71e2d8ad0a3caa0383c8933f4663e4d1" - integrity sha512-R8/V7vugY+822rsQGQCjoLhMuC9oFj9SOi4Cl4b2wjDrseD0LRZ10W7R6Czo4w9ZznVSshKjuIomsRjvm9EKJQ== +next@14.2.5: + version "14.2.5" + resolved "https://registry.yarnpkg.com/next/-/next-14.2.5.tgz#afe4022bb0b752962e2205836587a289270efbea" + integrity sha512-0f8aRfBVL+mpzfBjYfQuLWh2WyAwtJXCRfkPF4UJ5qd2YwrHczsrSzXU4tRMV0OAxR8ZJZWPFn6uhSC56UTsLA== dependencies: - "@next/env" "14.2.4" + "@next/env" "14.2.5" "@swc/helpers" "0.5.5" busboy "1.6.0" caniuse-lite "^1.0.30001579" @@ -7866,15 +7866,15 @@ next@14.2.4: postcss "8.4.31" styled-jsx "5.1.1" optionalDependencies: - "@next/swc-darwin-arm64" "14.2.4" - "@next/swc-darwin-x64" "14.2.4" - "@next/swc-linux-arm64-gnu" "14.2.4" - "@next/swc-linux-arm64-musl" "14.2.4" - "@next/swc-linux-x64-gnu" "14.2.4" - "@next/swc-linux-x64-musl" "14.2.4" - "@next/swc-win32-arm64-msvc" "14.2.4" - "@next/swc-win32-ia32-msvc" "14.2.4" - "@next/swc-win32-x64-msvc" "14.2.4" + "@next/swc-darwin-arm64" "14.2.5" + "@next/swc-darwin-x64" "14.2.5" + "@next/swc-linux-arm64-gnu" "14.2.5" + "@next/swc-linux-arm64-musl" "14.2.5" + "@next/swc-linux-x64-gnu" "14.2.5" + "@next/swc-linux-x64-musl" "14.2.5" + "@next/swc-win32-arm64-msvc" "14.2.5" + "@next/swc-win32-ia32-msvc" "14.2.5" + "@next/swc-win32-x64-msvc" "14.2.5" nice-try@^1.0.4: version "1.0.5" From eda18d37957a8818748ff54afb101870ba323a12 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 2 Aug 2024 17:52:53 -0700 Subject: [PATCH 064/132] Fixed website details page not rendering. --- src/app/(main)/websites/[websiteId]/layout.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/layout.tsx b/src/app/(main)/websites/[websiteId]/layout.tsx index 1729e3ed9f..f8756ea3e7 100644 --- a/src/app/(main)/websites/[websiteId]/layout.tsx +++ b/src/app/(main)/websites/[websiteId]/layout.tsx @@ -2,10 +2,6 @@ import { Metadata } from 'next'; import WebsiteProvider from './WebsiteProvider'; export default function ({ children, params: { websiteId } }) { - if (process.env.cloudMode) { - return null; - } - return {children}; } From 4e7e1019bdd59d8d22af8e6366e8b299723834b3 Mon Sep 17 00:00:00 2001 From: Sergey Lisovskiy Date: Sat, 3 Aug 2024 18:51:44 +0400 Subject: [PATCH 065/132] feat: Update be-BY translations --- src/lang/be-BY.json | 364 ++++++++++++++++++++++---------------------- 1 file changed, 182 insertions(+), 182 deletions(-) diff --git a/src/lang/be-BY.json b/src/lang/be-BY.json index 53533023fd..129e6702af 100644 --- a/src/lang/be-BY.json +++ b/src/lang/be-BY.json @@ -1,267 +1,267 @@ { - "label.access-code": "Access code", + "label.access-code": "Код доступу", "label.actions": "Дзеянні", - "label.activity-log": "Activity log", - "label.add": "Add", - "label.add-description": "Add description", - "label.add-member": "Add member", - "label.add-step": "Add step", + "label.activity-log": "Журнал актыўнасці", + "label.add": "Дадаць", + "label.add-description": "Дадаць апісанне", + "label.add-member": "Дадаць удзельніка", + "label.add-step": "Дадаць крок", "label.add-website": "Дадаць сайт", "label.admin": "Адміністратар", - "label.after": "After", + "label.after": "Пасля", "label.all": "Усё", "label.all-time": "Увесь час", - "label.analytics": "Analytics", - "label.average": "Average", + "label.analytics": "Аналітыка", + "label.average": "Сярэдняе", "label.back": "Назад", - "label.before": "Before", + "label.before": "Да", "label.bounce-rate": "Паказчык адмоваў", - "label.breakdown": "Breakdown", - "label.browser": "Browser", + "label.breakdown": "Разбіўка", + "label.browser": "Браўзер", "label.browsers": "Браўзеры", "label.cancel": "Адмена", "label.change-password": "Змяніць пароль", - "label.cities": "Cities", - "label.city": "City", - "label.clear-all": "Clear all", - "label.compare": "Compare", - "label.confirm": "Confirm", + "label.cities": "Гарады", + "label.city": "Горад", + "label.clear-all": "Ачысціць усё", + "label.compare": "Параўнаць", + "label.confirm": "Падцвердзіць", "label.confirm-password": "Падцвердзіць пароль", - "label.contains": "Contains", - "label.continue": "Continue", - "label.count": "Count", + "label.contains": "Уключае", + "label.continue": "Працягнуць", + "label.count": "Колькасць", "label.countries": "Краіны", - "label.country": "Country", - "label.create": "Create", - "label.create-report": "Create report", - "label.create-team": "Create team", - "label.create-user": "Create user", - "label.created": "Created", - "label.created-by": "Created By", - "label.current": "Current", - "label.current-password": "Бягучы пароль", - "label.custom-range": "Карыстацкі дыяпазон", + "label.country": "Краіна", + "label.create": "Стварыць", + "label.create-report": "Стварыць справаздачу", + "label.create-team": "Стварыць каманду", + "label.create-user": "Стварыць карыстальніка", + "label.created": "Створана", + "label.created-by": "Створана", + "label.current": "Цяперашні", + "label.current-password": "Цяперашні пароль", + "label.custom-range": "Іншы дыяпазон", "label.dashboard": "Інфармацыйная панэль", - "label.data": "Data", - "label.date": "Date", + "label.data": "Дадзеныя", + "label.date": "Дата", "label.date-range": "Дыяпазон дат", - "label.day": "Day", + "label.day": "Дзень", "label.default-date-range": "Дыяпазон дат па змаўчанню", "label.delete": "Выдаліць", - "label.delete-report": "Delete report", - "label.delete-team": "Delete team", - "label.delete-user": "Delete user", + "label.delete-report": "Выдаліць справаздачу", + "label.delete-team": "Выдаліць каманду", + "label.delete-user": "Выдаліць карыстальніка", "label.delete-website": "Выдаліць сайт", - "label.description": "Description", + "label.description": "Апісанне", "label.desktop": "Настольны ПК", - "label.details": "Details", - "label.device": "Device", + "label.details": "Дэталі", + "label.device": "Прылада", "label.devices": "Прылады", - "label.dismiss": "Адмена", - "label.does-not-contain": "Does not contain", + "label.dismiss": "Адхіліць", + "label.does-not-contain": "Не ўключае", "label.domain": "Дамен", - "label.dropoff": "Dropoff", + "label.dropoff": "Адмовы", "label.edit": "Змяніць", "label.edit-dashboard": "Змяніць інфармацыйную панэль", - "label.edit-member": "Edit member", + "label.edit-member": "Рэдагаваць удзельніка", "label.enable-share-url": "Дазволіць дзяліцца спасылкай", - "label.end-step": "End Step", - "label.entry": "Entry URL", - "label.event": "Event", - "label.event-data": "Event data", + "label.end-step": "Канчатковы крок", + "label.entry": "URL уваходу", + "label.event": "Падзея", + "label.event-data": "Дадзеныя падзеі", "label.events": "Падзеі", - "label.exit": "Exit URL", - "label.false": "False", - "label.field": "Field", - "label.fields": "Fields", - "label.filter": "Filter", - "label.filter-combined": "Камбініаваны", + "label.exit": "URL выхаду", + "label.false": "Ложна", + "label.field": "Поле", + "label.fields": "Палі", + "label.filter": "Фільтр", + "label.filter-combined": "Камбініраваны", "label.filter-raw": "Сырыя", - "label.filters": "Filters", - "label.funnel": "Funnel", - "label.funnel-description": "Understand the conversion and drop-off rate of users.", - "label.goal": "Goal", - "label.goals": "Goals", - "label.goals-description": "Track your goals for pageviews and events.", - "label.greater-than": "Greater than", - "label.greater-than-equals": "Greater than or equals", - "label.host": "Host", - "label.hosts": "Hosts", - "label.insights": "Insights", - "label.insights-description": "Dive deeper into your data by using segments and filters.", - "label.is": "Is", - "label.is-not": "Is not", - "label.is-not-set": "Is not set", - "label.is-set": "Is set", - "label.join": "Join", - "label.join-team": "Join team", - "label.journey": "Journey", - "label.journey-description": "Understand how users navigate through your website.", + "label.filters": "Фільтры", + "label.funnel": "Варонка", + "label.funnel-description": "Разумець паказчыкі канверсіі і адмоваў.", + "label.goal": "Мэта", + "label.goals": "Мэты", + "label.goals-description": "Сачыць за мэтамі па праглядах старонак і падзеях.", + "label.greater-than": "Больш чым", + "label.greater-than-equals": "Больш чым або роўна", + "label.host": "Хост", + "label.hosts": "Хасты", + "label.insights": "Інсайты", + "label.insights-description": "Даследваць дадзеныя з дапамогай сегментаў і фільтраў.", + "label.is": "З'яўляецца", + "label.is-not": "Не з'яўляецца", + "label.is-not-set": "Не ўстаноўлена", + "label.is-set": "Устаноўлена", + "label.join": "Далучыцца", + "label.join-team": "Далучыцца да каманды", + "label.journey": "Маршрут карыстальніка", + "label.journey-description": "Разумець як карыстальнікі навігуюць па сайце.", "label.language": "Мова", "label.languages": "Мовы", "label.laptop": "Ноўтбук", - "label.last-days": "Апошнія {x} дні", + "label.last-days": "Апошнія {x} дзён", "label.last-hours": "Апошнія {x} гадзіны", - "label.last-months": "Last {x} months", - "label.leave": "Leave", - "label.leave-team": "Leave team", - "label.less-than": "Less than", - "label.less-than-equals": "Less than or equals", - "label.login": "Login", + "label.last-months": "Апошнія {x} месяцаў", + "label.leave": "Пакінуць", + "label.leave-team": "Пакінуць каманду", + "label.less-than": "Менш чым", + "label.less-than-equals": "Менш чым або роўна", + "label.login": "Увайсці", "label.logout": "Выйсці", - "label.manage": "Manage", - "label.manager": "Manager", - "label.max": "Max", - "label.member": "Member", - "label.members": "Members", - "label.min": "Min", + "label.manage": "Кіраваць", + "label.manager": "Кіраўнік", + "label.max": "Максімум", + "label.member": "Удзельнік", + "label.members": "Удзельнікі", + "label.min": "Мінімум", "label.mobile": "Мабільны", "label.more": "Болей", - "label.my-account": "My account", - "label.my-websites": "My websites", + "label.my-account": "Мой уліковы запіс", + "label.my-websites": "Мае сайты", "label.name": "Імя", "label.new-password": "Новы пароль", "label.none": "Няма", - "label.number-of-records": "{x} {x, plural, one {record} other {records}}", - "label.ok": "OK", - "label.os": "OS", - "label.overview": "Overview", + "label.number-of-records": "{x} {x, plural, one {запіс} other {запісаў}}", + "label.ok": "ОК", + "label.os": "Аперацыйная сістэма", + "label.overview": "Агляд", "label.owner": "Уласнік", - "label.page-of": "Page {current} of {total}", + "label.page-of": "Старонка {current} з {total}", "label.page-views": "Прагляды старонкі", - "label.pageTitle": "Page title", + "label.pageTitle": "Загаловак старонкі", "label.pages": "Старонкі", "label.password": "Пароль", "label.powered-by": "Зроблена {name}", - "label.previous": "Previous", - "label.previous-period": "Previous period", - "label.previous-year": "Previous year", + "label.previous": "Папярэдні", + "label.previous-period": "Папярэдні перыяд", + "label.previous-year": "Папярэдні год", "label.profile": "Профіль", - "label.property": "Property", - "label.queries": "Queries", - "label.query": "Query", - "label.query-parameters": "Query parameters", + "label.property": "Уласцівасць", + "label.queries": "Запыты", + "label.query": "Запыт", + "label.query-parameters": "Параметры запыту", "label.realtime": "У рэяльным часе", - "label.referrer": "Referrer", - "label.referrers": "Referrers", + "label.referrer": "Рэферэр", + "label.referrers": "Рэферэры", "label.refresh": "Аднавіць", - "label.regenerate": "Regenerate", - "label.region": "Region", - "label.regions": "Regions", - "label.remove": "Remove", - "label.remove-member": "Remove member", - "label.reports": "Reports", + "label.regenerate": "Рэгенераваць", + "label.region": "Рэгіён", + "label.regions": "Рэгіёны", + "label.remove": "Выдаліць", + "label.remove-member": "Выдаліць удзельніка", + "label.reports": "Справаздачы", "label.required": "Абавязкова", "label.reset": "Скінуць", "label.reset-website": "Скінуць статыстыку", - "label.retention": "Retention", - "label.retention-description": "Measure your website stickiness by tracking how often users return.", - "label.role": "Role", - "label.run-query": "Run query", + "label.retention": "Утрыманне", + "label.retention-description": "Ацаніць прыцягальнасць сайта, адсочваючы павяртанні карыстальнікаў.", + "label.role": "Роля", + "label.run-query": "Запусціць запыт", "label.save": "Захаваць", "label.screens": "Экраны", - "label.search": "Search", - "label.select": "Select", - "label.select-date": "Select date", - "label.select-role": "Select role", - "label.select-website": "Select website", - "label.sessions": "Sessions", + "label.search": "Пошук", + "label.select": "Выбраць", + "label.select-date": "Выбраць дату", + "label.select-role": "Выбраць ролю", + "label.select-website": "Выбраць сайт", + "label.sessions": "Сесіі", "label.settings": "Налады", "label.share-url": "Падзяліцца спасылкай", "label.single-day": "Адзін дзень", - "label.start-step": "Start Step", - "label.steps": "Steps", - "label.sum": "Sum", + "label.start-step": "Першы кроку", + "label.steps": "Крокі", + "label.sum": "Сума", "label.tablet": "Планшэт", - "label.team": "Team", - "label.team-id": "Team ID", - "label.team-manager": "Team manager", - "label.team-member": "Team member", - "label.team-name": "Team name", - "label.team-owner": "Team owner", - "label.team-view-only": "Team view only", - "label.team-websites": "Team websites", - "label.teams": "Teams", + "label.team": "Каманда", + "label.team-id": "Ідэнтыфікатар каманды", + "label.team-manager": "Кіраўнік каманды", + "label.team-member": "Удзельнік каманды", + "label.team-name": "Назва каманды", + "label.team-owner": "Уласнік каманды", + "label.team-view-only": "Толькі для каманднага прагляду", + "label.team-websites": "Сайты каманды", + "label.teams": "Каманды", "label.theme": "Тэма", "label.this-month": "Гэты месяц", "label.this-week": "Гэты тыдзень", "label.this-year": "Гэты год", - "label.timezone": "Таймзона", - "label.title": "Title", + "label.timezone": "Часавы пояс", + "label.title": "Загаловак", "label.today": "Сёння", "label.toggle-charts": "Пераключыць графікі", - "label.total": "Total", - "label.total-records": "Total records", + "label.total": "Агульная колькасць", + "label.total-records": "Агульная колькасць запісаў", "label.tracking-code": "Код адсочвання", - "label.transfer": "Transfer", - "label.transfer-website": "Transfer website", - "label.true": "True", - "label.type": "Type", - "label.unique": "Unique", + "label.transfer": "Перадаць", + "label.transfer-website": "Перадаць сайт", + "label.true": "Ісціна", + "label.type": "Тып", + "label.unique": "Унікальны", "label.unique-visitors": "Унікальныя наведвальнікі", "label.unknown": "Невядома", - "label.untitled": "Untitled", - "label.update": "Update", + "label.untitled": "Без назвы", + "label.update": "Абнавіць", "label.url": "URL", "label.urls": "URLs", - "label.user": "User", + "label.user": "Карыстальнік", "label.username": "Імя карыстальніка", - "label.users": "Users", + "label.users": "Карыстальнікі", "label.utm": "UTM", - "label.utm-description": "Track your campaigns through UTM parameters.", - "label.value": "Value", - "label.view": "View", - "label.view-details": "Пабачыць дэталі", - "label.view-only": "View only", + "label.utm-description": "Сачыць за кампаніямі з дапамогай UTM-метак.", + "label.value": "Значэнне", + "label.view": "Паглядзець", + "label.view-details": "Паглядзець дэталі", + "label.view-only": "Толькі прагляд", "label.views": "Прагляды", - "label.views-per-visit": "Views per visit", + "label.views-per-visit": "Прагляды за наведванне", "label.visit-duration": "Сярэдняя даўжыня наведвання", "label.visitors": "Наведвальнікі", - "label.visits": "Visits", - "label.website": "Website", - "label.website-id": "Website ID", + "label.visits": "Наведванні", + "label.website": "Сайт", + "label.website-id": "Ідэнтыфікатар сайта", "label.websites": "Сайты", - "label.window": "Window", + "label.window": "Вакно", "label.yesterday": "Учора", - "message.action-confirmation": "Type {confirmation} in the box below to confirm.", - "message.active-users": "{x} тякучых {x, plural, one {наведвальнік} other {наведвальнікаў}}", - "message.collected-data": "Collected data", + "message.action-confirmation": "Увядзіце {confirmation} у поле ніжэй, каб пацвердзіць.", + "message.active-users": "{x} цякучых {x, plural, one {наведвальнік} other {наведвальнікаў}}", + "message.collected-data": "Сабраныя дадзеныя", "message.confirm-delete": "Вы дакладна хочаце выдаліць {target}?", - "message.confirm-leave": "Are you sure you want to leave {target}?", - "message.confirm-remove": "Are you sure you want to remove {target}?", + "message.confirm-leave": "Вы дакладна хочаце пакінуць {target}?", + "message.confirm-remove": "Вы дакладна хочаце выдаліць {target}?", "message.confirm-reset": "Вы дакладна хочаце скінуць {target} статыстыку?", - "message.delete-team-warning": "Deleting a team will also delete all team websites.", + "message.delete-team-warning": "Выдаленне каманды таксама выдаліць усе сайты каманды.", "message.delete-website-warning": "Усе асацыяваныя дадзеныя будуць таксама выдалены.", "message.error": "Нешта пайшло не так.", - "message.event-log": "{event} on {url}", + "message.event-log": "{event} на {url}", "message.go-to-settings": "Да налад", - "message.incorrect-username-password": "Некарэктны username/password.", + "message.incorrect-username-password": "Некарэктнае імя карыстальніка/пароль.", "message.invalid-domain": "Некарэктны дамен", - "message.min-password-length": "Minimum length of {n} characters", - "message.new-version-available": "A new version of Umami {version} is available!", + "message.min-password-length": "Мінімальная даўжыня {n} знакаў", + "message.new-version-available": "Даступная новая версія Umami {version}!", "message.no-data-available": "Няма дадзеных.", - "message.no-event-data": "No event data is available.", + "message.no-event-data": "Дадзеныя падзеі недаступныя.", "message.no-match-password": "Паролі не супадаюць", - "message.no-results-found": "No results were found.", - "message.no-team-websites": "This team does not have any websites.", - "message.no-teams": "You have not created any teams.", - "message.no-users": "There are no users.", - "message.no-websites-configured": "Вы не наладзілі ніводнага сайту.", + "message.no-results-found": "Вынікаў не знойдзена.", + "message.no-team-websites": "Гэтая каманда не мае ніводнага сайта.", + "message.no-teams": "Вы не стварылі ніводнай каманды.", + "message.no-users": "Няма карыстальнікаў.", + "message.no-websites-configured": "Вы не наладзілі ніводнага сайта.", "message.page-not-found": "Старонка не знойдзена.", - "message.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", - "message.reset-website-warning": "Уся статыстыка для гэтага сайту будзе выдалена, але код адсочвання будзе працягваць працаваць.", + "message.reset-website": "Каб скінуць гэты сайт, увядзіце {confirmation} у поле ніжэй для пацверджання.", + "message.reset-website-warning": "Уся статыстыка для гэтага сайта будзе выдалена, але код адсочвання будзе працягваць працаваць.", "message.saved": "Захавана паспяхова.", "message.share-url": "Гэта публічная спасылка для {target}.", - "message.team-already-member": "You are already a member of the team.", - "message.team-not-found": "Team not found.", - "message.team-websites-info": "Websites can be viewed by anyone on the team.", + "message.team-already-member": "Вы ўжо ўдзельнік каманды.", + "message.team-not-found": "Каманда не знойдзена.", + "message.team-websites-info": "Сайты могуць быць праглядацца любым удзельнікам каманды.", "message.tracking-code": "Код адсочвання", - "message.transfer-team-website-to-user": "Transfer this website to your account?", - "message.transfer-user-website-to-team": "Select the team to transfer this website to.", - "message.transfer-website": "Transfer website ownership to your account or another team.", - "message.triggered-event": "Triggered event", - "message.user-deleted": "User deleted.", - "message.viewed-page": "Viewed page", + "message.transfer-team-website-to-user": "Перадаць гэты сайт на ваш уліковы запіс?", + "message.transfer-user-website-to-team": "Выберыце каманду для перадачы гэтага сайта.", + "message.transfer-website": "Перадача сайта на ваш уліковы запіс або іншай камандзе.", + "message.triggered-event": "Падзея якая спрацавала", + "message.user-deleted": "Карыстальнік выдалены.", + "message.viewed-page": "Праглядзеў старонку", "message.visitor-log": "Наведвальнік з {country} праз {browser} на {os} {device}", - "message.visitors-dropped-off": "Visitors dropped off" + "message.visitors-dropped-off": "Наведвальнікі сышлі" } From 440db17000067fcedae580ee527b19cbd73ee1a8 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Sat, 3 Aug 2024 22:02:41 -0700 Subject: [PATCH 066/132] Added created date to session data save. --- src/pages/api/send.ts | 4 +--- src/queries/analytics/sessions/saveSessionData.ts | 5 ++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/pages/api/send.ts b/src/pages/api/send.ts index 313184202d..23640de9f0 100644 --- a/src/pages/api/send.ts +++ b/src/pages/api/send.ts @@ -144,9 +144,7 @@ export default async (req: NextApiRequestCollect, res: NextApiResponse) => { ...session, sessionId: session.id, }); - } - - if (type === COLLECTION_TYPE.identify) { + } else if (type === COLLECTION_TYPE.identify) { if (!data) { return badRequest(res, 'Data required.'); } diff --git a/src/queries/analytics/sessions/saveSessionData.ts b/src/queries/analytics/sessions/saveSessionData.ts index 1f5c149454..51f5c47d0d 100644 --- a/src/queries/analytics/sessions/saveSessionData.ts +++ b/src/queries/analytics/sessions/saveSessionData.ts @@ -11,7 +11,6 @@ export async function saveSessionData(data: { websiteId: string; sessionId: string; sessionData: DynamicData; - createdAt?: string; }) { return runQuery({ [PRISMA]: () => relationalQuery(data), @@ -78,12 +77,12 @@ async function clickhouseQuery(data: { websiteId: string; sessionId: string; sessionData: DynamicData; - createdAt?: string; }) { - const { websiteId, sessionId, sessionData, createdAt } = data; + const { websiteId, sessionId, sessionData } = data; const { insert } = clickhouse; const { getDateFormat, sendMessages } = kafka; + const createdAt = getDateFormat(new Date()); const jsonKeys = flattenJSON(sessionData); From 0988808b756ed890fe88726279eeeafe93ee771d Mon Sep 17 00:00:00 2001 From: Raye Evtuch Date: Sun, 4 Aug 2024 20:00:45 -0500 Subject: [PATCH 067/132] Allow team managers to transfer websites to teams --- .../settings/websites/[websiteId]/WebsiteTransferForm.tsx | 2 +- src/lib/auth.ts | 4 ++-- src/lib/constants.ts | 5 +++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/app/(main)/settings/websites/[websiteId]/WebsiteTransferForm.tsx b/src/app/(main)/settings/websites/[websiteId]/WebsiteTransferForm.tsx index db46c212f6..eb568a7f8e 100644 --- a/src/app/(main)/settings/websites/[websiteId]/WebsiteTransferForm.tsx +++ b/src/app/(main)/settings/websites/[websiteId]/WebsiteTransferForm.tsx @@ -71,7 +71,7 @@ export function WebsiteTransferForm({ {result.data .filter(({ teamUser }) => teamUser.find( - ({ role, userId }) => role === ROLES.teamOwner && userId === user.id, + ({ role, userId }) => [ ROLES.teamOwner, ROLES.teamManager ].includes(role) && userId === user.id, ), ) .map(({ id, name }) => { diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 5b17cf1329..12fb0a2817 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -106,7 +106,7 @@ export async function canTransferWebsiteToUser({ user }: Auth, websiteId: string if (website.teamId && user.id === userId) { const teamUser = await getTeamUser(website.teamId, userId); - return teamUser?.role === ROLES.teamOwner; + return teamUser && hasPermission(teamUser.role, PERMISSIONS.websiteTransferToUser); } return false; @@ -118,7 +118,7 @@ export async function canTransferWebsiteToTeam({ user }: Auth, websiteId: string if (website.userId && website.userId === user.id) { const teamUser = await getTeamUser(teamId, user.id); - return teamUser?.role === ROLES.teamOwner; + return teamUser && hasPermission(teamUser.role, PERMISSIONS.websiteTransferToTeam); } return false; diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 35917802d2..250307086e 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -160,6 +160,8 @@ export const PERMISSIONS = { websiteCreate: 'website:create', websiteUpdate: 'website:update', websiteDelete: 'website:delete', + websiteTransferToTeam: 'website:transfer-to-team', + websiteTransferToUser: 'website:transfer-to-user', teamCreate: 'team:create', teamUpdate: 'team:update', teamDelete: 'team:delete', @@ -180,12 +182,15 @@ export const ROLE_PERMISSIONS = { PERMISSIONS.websiteCreate, PERMISSIONS.websiteUpdate, PERMISSIONS.websiteDelete, + PERMISSIONS.websiteTransferToTeam, + PERMISSIONS.websiteTransferToUser, ], [ROLES.teamManager]: [ PERMISSIONS.teamUpdate, PERMISSIONS.websiteCreate, PERMISSIONS.websiteUpdate, PERMISSIONS.websiteDelete, + PERMISSIONS.websiteTransferToTeam, ], [ROLES.teamMember]: [ PERMISSIONS.websiteCreate, From 3fd9d6b148d3349349f7398602a090a5810c67be Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Mon, 5 Aug 2024 13:57:33 -0700 Subject: [PATCH 068/132] limit contain filters on country, region, city --- .../[reportId]/FieldFilterEditForm.tsx | 36 +++++++++----- yarn.lock | 49 +++---------------- 2 files changed, 30 insertions(+), 55 deletions(-) diff --git a/src/app/(main)/reports/[reportId]/FieldFilterEditForm.tsx b/src/app/(main)/reports/[reportId]/FieldFilterEditForm.tsx index dc10b724dc..a1417780e6 100644 --- a/src/app/(main)/reports/[reportId]/FieldFilterEditForm.tsx +++ b/src/app/(main)/reports/[reportId]/FieldFilterEditForm.tsx @@ -1,22 +1,22 @@ -import { useState, useMemo } from 'react'; +import { useFilters, useFormat, useLocale, useMessages, useWebsiteValues } from 'components/hooks'; +import { OPERATORS } from 'lib/constants'; +import { isEqualsOperator } from 'lib/params'; +import { useMemo, useState } from 'react'; import { + Button, + Dropdown, + Flexbox, Form, FormRow, - Item, - Flexbox, - Dropdown, - Button, - SearchField, - TextField, - Text, Icon, Icons, - Menu, + Item, Loading, + Menu, + SearchField, + Text, + TextField, } from 'react-basics'; -import { useMessages, useFilters, useFormat, useLocale, useWebsiteValues } from 'components/hooks'; -import { OPERATORS } from 'lib/constants'; -import { isEqualsOperator } from 'lib/params'; import styles from './FieldFilterEditForm.module.css'; export interface FieldFilterFormProps { @@ -69,6 +69,16 @@ export default function FieldFilterEditForm({ search, }); + const filterDropdownItems = (name: string) => { + const limitedFilters = ['country', 'region', 'city']; + + if (limitedFilters.includes(name)) { + return filters.filter(f => f.type === type && !f.label.match(/contain/gi)); + } else { + return filters.filter(f => f.type === type); + } + }; + const formattedValues = useMemo(() => { if (!values) { return {}; @@ -142,7 +152,7 @@ export default function FieldFilterEditForm({ {allowFilterSelect && ( f.type === type)} + items={filterDropdownItems(name)} value={operator} renderValue={renderFilterValue} onChange={handleOperatorChange} diff --git a/yarn.lock b/yarn.lock index eee877d39c..08dac6cb4a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3882,20 +3882,10 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001538, caniuse-lite@^1.0.30001541: - version "1.0.30001551" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001551.tgz#1f2cfa8820bd97c971a57349d7fd8f6e08664a3e" - integrity sha512-vtBAez47BoGMMzlbYhfXrMV1kvRF2WP/lqiMuDu1Sb4EE4LKEgjopFDSRtZfdVnslNRpOqV/woE+Xgrwj6VQlg== - -caniuse-lite@^1.0.30001579: - version "1.0.30001587" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001587.tgz#a0bce920155fa56a1885a69c74e1163fc34b4881" - integrity sha512-HMFNotUmLXn71BQxg8cijvqxnIAofforZOwGsxyXJ0qugTdspUF4sPSJ2vhgprHCB996tIDzEq1ubumPDV8ULA== - -caniuse-lite@^1.0.30001587: - version "1.0.30001591" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001591.tgz#16745e50263edc9f395895a7cd468b9f3767cf33" - integrity sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ== +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001538, caniuse-lite@^1.0.30001541, caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001587: + version "1.0.30001649" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001649.tgz" + integrity sha512-fJegqZZ0ZX8HOWr6rcafGr72+xcgJKI9oWfDW5DrD7ExUtgZC7a7R7ZYmZqplh7XDocFdGeIFn7roAxhOeYrPQ== caseless@~0.12.0: version "0.12.0" @@ -9857,16 +9847,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -9947,14 +9928,7 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -10720,7 +10694,7 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -10738,15 +10712,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.0.1, wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From b9068c00504a53980f55d31c24d5112d2b8b8372 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Wed, 7 Aug 2024 00:10:25 -0700 Subject: [PATCH 069/132] Added metrics bar and date range to sessions. --- package.json | 6 +- public/intl/messages/be-BY.json | 378 +++++++-------- public/intl/messages/de-DE.json | 84 ++-- public/intl/messages/es-ES.json | 68 +-- public/intl/messages/km-KH.json | 328 ++++++------- src/app/(main)/reports/ReportsTable.tsx | 5 +- .../(main)/reports/create/ReportTemplates.tsx | 13 +- .../(main)/reports/revenue/RevenueReport.tsx | 6 +- src/app/(main)/settings/teams/TeamsTable.tsx | 5 +- src/app/(main)/settings/users/UsersTable.tsx | 5 +- .../settings/websites/WebsitesTable.tsx | 5 +- .../settings/members/TeamMembersTable.tsx | 5 +- .../settings/websites/TeamWebsitesTable.tsx | 5 +- .../websites/[websiteId]/WebsiteHeader.tsx | 2 +- .../[websiteId]/events/EventsTable.tsx | 20 +- .../sessions/SessionMetricsBar.tsx | 5 - .../sessions/SessionsMetricsBar.tsx | 42 ++ .../[websiteId]/sessions/SessionsPage.tsx | 2 + .../[websiteId]/sessions/SessionsTable.tsx | 16 +- src/assets/location.svg | 2 +- src/assets/logo-white.svg | 5 +- src/assets/money.svg | 1 + src/components/common/DataTable.tsx | 4 +- src/components/common/TypeIcon.tsx | 19 +- src/components/hooks/index.ts | 3 +- .../{useFilterQuery.ts => usePagedQuery.ts} | 8 +- src/components/hooks/queries/useReports.ts | 4 +- .../hooks/queries/useTeamMembers.ts | 4 +- .../hooks/queries/useTeamWebsites.ts | 4 +- src/components/hooks/queries/useTeams.ts | 4 +- src/components/hooks/queries/useUsers.ts | 4 +- .../hooks/queries/useWebsiteEvents.ts | 4 +- .../hooks/queries/useWebsiteEventsSeries.ts | 20 + .../hooks/queries/useWebsiteSessions.ts | 9 +- src/components/hooks/queries/useWebsites.ts | 4 +- src/components/messages.ts | 7 +- src/components/metrics/EventsChart.tsx | 4 +- src/components/metrics/MetricCard.tsx | 2 +- src/components/metrics/PagesTable.tsx | 2 +- src/lib/types.ts | 2 +- .../websites/[websiteId]/sessions/index.ts | 9 +- src/queries/analytics/getWebsiteStats.ts | 38 +- .../analytics/sessions/getWebsiteSessions.ts | 3 +- yarn.lock | 432 ++++++++++-------- 44 files changed, 859 insertions(+), 739 deletions(-) delete mode 100644 src/app/(main)/websites/[websiteId]/sessions/SessionMetricsBar.tsx create mode 100644 src/app/(main)/websites/[websiteId]/sessions/SessionsMetricsBar.tsx create mode 100644 src/assets/money.svg rename src/components/hooks/queries/{useFilterQuery.ts => usePagedQuery.ts} (79%) create mode 100644 src/components/hooks/queries/useWebsiteEventsSeries.ts diff --git a/package.json b/package.json index 186a1cd4c2..9ca56fe356 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "@dicebear/collection": "^9.2.1", "@dicebear/core": "^9.2.1", "@fontsource/inter": "^4.5.15", - "@prisma/client": "5.16.2", + "@prisma/client": "5.17.0", "@prisma/extension-read-replicas": "^0.3.0", "@react-spring/web": "^9.7.3", "@tanstack/react-query": "^5.28.6", @@ -104,9 +104,9 @@ "next-basics": "^0.39.0", "node-fetch": "^3.2.8", "npm-run-all": "^4.1.5", - "prisma": "5.16.2", + "prisma": "5.17.0", "react": "^18.2.0", - "react-basics": "^0.123.0", + "react-basics": "^0.124.0", "react-beautiful-dnd": "^13.1.0", "react-dom": "^18.2.0", "react-error-boundary": "^4.0.4", diff --git a/public/intl/messages/be-BY.json b/public/intl/messages/be-BY.json index e6afc2ca85..5d84ef4e09 100644 --- a/public/intl/messages/be-BY.json +++ b/public/intl/messages/be-BY.json @@ -2,7 +2,7 @@ "label.access-code": [ { "type": 0, - "value": "Access code" + "value": "Код доступу" } ], "label.actions": [ @@ -14,31 +14,31 @@ "label.activity-log": [ { "type": 0, - "value": "Activity log" + "value": "Журнал актыўнасці" } ], "label.add": [ { "type": 0, - "value": "Add" + "value": "Дадаць" } ], "label.add-description": [ { "type": 0, - "value": "Add description" + "value": "Дадаць апісанне" } ], "label.add-member": [ { "type": 0, - "value": "Add member" + "value": "Дадаць удзельніка" } ], "label.add-step": [ { "type": 0, - "value": "Add step" + "value": "Дадаць крок" } ], "label.add-website": [ @@ -56,7 +56,7 @@ "label.after": [ { "type": 0, - "value": "After" + "value": "Пасля" } ], "label.all": [ @@ -74,13 +74,13 @@ "label.analytics": [ { "type": 0, - "value": "Analytics" + "value": "Аналітыка" } ], "label.average": [ { "type": 0, - "value": "Average" + "value": "Сярэдняе" } ], "label.back": [ @@ -92,7 +92,7 @@ "label.before": [ { "type": 0, - "value": "Before" + "value": "Да" } ], "label.bounce-rate": [ @@ -104,13 +104,13 @@ "label.breakdown": [ { "type": 0, - "value": "Breakdown" + "value": "Разбіўка" } ], "label.browser": [ { "type": 0, - "value": "Browser" + "value": "Браўзер" } ], "label.browsers": [ @@ -134,31 +134,31 @@ "label.cities": [ { "type": 0, - "value": "Cities" + "value": "Гарады" } ], "label.city": [ { "type": 0, - "value": "City" + "value": "Горад" } ], "label.clear-all": [ { "type": 0, - "value": "Clear all" + "value": "Ачысціць усё" } ], "label.compare": [ { "type": 0, - "value": "Compare" + "value": "Параўнаць" } ], "label.confirm": [ { "type": 0, - "value": "Confirm" + "value": "Падцвердзіць" } ], "label.confirm-password": [ @@ -170,19 +170,19 @@ "label.contains": [ { "type": 0, - "value": "Contains" + "value": "Уключае" } ], "label.continue": [ { "type": 0, - "value": "Continue" + "value": "Працягнуць" } ], "label.count": [ { "type": 0, - "value": "Count" + "value": "Колькасць" } ], "label.countries": [ @@ -194,61 +194,61 @@ "label.country": [ { "type": 0, - "value": "Country" + "value": "Краіна" } ], "label.create": [ { "type": 0, - "value": "Create" + "value": "Стварыць" } ], "label.create-report": [ { "type": 0, - "value": "Create report" + "value": "Стварыць справаздачу" } ], "label.create-team": [ { "type": 0, - "value": "Create team" + "value": "Стварыць каманду" } ], "label.create-user": [ { "type": 0, - "value": "Create user" + "value": "Стварыць карыстальніка" } ], "label.created": [ { "type": 0, - "value": "Created" + "value": "Створана" } ], "label.created-by": [ { "type": 0, - "value": "Created By" + "value": "Створана" } ], "label.current": [ { "type": 0, - "value": "Current" + "value": "Цяперашні" } ], "label.current-password": [ { "type": 0, - "value": "Бягучы пароль" + "value": "Цяперашні пароль" } ], "label.custom-range": [ { "type": 0, - "value": "Карыстацкі дыяпазон" + "value": "Іншы дыяпазон" } ], "label.dashboard": [ @@ -260,13 +260,13 @@ "label.data": [ { "type": 0, - "value": "Data" + "value": "Дадзеныя" } ], "label.date": [ { "type": 0, - "value": "Date" + "value": "Дата" } ], "label.date-range": [ @@ -278,7 +278,7 @@ "label.day": [ { "type": 0, - "value": "Day" + "value": "Дзень" } ], "label.default-date-range": [ @@ -296,19 +296,19 @@ "label.delete-report": [ { "type": 0, - "value": "Delete report" + "value": "Выдаліць справаздачу" } ], "label.delete-team": [ { "type": 0, - "value": "Delete team" + "value": "Выдаліць каманду" } ], "label.delete-user": [ { "type": 0, - "value": "Delete user" + "value": "Выдаліць карыстальніка" } ], "label.delete-website": [ @@ -320,7 +320,7 @@ "label.description": [ { "type": 0, - "value": "Description" + "value": "Апісанне" } ], "label.desktop": [ @@ -332,13 +332,13 @@ "label.details": [ { "type": 0, - "value": "Details" + "value": "Дэталі" } ], "label.device": [ { "type": 0, - "value": "Device" + "value": "Прылада" } ], "label.devices": [ @@ -350,13 +350,13 @@ "label.dismiss": [ { "type": 0, - "value": "Адмена" + "value": "Адхіліць" } ], "label.does-not-contain": [ { "type": 0, - "value": "Does not contain" + "value": "Не ўключае" } ], "label.domain": [ @@ -368,7 +368,7 @@ "label.dropoff": [ { "type": 0, - "value": "Dropoff" + "value": "Адмовы" } ], "label.edit": [ @@ -386,7 +386,7 @@ "label.edit-member": [ { "type": 0, - "value": "Edit member" + "value": "Рэдагаваць удзельніка" } ], "label.enable-share-url": [ @@ -398,25 +398,25 @@ "label.end-step": [ { "type": 0, - "value": "End Step" + "value": "Канчатковы крок" } ], "label.entry": [ { "type": 0, - "value": "Entry URL" + "value": "URL уваходу" } ], "label.event": [ { "type": 0, - "value": "Event" + "value": "Падзея" } ], "label.event-data": [ { "type": 0, - "value": "Event data" + "value": "Дадзеныя падзеі" } ], "label.events": [ @@ -428,37 +428,37 @@ "label.exit": [ { "type": 0, - "value": "Exit URL" + "value": "URL выхаду" } ], "label.false": [ { "type": 0, - "value": "False" + "value": "Ложна" } ], "label.field": [ { "type": 0, - "value": "Field" + "value": "Поле" } ], "label.fields": [ { "type": 0, - "value": "Fields" + "value": "Палі" } ], "label.filter": [ { "type": 0, - "value": "Filter" + "value": "Фільтр" } ], "label.filter-combined": [ { "type": 0, - "value": "Камбініаваны" + "value": "Камбініраваны" } ], "label.filter-raw": [ @@ -470,121 +470,121 @@ "label.filters": [ { "type": 0, - "value": "Filters" + "value": "Фільтры" } ], "label.funnel": [ { "type": 0, - "value": "Funnel" + "value": "Варонка" } ], "label.funnel-description": [ { "type": 0, - "value": "Understand the conversion and drop-off rate of users." + "value": "Разумець паказчыкі канверсіі і адмоваў." } ], "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": [ { "type": 0, - "value": "Greater than" + "value": "Больш чым" } ], "label.greater-than-equals": [ { "type": 0, - "value": "Greater than or equals" + "value": "Больш чым або роўна" } ], "label.host": [ { "type": 0, - "value": "Host" + "value": "Хост" } ], "label.hosts": [ { "type": 0, - "value": "Hosts" + "value": "Хасты" } ], "label.insights": [ { "type": 0, - "value": "Insights" + "value": "Інсайты" } ], "label.insights-description": [ { "type": 0, - "value": "Dive deeper into your data by using segments and filters." + "value": "Даследваць дадзеныя з дапамогай сегментаў і фільтраў." } ], "label.is": [ { "type": 0, - "value": "Is" + "value": "З'яўляецца" } ], "label.is-not": [ { "type": 0, - "value": "Is not" + "value": "Не з'яўляецца" } ], "label.is-not-set": [ { "type": 0, - "value": "Is not set" + "value": "Не ўстаноўлена" } ], "label.is-set": [ { "type": 0, - "value": "Is set" + "value": "Устаноўлена" } ], "label.join": [ { "type": 0, - "value": "Join" + "value": "Далучыцца" } ], "label.join-team": [ { "type": 0, - "value": "Join team" + "value": "Далучыцца да каманды" } ], "label.journey": [ { "type": 0, - "value": "Journey" + "value": "Маршрут карыстальніка" } ], "label.journey-description": [ { "type": 0, - "value": "Understand how users navigate through your website." + "value": "Разумець як карыстальнікі навігуюць па сайце." } ], "label.language": [ @@ -616,7 +616,7 @@ }, { "type": 0, - "value": " дні" + "value": " дзён" } ], "label.last-hours": [ @@ -636,7 +636,7 @@ "label.last-months": [ { "type": 0, - "value": "Last " + "value": "Апошнія " }, { "type": 1, @@ -644,37 +644,37 @@ }, { "type": 0, - "value": " months" + "value": " месяцаў" } ], "label.leave": [ { "type": 0, - "value": "Leave" + "value": "Пакінуць" } ], "label.leave-team": [ { "type": 0, - "value": "Leave team" + "value": "Пакінуць каманду" } ], "label.less-than": [ { "type": 0, - "value": "Less than" + "value": "Менш чым" } ], "label.less-than-equals": [ { "type": 0, - "value": "Less than or equals" + "value": "Менш чым або роўна" } ], "label.login": [ { "type": 0, - "value": "Login" + "value": "Увайсці" } ], "label.logout": [ @@ -686,37 +686,37 @@ "label.manage": [ { "type": 0, - "value": "Manage" + "value": "Кіраваць" } ], "label.manager": [ { "type": 0, - "value": "Manager" + "value": "Кіраўнік" } ], "label.max": [ { "type": 0, - "value": "Max" + "value": "Максімум" } ], "label.member": [ { "type": 0, - "value": "Member" + "value": "Удзельнік" } ], "label.members": [ { "type": 0, - "value": "Members" + "value": "Удзельнікі" } ], "label.min": [ { "type": 0, - "value": "Min" + "value": "Мінімум" } ], "label.mobile": [ @@ -734,13 +734,13 @@ "label.my-account": [ { "type": 0, - "value": "My account" + "value": "Мой уліковы запіс" } ], "label.my-websites": [ { "type": 0, - "value": "My websites" + "value": "Мае сайты" } ], "label.name": [ @@ -777,7 +777,7 @@ "value": [ { "type": 0, - "value": "record" + "value": "запіс" } ] }, @@ -785,7 +785,7 @@ "value": [ { "type": 0, - "value": "records" + "value": "запісаў" } ] } @@ -798,19 +798,19 @@ "label.ok": [ { "type": 0, - "value": "OK" + "value": "ОК" } ], "label.os": [ { "type": 0, - "value": "OS" + "value": "Аперацыйная сістэма" } ], "label.overview": [ { "type": 0, - "value": "Overview" + "value": "Агляд" } ], "label.owner": [ @@ -822,7 +822,7 @@ "label.page-of": [ { "type": 0, - "value": "Page " + "value": "Старонка " }, { "type": 1, @@ -830,7 +830,7 @@ }, { "type": 0, - "value": " of " + "value": " з " }, { "type": 1, @@ -846,7 +846,7 @@ "label.pageTitle": [ { "type": 0, - "value": "Page title" + "value": "Загаловак старонкі" } ], "label.pages": [ @@ -874,19 +874,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": [ @@ -898,25 +898,25 @@ "label.property": [ { "type": 0, - "value": "Property" + "value": "Уласцівасць" } ], "label.queries": [ { "type": 0, - "value": "Queries" + "value": "Запыты" } ], "label.query": [ { "type": 0, - "value": "Query" + "value": "Запыт" } ], "label.query-parameters": [ { "type": 0, - "value": "Query parameters" + "value": "Параметры запыту" } ], "label.realtime": [ @@ -928,13 +928,13 @@ "label.referrer": [ { "type": 0, - "value": "Referrer" + "value": "Рэферэр" } ], "label.referrers": [ { "type": 0, - "value": "Referrers" + "value": "Рэферэры" } ], "label.refresh": [ @@ -946,37 +946,37 @@ "label.regenerate": [ { "type": 0, - "value": "Regenerate" + "value": "Рэгенераваць" } ], "label.region": [ { "type": 0, - "value": "Region" + "value": "Рэгіён" } ], "label.regions": [ { "type": 0, - "value": "Regions" + "value": "Рэгіёны" } ], "label.remove": [ { "type": 0, - "value": "Remove" + "value": "Выдаліць" } ], "label.remove-member": [ { "type": 0, - "value": "Remove member" + "value": "Выдаліць удзельніка" } ], "label.reports": [ { "type": 0, - "value": "Reports" + "value": "Справаздачы" } ], "label.required": [ @@ -1000,25 +1000,25 @@ "label.retention": [ { "type": 0, - "value": "Retention" + "value": "Утрыманне" } ], "label.retention-description": [ { "type": 0, - "value": "Measure your website stickiness by tracking how often users return." + "value": "Ацаніць прыцягальнасць сайта, адсочваючы павяртанні карыстальнікаў." } ], "label.role": [ { "type": 0, - "value": "Role" + "value": "Роля" } ], "label.run-query": [ { "type": 0, - "value": "Run query" + "value": "Запусціць запыт" } ], "label.save": [ @@ -1036,37 +1036,37 @@ "label.search": [ { "type": 0, - "value": "Search" + "value": "Пошук" } ], "label.select": [ { "type": 0, - "value": "Select" + "value": "Выбраць" } ], "label.select-date": [ { "type": 0, - "value": "Select date" + "value": "Выбраць дату" } ], "label.select-role": [ { "type": 0, - "value": "Select role" + "value": "Выбраць ролю" } ], "label.select-website": [ { "type": 0, - "value": "Select website" + "value": "Выбраць сайт" } ], "label.sessions": [ { "type": 0, - "value": "Sessions" + "value": "Сесіі" } ], "label.settings": [ @@ -1090,19 +1090,19 @@ "label.start-step": [ { "type": 0, - "value": "Start Step" + "value": "Першы кроку" } ], "label.steps": [ { "type": 0, - "value": "Steps" + "value": "Крокі" } ], "label.sum": [ { "type": 0, - "value": "Sum" + "value": "Сума" } ], "label.tablet": [ @@ -1114,55 +1114,55 @@ "label.team": [ { "type": 0, - "value": "Team" + "value": "Каманда" } ], "label.team-id": [ { "type": 0, - "value": "Team ID" + "value": "Ідэнтыфікатар каманды" } ], "label.team-manager": [ { "type": 0, - "value": "Team manager" + "value": "Кіраўнік каманды" } ], "label.team-member": [ { "type": 0, - "value": "Team member" + "value": "Удзельнік каманды" } ], "label.team-name": [ { "type": 0, - "value": "Team name" + "value": "Назва каманды" } ], "label.team-owner": [ { "type": 0, - "value": "Team owner" + "value": "Уласнік каманды" } ], "label.team-view-only": [ { "type": 0, - "value": "Team view only" + "value": "Толькі для каманднага прагляду" } ], "label.team-websites": [ { "type": 0, - "value": "Team websites" + "value": "Сайты каманды" } ], "label.teams": [ { "type": 0, - "value": "Teams" + "value": "Каманды" } ], "label.theme": [ @@ -1192,13 +1192,13 @@ "label.timezone": [ { "type": 0, - "value": "Таймзона" + "value": "Часавы пояс" } ], "label.title": [ { "type": 0, - "value": "Title" + "value": "Загаловак" } ], "label.today": [ @@ -1216,13 +1216,13 @@ "label.total": [ { "type": 0, - "value": "Total" + "value": "Агульная колькасць" } ], "label.total-records": [ { "type": 0, - "value": "Total records" + "value": "Агульная колькасць запісаў" } ], "label.tracking-code": [ @@ -1234,31 +1234,31 @@ "label.transfer": [ { "type": 0, - "value": "Transfer" + "value": "Перадаць" } ], "label.transfer-website": [ { "type": 0, - "value": "Transfer website" + "value": "Перадаць сайт" } ], "label.true": [ { "type": 0, - "value": "True" + "value": "Ісціна" } ], "label.type": [ { "type": 0, - "value": "Type" + "value": "Тып" } ], "label.unique": [ { "type": 0, - "value": "Unique" + "value": "Унікальны" } ], "label.unique-visitors": [ @@ -1276,13 +1276,13 @@ "label.untitled": [ { "type": 0, - "value": "Untitled" + "value": "Без назвы" } ], "label.update": [ { "type": 0, - "value": "Update" + "value": "Абнавіць" } ], "label.url": [ @@ -1300,7 +1300,7 @@ "label.user": [ { "type": 0, - "value": "User" + "value": "Карыстальнік" } ], "label.username": [ @@ -1312,7 +1312,7 @@ "label.users": [ { "type": 0, - "value": "Users" + "value": "Карыстальнікі" } ], "label.utm": [ @@ -1324,31 +1324,31 @@ "label.utm-description": [ { "type": 0, - "value": "Track your campaigns through UTM parameters." + "value": "Сачыць за кампаніямі з дапамогай UTM-метак." } ], "label.value": [ { "type": 0, - "value": "Value" + "value": "Значэнне" } ], "label.view": [ { "type": 0, - "value": "View" + "value": "Паглядзець" } ], "label.view-details": [ { "type": 0, - "value": "Пабачыць дэталі" + "value": "Паглядзець дэталі" } ], "label.view-only": [ { "type": 0, - "value": "View only" + "value": "Толькі прагляд" } ], "label.views": [ @@ -1360,7 +1360,7 @@ "label.views-per-visit": [ { "type": 0, - "value": "Views per visit" + "value": "Прагляды за наведванне" } ], "label.visit-duration": [ @@ -1378,19 +1378,19 @@ "label.visits": [ { "type": 0, - "value": "Visits" + "value": "Наведванні" } ], "label.website": [ { "type": 0, - "value": "Website" + "value": "Сайт" } ], "label.website-id": [ { "type": 0, - "value": "Website ID" + "value": "Ідэнтыфікатар сайта" } ], "label.websites": [ @@ -1402,7 +1402,7 @@ "label.window": [ { "type": 0, - "value": "Window" + "value": "Вакно" } ], "label.yesterday": [ @@ -1414,7 +1414,7 @@ "message.action-confirmation": [ { "type": 0, - "value": "Type " + "value": "Увядзіце " }, { "type": 1, @@ -1422,7 +1422,7 @@ }, { "type": 0, - "value": " in the box below to confirm." + "value": " у поле ніжэй, каб пацвердзіць." } ], "message.active-users": [ @@ -1432,7 +1432,7 @@ }, { "type": 0, - "value": " тякучых " + "value": " цякучых " }, { "offset": 0, @@ -1462,7 +1462,7 @@ "message.collected-data": [ { "type": 0, - "value": "Collected data" + "value": "Сабраныя дадзеныя" } ], "message.confirm-delete": [ @@ -1482,7 +1482,7 @@ "message.confirm-leave": [ { "type": 0, - "value": "Are you sure you want to leave " + "value": "Вы дакладна хочаце пакінуць " }, { "type": 1, @@ -1496,7 +1496,7 @@ "message.confirm-remove": [ { "type": 0, - "value": "Are you sure you want to remove " + "value": "Вы дакладна хочаце выдаліць " }, { "type": 1, @@ -1524,7 +1524,7 @@ "message.delete-team-warning": [ { "type": 0, - "value": "Deleting a team will also delete all team websites." + "value": "Выдаленне каманды таксама выдаліць усе сайты каманды." } ], "message.delete-website-warning": [ @@ -1546,7 +1546,7 @@ }, { "type": 0, - "value": " on " + "value": " на " }, { "type": 1, @@ -1562,7 +1562,7 @@ "message.incorrect-username-password": [ { "type": 0, - "value": "Некарэктны username/password." + "value": "Некарэктнае імя карыстальніка/пароль." } ], "message.invalid-domain": [ @@ -1574,7 +1574,7 @@ "message.min-password-length": [ { "type": 0, - "value": "Minimum length of " + "value": "Мінімальная даўжыня " }, { "type": 1, @@ -1582,13 +1582,13 @@ }, { "type": 0, - "value": " characters" + "value": " знакаў" } ], "message.new-version-available": [ { "type": 0, - "value": "A new version of Umami " + "value": "Даступная новая версія Umami " }, { "type": 1, @@ -1596,7 +1596,7 @@ }, { "type": 0, - "value": " is available!" + "value": "!" } ], "message.no-data-available": [ @@ -1608,7 +1608,7 @@ "message.no-event-data": [ { "type": 0, - "value": "No event data is available." + "value": "Дадзеныя падзеі недаступныя." } ], "message.no-match-password": [ @@ -1620,31 +1620,31 @@ "message.no-results-found": [ { "type": 0, - "value": "No results were found." + "value": "Вынікаў не знойдзена." } ], "message.no-team-websites": [ { "type": 0, - "value": "This team does not have any websites." + "value": "Гэтая каманда не мае ніводнага сайта." } ], "message.no-teams": [ { "type": 0, - "value": "You have not created any teams." + "value": "Вы не стварылі ніводнай каманды." } ], "message.no-users": [ { "type": 0, - "value": "There are no users." + "value": "Няма карыстальнікаў." } ], "message.no-websites-configured": [ { "type": 0, - "value": "Вы не наладзілі ніводнага сайту." + "value": "Вы не наладзілі ніводнага сайта." } ], "message.page-not-found": [ @@ -1656,7 +1656,7 @@ "message.reset-website": [ { "type": 0, - "value": "To reset this website, type " + "value": "Каб скінуць гэты сайт, увядзіце " }, { "type": 1, @@ -1664,13 +1664,13 @@ }, { "type": 0, - "value": " in the box below to confirm." + "value": " у поле ніжэй для пацверджання." } ], "message.reset-website-warning": [ { "type": 0, - "value": "Уся статыстыка для гэтага сайту будзе выдалена, але код адсочвання будзе працягваць працаваць." + "value": "Уся статыстыка для гэтага сайта будзе выдалена, але код адсочвання будзе працягваць працаваць." } ], "message.saved": [ @@ -1696,19 +1696,19 @@ "message.team-already-member": [ { "type": 0, - "value": "You are already a member of the team." + "value": "Вы ўжо ўдзельнік каманды." } ], "message.team-not-found": [ { "type": 0, - "value": "Team not found." + "value": "Каманда не знойдзена." } ], "message.team-websites-info": [ { "type": 0, - "value": "Websites can be viewed by anyone on the team." + "value": "Сайты могуць быць праглядацца любым удзельнікам каманды." } ], "message.tracking-code": [ @@ -1720,37 +1720,37 @@ "message.transfer-team-website-to-user": [ { "type": 0, - "value": "Transfer this website to your account?" + "value": "Перадаць гэты сайт на ваш уліковы запіс?" } ], "message.transfer-user-website-to-team": [ { "type": 0, - "value": "Select the team to transfer this website to." + "value": "Выберыце каманду для перадачы гэтага сайта." } ], "message.transfer-website": [ { "type": 0, - "value": "Transfer website ownership to your account or another team." + "value": "Перадача сайта на ваш уліковы запіс або іншай камандзе." } ], "message.triggered-event": [ { "type": 0, - "value": "Triggered event" + "value": "Падзея якая спрацавала" } ], "message.user-deleted": [ { "type": 0, - "value": "User deleted." + "value": "Карыстальнік выдалены." } ], "message.viewed-page": [ { "type": 0, - "value": "Viewed page" + "value": "Праглядзеў старонку" } ], "message.visitor-log": [ @@ -1790,7 +1790,7 @@ "message.visitors-dropped-off": [ { "type": 0, - "value": "Visitors dropped off" + "value": "Наведвальнікі сышлі" } ] } diff --git a/public/intl/messages/de-DE.json b/public/intl/messages/de-DE.json index a7d1f1ab60..713238bc75 100644 --- a/public/intl/messages/de-DE.json +++ b/public/intl/messages/de-DE.json @@ -74,7 +74,7 @@ "label.analytics": [ { "type": 0, - "value": "Analytics" + "value": "Analysen" } ], "label.average": [ @@ -152,7 +152,7 @@ "label.compare": [ { "type": 0, - "value": "Vergleich" + "value": "Vergleichen" } ], "label.confirm": [ @@ -368,7 +368,7 @@ "label.dropoff": [ { "type": 0, - "value": "Dropoff" + "value": "Absprung" } ], "label.edit": [ @@ -398,13 +398,13 @@ "label.end-step": [ { "type": 0, - "value": "Schritt beenden" + "value": "Schlussschritt" } ], "label.entry": [ { "type": 0, - "value": "Entry URL" + "value": "Eintrags-URL" } ], "label.event": [ @@ -428,7 +428,7 @@ "label.exit": [ { "type": 0, - "value": "Exit URL" + "value": "Ausgangs-URL" } ], "label.false": [ @@ -482,7 +482,7 @@ "label.funnel-description": [ { "type": 0, - "value": "Verstehe die Konversions- und Dropoffrate von Nutzern." + "value": "Verstehe die Konversions- und Abbruchrate der Nutzer." } ], "label.goal": [ @@ -500,7 +500,7 @@ "label.goals-description": [ { "type": 0, - "value": "Verfolgen Sie Ihre Ziele für Aufrufe und Events." + "value": "Verfolgen Sie Ihre Ziele für Seitenaufrufe und Ereignisse." } ], "label.greater-than": [ @@ -530,13 +530,13 @@ "label.insights": [ { "type": 0, - "value": "Insights" + "value": "Einblicke" } ], "label.insights-description": [ { "type": 0, - "value": "Tauchen Sie tiefer in Ihre Daten mit Filtern und Segmenten ein." + "value": "Vertiefen Sie Ihre Daten mit Hilfe von Segmenten und Filtern." } ], "label.is": [ @@ -578,13 +578,13 @@ "label.journey": [ { "type": 0, - "value": "Journey" + "value": "Reise" } ], "label.journey-description": [ { "type": 0, - "value": "Verstehen Sie, wie Nutzer Ihre Website navigieren." + "value": "Verstehe, wie Nutzer auf der Webseite navigieren" } ], "label.language": [ @@ -608,7 +608,7 @@ "label.last-days": [ { "type": 0, - "value": "Letzte " + "value": "Letzten " }, { "type": 1, @@ -622,7 +622,7 @@ "label.last-hours": [ { "type": 0, - "value": "Letzte " + "value": "Letzten " }, { "type": 1, @@ -636,7 +636,7 @@ "label.last-months": [ { "type": 0, - "value": "Last " + "value": "Letzten " }, { "type": 1, @@ -644,7 +644,7 @@ }, { "type": 0, - "value": " months" + "value": " Monate" } ], "label.leave": [ @@ -692,7 +692,7 @@ "label.manager": [ { "type": 0, - "value": "Manager" + "value": "Verwaltung" } ], "label.max": [ @@ -734,13 +734,13 @@ "label.my-account": [ { "type": 0, - "value": "Mein Konto" + "value": "Mein Account" } ], "label.my-websites": [ { "type": 0, - "value": "Meine Websites" + "value": "Meine Webseiten" } ], "label.name": [ @@ -874,13 +874,13 @@ "label.previous": [ { "type": 0, - "value": "Vorherige" + "value": "Vorheriges" } ], "label.previous-period": [ { "type": 0, - "value": "Vorheriger Zeitraum" + "value": "Vorherige Periode" } ], "label.previous-year": [ @@ -898,7 +898,7 @@ "label.property": [ { "type": 0, - "value": "Besitz" + "value": "Eigentum" } ], "label.queries": [ @@ -1000,13 +1000,13 @@ "label.retention": [ { "type": 0, - "value": "Retention" + "value": "Bewahrung" } ], "label.retention-description": [ { "type": 0, - "value": "Messen Sie die Presenz Ihrer Website, indem Sie tracken wie oft Nutzer zurückkehren." + "value": "Messen Sie die Verweildauer auf Ihrer Website, indem Sie verfolgen, wie oft die Nutzer zurückkehren." } ], "label.role": [ @@ -1060,7 +1060,7 @@ "label.select-website": [ { "type": 0, - "value": "Website auswählen" + "value": "Webseite auswählen" } ], "label.sessions": [ @@ -1090,7 +1090,7 @@ "label.start-step": [ { "type": 0, - "value": "Schritt starten" + "value": "Startschritt" } ], "label.steps": [ @@ -1150,7 +1150,7 @@ "label.team-view-only": [ { "type": 0, - "value": "Team view only" + "value": "Nur für Team sichtbar" } ], "label.team-websites": [ @@ -1324,7 +1324,7 @@ "label.utm-description": [ { "type": 0, - "value": "Tracken Sie Ihre Kampagnen mit Hilfe von UTM Parametern." + "value": "Tracke deine Kampagnen mit UTM parameterns." } ], "label.value": [ @@ -1384,19 +1384,19 @@ "label.website": [ { "type": 0, - "value": "Website" + "value": "Webseite" } ], "label.website-id": [ { "type": 0, - "value": "Website ID" + "value": "Webseiten-ID" } ], "label.websites": [ { "type": 0, - "value": "Websites" + "value": "Webseiten" } ], "label.window": [ @@ -1414,7 +1414,7 @@ "message.action-confirmation": [ { "type": 0, - "value": "Tippen Sie " + "value": "Schreibe " }, { "type": 1, @@ -1422,7 +1422,7 @@ }, { "type": 0, - "value": " in das untenliegende Feld, um zu bestätigen." + "value": " in die Box zur bestätigung." } ], "message.active-users": [ @@ -1496,7 +1496,7 @@ "message.confirm-remove": [ { "type": 0, - "value": "Sind Sie sicher, dass Sie " + "value": "Sind Sie sicher, " }, { "type": 1, @@ -1504,7 +1504,7 @@ }, { "type": 0, - "value": " entfernen möchten?" + "value": " zu entfernen?" } ], "message.confirm-reset": [ @@ -1524,7 +1524,7 @@ "message.delete-team-warning": [ { "type": 0, - "value": "Alle zugehörigen Websiten werden ebenfalls gelöscht." + "value": "Ein Team zu löschen, wird auch alle Team-Webseiten löschen." } ], "message.delete-website-warning": [ @@ -1708,25 +1708,25 @@ "message.transfer-team-website-to-user": [ { "type": 0, - "value": "Möchten Sie diese Website auf Ihr Konto übertragen?" + "value": "Diese Webseite zu deinem Account transferieren?" } ], "message.transfer-user-website-to-team": [ { "type": 0, - "value": "Wählen Sie das Team, auf das die Website übertragen wird." + "value": "Wähle ein Team aus, zu dem die Webseite transferiert werden soll." } ], "message.transfer-website": [ { "type": 0, - "value": "Übertragen Sie den Besitz der Website auf Ihren Account oder ein anderes Team." + "value": "Übertrage die Eigentümerrechte zu deinem Account oder einem anderen Team." } ], "message.triggered-event": [ { "type": 0, - "value": "Event ausgelöst" + "value": "Ausgelöstes Event" } ], "message.user-deleted": [ @@ -1738,7 +1738,7 @@ "message.viewed-page": [ { "type": 0, - "value": "Seite besucht" + "value": "Besuchte Seite" } ], "message.visitor-log": [ @@ -1778,7 +1778,7 @@ "message.visitors-dropped-off": [ { "type": 0, - "value": "Besucher haben die Seite verlassen" + "value": "Besucherverlust" } ] } diff --git a/public/intl/messages/es-ES.json b/public/intl/messages/es-ES.json index 496fabc664..825877e4fa 100644 --- a/public/intl/messages/es-ES.json +++ b/public/intl/messages/es-ES.json @@ -38,7 +38,7 @@ "label.add-step": [ { "type": 0, - "value": "Add step" + "value": "Añadir paso" } ], "label.add-website": [ @@ -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": "Contar" } ], "label.countries": [ @@ -230,13 +230,13 @@ "label.created-by": [ { "type": 0, - "value": "Created By" + "value": "Creado por" } ], "label.current": [ { "type": 0, - "value": "Current" + "value": "Actual" } ], "label.current-password": [ @@ -386,7 +386,7 @@ "label.edit-member": [ { "type": 0, - "value": "Edit member" + "value": "Editar miembro" } ], "label.enable-share-url": [ @@ -398,13 +398,13 @@ "label.end-step": [ { "type": 0, - "value": "End Step" + "value": "Paso final" } ], "label.entry": [ { "type": 0, - "value": "Entry URL" + "value": "URL de entrada" } ], "label.event": [ @@ -428,7 +428,7 @@ "label.exit": [ { "type": 0, - "value": "Exit URL" + "value": "URL de salida" } ], "label.false": [ @@ -476,7 +476,7 @@ "label.funnel": [ { "type": 0, - "value": "Funnel" + "value": "Embudo" } ], "label.funnel-description": [ @@ -488,19 +488,19 @@ "label.goal": [ { "type": 0, - "value": "Goal" + "value": "Objetivo" } ], "label.goals": [ { "type": 0, - "value": "Goals" + "value": "Objetivos" } ], "label.goals-description": [ { "type": 0, - "value": "Track your goals for pageviews and events." + "value": "Realice un seguimiento de sus objetivos de páginas vistas y eventos." } ], "label.greater-than": [ @@ -578,13 +578,13 @@ "label.journey": [ { "type": 0, - "value": "Journey" + "value": "Viaje" } ], "label.journey-description": [ { "type": 0, - "value": "Understand how users navigate through your website." + "value": "Comprenda cómo los usuarios navegan por su sitio web." } ], "label.language": [ @@ -636,7 +636,7 @@ "label.last-months": [ { "type": 0, - "value": "Last " + "value": "Últimos " }, { "type": 1, @@ -644,7 +644,7 @@ }, { "type": 0, - "value": " months" + "value": " meses" } ], "label.leave": [ @@ -698,7 +698,7 @@ "label.max": [ { "type": 0, - "value": "Máx" + "value": "Max" } ], "label.member": [ @@ -716,7 +716,7 @@ "label.min": [ { "type": 0, - "value": "Mín" + "value": "Min" } ], "label.mobile": [ @@ -874,19 +874,19 @@ "label.previous": [ { "type": 0, - "value": "Previous" + "value": "Anterior" } ], "label.previous-period": [ { "type": 0, - "value": "Previous period" + "value": "Periodo anterior" } ], "label.previous-year": [ { "type": 0, - "value": "Previous year" + "value": "Año anterior" } ], "label.profile": [ @@ -898,7 +898,7 @@ "label.property": [ { "type": 0, - "value": "Property" + "value": "Propiedad" } ], "label.queries": [ @@ -952,7 +952,7 @@ "label.region": [ { "type": 0, - "value": "Region" + "value": "Región" } ], "label.regions": [ @@ -1042,7 +1042,7 @@ "label.select": [ { "type": 0, - "value": "Select" + "value": "Seleccionar" } ], "label.select-date": [ @@ -1090,13 +1090,13 @@ "label.start-step": [ { "type": 0, - "value": "Start Step" + "value": "Paso inical" } ], "label.steps": [ { "type": 0, - "value": "Steps" + "value": "Pasos" } ], "label.sum": [ @@ -1126,7 +1126,7 @@ "label.team-manager": [ { "type": 0, - "value": "Team manager" + "value": "Jefe de equipo" } ], "label.team-member": [ @@ -1282,7 +1282,7 @@ "label.update": [ { "type": 0, - "value": "Update" + "value": "Actualizar" } ], "label.url": [ @@ -1324,7 +1324,7 @@ "label.utm-description": [ { "type": 0, - "value": "Track your campaigns through UTM parameters." + "value": "Realice un seguimiento de sus campañas a través de parámetros UTM." } ], "label.value": [ @@ -1360,7 +1360,7 @@ "label.views-per-visit": [ { "type": 0, - "value": "Views per visit" + "value": "Vistas por visita" } ], "label.visit-duration": [ @@ -1378,7 +1378,7 @@ "label.visits": [ { "type": 0, - "value": "Visits" + "value": "Visitas" } ], "label.website": [ @@ -1462,7 +1462,7 @@ "message.collected-data": [ { "type": 0, - "value": "Collected data" + "value": "Datos obtenidos" } ], "message.confirm-delete": [ @@ -1726,7 +1726,7 @@ "message.transfer-user-website-to-team": [ { "type": 0, - "value": "Select the team to transfer this website to." + "value": "Seleccione el equipo al que transferir este sitio web." } ], "message.transfer-website": [ diff --git a/public/intl/messages/km-KH.json b/public/intl/messages/km-KH.json index 841dabdd33..ad273d2932 100644 --- a/public/intl/messages/km-KH.json +++ b/public/intl/messages/km-KH.json @@ -14,31 +14,31 @@ "label.activity-log": [ { "type": 0, - "value": "Activity log" + "value": "កំណត់ហេតុ​សកម្មភាព" } ], "label.add": [ { "type": 0, - "value": "Add" + "value": "បង្កើតបន្ថែម" } ], "label.add-description": [ { "type": 0, - "value": "Add description" + "value": "បន្ថែមពិពណ៌នា" } ], "label.add-member": [ { "type": 0, - "value": "Add member" + "value": "បន្ថែមសមាជិក" } ], "label.add-step": [ { "type": 0, - "value": "Add step" + "value": "បន្ថែមជំហាន" } ], "label.add-website": [ @@ -56,7 +56,7 @@ "label.after": [ { "type": 0, - "value": "After" + "value": "បន្ទាប់" } ], "label.all": [ @@ -80,7 +80,7 @@ "label.average": [ { "type": 0, - "value": "Average" + "value": "ជាមធ្យម" } ], "label.back": [ @@ -92,7 +92,7 @@ "label.before": [ { "type": 0, - "value": "Before" + "value": "មុន" } ], "label.bounce-rate": [ @@ -134,31 +134,31 @@ "label.cities": [ { "type": 0, - "value": "Cities" + "value": "ទីក្រុង" } ], "label.city": [ { "type": 0, - "value": "City" + "value": "ទីក្រុង" } ], "label.clear-all": [ { "type": 0, - "value": "Clear all" + "value": "លុបទាំងអស់" } ], "label.compare": [ { "type": 0, - "value": "Compare" + "value": "ប្រៀបធៀប" } ], "label.confirm": [ { "type": 0, - "value": "Confirm" + "value": "បញ្ជាក់" } ], "label.confirm-password": [ @@ -170,19 +170,19 @@ "label.contains": [ { "type": 0, - "value": "Contains" + "value": "មាន" } ], "label.continue": [ { "type": 0, - "value": "Continue" + "value": "បន្ត" } ], "label.count": [ { "type": 0, - "value": "Count" + "value": "ចំនួន" } ], "label.countries": [ @@ -194,49 +194,49 @@ "label.country": [ { "type": 0, - "value": "Country" + "value": "ប្រទេស" } ], "label.create": [ { "type": 0, - "value": "Create" + "value": "បង្កើត" } ], "label.create-report": [ { "type": 0, - "value": "Create report" + "value": "បង្កើតរបាយការណ៍" } ], "label.create-team": [ { "type": 0, - "value": "Create team" + "value": "បង្កើតក្រុម" } ], "label.create-user": [ { "type": 0, - "value": "Create user" + "value": "បង្កើតអ្នកប្រើប្រាស់" } ], "label.created": [ { "type": 0, - "value": "Created" + "value": "បង្កើតនៅ" } ], "label.created-by": [ { "type": 0, - "value": "Created By" + "value": "បង្កើតដោយ" } ], "label.current": [ { "type": 0, - "value": "Current" + "value": "បច្ចុប្បន្ន" } ], "label.current-password": [ @@ -260,13 +260,13 @@ "label.data": [ { "type": 0, - "value": "Data" + "value": "ទិន្នន័យ" } ], "label.date": [ { "type": 0, - "value": "Date" + "value": "កាលបរិច្ឆេទ" } ], "label.date-range": [ @@ -278,13 +278,13 @@ "label.day": [ { "type": 0, - "value": "Day" + "value": "ថ្ងៃ" } ], "label.default-date-range": [ { "type": 0, - "value": "ចន្លោះកាលបរិច្ឆេទស្រាប់" + "value": "ចន្លោះកាលបរិច្ឆេទដើម" } ], "label.delete": [ @@ -296,19 +296,19 @@ "label.delete-report": [ { "type": 0, - "value": "Delete report" + "value": "លុបរបាយការណ៍" } ], "label.delete-team": [ { "type": 0, - "value": "Delete team" + "value": "លុបក្រុម" } ], "label.delete-user": [ { "type": 0, - "value": "Delete user" + "value": "លុបអ្នកប្រើប្រាស់" } ], "label.delete-website": [ @@ -320,7 +320,7 @@ "label.description": [ { "type": 0, - "value": "Description" + "value": "ការពិពណ៌នា" } ], "label.desktop": [ @@ -332,13 +332,13 @@ "label.details": [ { "type": 0, - "value": "Details" + "value": "ព័ត៌មានលម្អិត" } ], "label.device": [ { "type": 0, - "value": "Device" + "value": "ឧបករណ៍" } ], "label.devices": [ @@ -350,19 +350,19 @@ "label.dismiss": [ { "type": 0, - "value": "បដិសេធ" + "value": "រំសាយ" } ], "label.does-not-contain": [ { "type": 0, - "value": "Does not contain" + "value": "មិនមាន" } ], "label.domain": [ { "type": 0, - "value": "ឈ្មោះគេហទំព័រ" + "value": "Domain" } ], "label.dropoff": [ @@ -386,7 +386,7 @@ "label.edit-member": [ { "type": 0, - "value": "Edit member" + "value": "កែព័ត៌មានសមាជិក" } ], "label.enable-share-url": [ @@ -398,25 +398,25 @@ "label.end-step": [ { "type": 0, - "value": "End Step" + "value": "បញ្ចប់ជំហាន" } ], "label.entry": [ { "type": 0, - "value": "Entry URL" + "value": "URL ចូល" } ], "label.event": [ { "type": 0, - "value": "Event" + "value": "ព្រឹត្តិការណ៍" } ], "label.event-data": [ { "type": 0, - "value": "Event data" + "value": "ទិន្នន័យព្រឹត្តិការណ៍" } ], "label.events": [ @@ -428,13 +428,13 @@ "label.exit": [ { "type": 0, - "value": "Exit URL" + "value": "URL ចេញ" } ], "label.false": [ { "type": 0, - "value": "False" + "value": "មិនពិត" } ], "label.field": [ @@ -452,7 +452,7 @@ "label.filter": [ { "type": 0, - "value": "Filter" + "value": "ចម្រោះ" } ], "label.filter-combined": [ @@ -470,7 +470,7 @@ "label.filters": [ { "type": 0, - "value": "Filters" + "value": "ចម្រោះ" } ], "label.funnel": [ @@ -488,31 +488,31 @@ "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": "តាមដានគោលដៅរបស់អ្នកសម្រាប់ pageviews និង events។" } ], "label.greater-than": [ { "type": 0, - "value": "Greater than" + "value": "ធំជាង" } ], "label.greater-than-equals": [ { "type": 0, - "value": "Greater than or equals" + "value": "ធំជាងឬស្មើ" } ], "label.host": [ @@ -542,49 +542,49 @@ "label.is": [ { "type": 0, - "value": "Is" + "value": "គឺ" } ], "label.is-not": [ { "type": 0, - "value": "Is not" + "value": "មិនមែន" } ], "label.is-not-set": [ { "type": 0, - "value": "Is not set" + "value": "មិនបានកំណត់" } ], "label.is-set": [ { "type": 0, - "value": "Is set" + "value": "បានកំណត់" } ], "label.join": [ { "type": 0, - "value": "Join" + "value": "ចូលរួម" } ], "label.join-team": [ { "type": 0, - "value": "Join team" + "value": "ចូលក្រុម" } ], "label.journey": [ { "type": 0, - "value": "Journey" + "value": "​ដំណើរ" } ], "label.journey-description": [ { "type": 0, - "value": "Understand how users navigate through your website." + "value": "ស្វែងយល់ពីការប្រើប្រាស់គេហទំព័ររបស់អតិថិជនអ្នក។" } ], "label.language": [ @@ -612,7 +612,7 @@ }, { "type": 0, - "value": " ថ្ងៃមុន" + "value": " ថ្ងៃចុងក្រោយ" } ], "label.last-hours": [ @@ -622,45 +622,41 @@ }, { "type": 0, - "value": " ម៉ោងមុន" + "value": " ម៉ោងចុងក្រោយ" } ], "label.last-months": [ - { - "type": 0, - "value": "Last " - }, { "type": 1, "value": "x" }, { "type": 0, - "value": " months" + "value": " ខែចុងក្រោយ" } ], "label.leave": [ { "type": 0, - "value": "Leave" + "value": "ចាកចេញ" } ], "label.leave-team": [ { "type": 0, - "value": "Leave team" + "value": "ចេញពីក្រុម" } ], "label.less-than": [ { "type": 0, - "value": "Less than" + "value": "តិច​ជាង" } ], "label.less-than-equals": [ { "type": 0, - "value": "Less than or equals" + "value": "តិចជាង ឬស្មើ" } ], "label.login": [ @@ -672,19 +668,19 @@ "label.logout": [ { "type": 0, - "value": "ចេញ" + "value": "Logout" } ], "label.manage": [ { "type": 0, - "value": "Manage" + "value": "គ្រប់គ្រង" } ], "label.manager": [ { "type": 0, - "value": "Manager" + "value": "អ្នកគ្រប់គ្រង" } ], "label.max": [ @@ -696,13 +692,13 @@ "label.member": [ { "type": 0, - "value": "Member" + "value": "សមាជិក" } ], "label.members": [ { "type": 0, - "value": "Members" + "value": "សមាជិក" } ], "label.min": [ @@ -726,13 +722,13 @@ "label.my-account": [ { "type": 0, - "value": "My account" + "value": "គណនី​របស់ខ្ញុំ" } ], "label.my-websites": [ { "type": 0, - "value": "My websites" + "value": "គេហទំព័ររបស់ខ្ញុំ" } ], "label.name": [ @@ -802,7 +798,7 @@ "label.overview": [ { "type": 0, - "value": "Overview" + "value": "ទិដ្ឋភាពរួម" } ], "label.owner": [ @@ -814,7 +810,7 @@ "label.page-of": [ { "type": 0, - "value": "Page " + "value": "ទំព័រទី " }, { "type": 1, @@ -822,7 +818,7 @@ }, { "type": 0, - "value": " of " + "value": " នៃ " }, { "type": 1, @@ -838,7 +834,7 @@ "label.pageTitle": [ { "type": 0, - "value": "Page title" + "value": "ចំណងជើងទំព័រ" } ], "label.pages": [ @@ -866,25 +862,25 @@ "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": [ { "type": 0, - "value": "ប្រវត្តិរូប" + "value": "គណនី" } ], "label.property": [ @@ -920,7 +916,7 @@ "label.referrer": [ { "type": 0, - "value": "Referrer" + "value": "អ្នកណែនាំ" } ], "label.referrers": [ @@ -944,31 +940,31 @@ "label.region": [ { "type": 0, - "value": "Region" + "value": "តំបន់" } ], "label.regions": [ { "type": 0, - "value": "Regions" + "value": "តំបន់" } ], "label.remove": [ { "type": 0, - "value": "Remove" + "value": "លុប" } ], "label.remove-member": [ { "type": 0, - "value": "Remove member" + "value": "លុបសមាជិកក្រុម" } ], "label.reports": [ { "type": 0, - "value": "Reports" + "value": "របាយការណ៍" } ], "label.required": [ @@ -980,7 +976,7 @@ "label.reset": [ { "type": 0, - "value": "កំណត់ឡើងវិញ" + "value": "កែសម្រួល" } ], "label.reset-website": [ @@ -992,7 +988,7 @@ "label.retention": [ { "type": 0, - "value": "Retention" + "value": "ការរក្សាទុក" } ], "label.retention-description": [ @@ -1004,7 +1000,7 @@ "label.role": [ { "type": 0, - "value": "Role" + "value": "មុខងារ" } ], "label.run-query": [ @@ -1028,31 +1024,31 @@ "label.search": [ { "type": 0, - "value": "Search" + "value": "ស្វែងរក" } ], "label.select": [ { "type": 0, - "value": "Select" + "value": "ជ្រើសរើស" } ], "label.select-date": [ { "type": 0, - "value": "Select date" + "value": "ជ្រើសរើសកាលបរិច្ឆេទ" } ], "label.select-role": [ { "type": 0, - "value": "Select role" + "value": "ជ្រើសរើសមុខងារ" } ], "label.select-website": [ { "type": 0, - "value": "Select website" + "value": "ជ្រើសរើសគេហទំព័រ" } ], "label.sessions": [ @@ -1082,13 +1078,13 @@ "label.start-step": [ { "type": 0, - "value": "Start Step" + "value": "ជំហានចាប់ផ្តើម" } ], "label.steps": [ { "type": 0, - "value": "Steps" + "value": "ជំហាន" } ], "label.sum": [ @@ -1106,37 +1102,37 @@ "label.team": [ { "type": 0, - "value": "Team" + "value": "ក្រុម" } ], "label.team-id": [ { "type": 0, - "value": "Team ID" + "value": "ID ក្រុម" } ], "label.team-manager": [ { "type": 0, - "value": "Team manager" + "value": "អ្នកគ្រប់គ្រងក្រុម" } ], "label.team-member": [ { "type": 0, - "value": "Team member" + "value": "សមាជិកក្រុម" } ], "label.team-name": [ { "type": 0, - "value": "Team name" + "value": "ឈ្មោះក្រុម" } ], "label.team-owner": [ { "type": 0, - "value": "Team owner" + "value": "ម្ចាស់ក្រុម" } ], "label.team-view-only": [ @@ -1148,13 +1144,13 @@ "label.team-websites": [ { "type": 0, - "value": "Team websites" + "value": "គេហទំព័ររបស់ក្រុម" } ], "label.teams": [ { "type": 0, - "value": "Teams" + "value": "ក្រុម" } ], "label.theme": [ @@ -1190,7 +1186,7 @@ "label.title": [ { "type": 0, - "value": "Title" + "value": "ចំណងជើង" } ], "label.today": [ @@ -1208,13 +1204,13 @@ "label.total": [ { "type": 0, - "value": "Total" + "value": "សរុប" } ], "label.total-records": [ { "type": 0, - "value": "Total records" + "value": "កំណត់ត្រាសរុប" } ], "label.tracking-code": [ @@ -1226,19 +1222,19 @@ "label.transfer": [ { "type": 0, - "value": "Transfer" + "value": "ការផ្ទេរ" } ], "label.transfer-website": [ { "type": 0, - "value": "Transfer website" + "value": "ការផ្ទេរគេហទំព័រ" } ], "label.true": [ { "type": 0, - "value": "True" + "value": "ពិត" } ], "label.type": [ @@ -1268,7 +1264,7 @@ "label.untitled": [ { "type": 0, - "value": "Untitled" + "value": "គ្មានចំណងជើង" } ], "label.update": [ @@ -1292,7 +1288,7 @@ "label.user": [ { "type": 0, - "value": "User" + "value": "អ្នកប្រើប្រាស់" } ], "label.username": [ @@ -1304,7 +1300,7 @@ "label.users": [ { "type": 0, - "value": "Users" + "value": "អ្នកប្រើប្រាស់" } ], "label.utm": [ @@ -1316,7 +1312,7 @@ "label.utm-description": [ { "type": 0, - "value": "Track your campaigns through UTM parameters." + "value": "តាមដានយុទ្ធនាការរបស់អ្នកតាមរយៈប៉ារ៉ាម៉ែត្រ UTM។" } ], "label.value": [ @@ -1340,7 +1336,7 @@ "label.view-only": [ { "type": 0, - "value": "View only" + "value": "បានតែមើលប៉ុណ្ណោះ" } ], "label.views": [ @@ -1358,7 +1354,7 @@ "label.visit-duration": [ { "type": 0, - "value": "មើលជាមធ្យម" + "value": "រយៈពេលទស្សនា" } ], "label.visitors": [ @@ -1370,19 +1366,19 @@ "label.visits": [ { "type": 0, - "value": "Visits" + "value": "ទស្សនា" } ], "label.website": [ { "type": 0, - "value": "Website" + "value": "គេហទំព័រ" } ], "label.website-id": [ { "type": 0, - "value": "Website ID" + "value": "ID គេហទំព័រ" } ], "label.websites": [ @@ -1406,7 +1402,7 @@ "message.action-confirmation": [ { "type": 0, - "value": "Type " + "value": "សសេរ " }, { "type": 1, @@ -1414,7 +1410,7 @@ }, { "type": 0, - "value": " in the box below to confirm." + "value": " នៅក្នុងប្រអប់ខាងក្រោមដើម្បីបញ្ជាក់។" } ], "message.active-users": [ @@ -1434,7 +1430,7 @@ "message.collected-data": [ { "type": 0, - "value": "Collected data" + "value": "ទិន្នន័យដែលបានប្រមូលទុក" } ], "message.confirm-delete": [ @@ -1454,7 +1450,7 @@ "message.confirm-leave": [ { "type": 0, - "value": "Are you sure you want to leave " + "value": "តើអ្នកប្រាកដថាចង់ចាកចេញ " }, { "type": 1, @@ -1462,13 +1458,13 @@ }, { "type": 0, - "value": "?" + "value": " ទេ?" } ], "message.confirm-remove": [ { "type": 0, - "value": "Are you sure you want to remove " + "value": "តើអ្នកប្រាកដថាចង់លុប " }, { "type": 1, @@ -1476,7 +1472,7 @@ }, { "type": 0, - "value": "?" + "value": " ទេ?" } ], "message.confirm-reset": [ @@ -1496,13 +1492,13 @@ "message.delete-team-warning": [ { "type": 0, - "value": "Deleting a team will also delete all team websites." + "value": "ពេលលុបក្រុម គេហទំព័ររបស់ក្រុមក៏នឹងត្រូវលប់ចោលទាំងអស់ផងដែរ។" } ], "message.delete-website-warning": [ { "type": 0, - "value": "ទិន្នន័យដែលពាក់ព័ន្ធទាំងអស់នឹងត្រូវបានលុបផងដែរ។" + "value": "ទិន្នន័យរបស់គេហទំព័រទាំងអស់នឹងត្រូវលុបចោល។" } ], "message.error": [ @@ -1534,19 +1530,19 @@ "message.incorrect-username-password": [ { "type": 0, - "value": "ឈ្មោះអ្នកប្រើ/ពាក្យសម្ងាត់មិនត្រឹមត្រូវ។" + "value": "ឈ្មោះអ្នកប្រើឬពាក្យសម្ងាត់មិនត្រឹមត្រូវ។" } ], "message.invalid-domain": [ { "type": 0, - "value": "ឈ្មោះគេហទំព័រមិន​ត្រឹមត្រូវ" + "value": "Domain មិន​ត្រឹមត្រូវ" } ], "message.min-password-length": [ { "type": 0, - "value": "Minimum length of " + "value": "តិចបំផុតដែលមានអក្សរ " }, { "type": 1, @@ -1554,13 +1550,13 @@ }, { "type": 0, - "value": " characters" + "value": " តួអក្សរ" } ], "message.new-version-available": [ { "type": 0, - "value": "A new version of Umami " + "value": "Version ថ្មីនៃ Umami " }, { "type": 1, @@ -1568,49 +1564,49 @@ }, { "type": 0, - "value": " is available!" + "value": " អាចប្រើប្រាស់បានហើយ!" } ], "message.no-data-available": [ { "type": 0, - "value": "មិនមានទិន្នន័យទេ។" + "value": "មិនមានទិន្នន័យ។" } ], "message.no-event-data": [ { "type": 0, - "value": "No event data is available." + "value": "មិនមានទិន្នន័យព្រឹត្តិការណ៍ទេ។" } ], "message.no-match-password": [ { "type": 0, - "value": "ពាក្យសម្ងាត់មិនត្រូវគ្នាទេ" + "value": "ពាក្យសម្ងាត់មិនត្រូវគ្នាទេ។" } ], "message.no-results-found": [ { "type": 0, - "value": "No results were found." + "value": "មិនមានលទ្ធផល។" } ], "message.no-team-websites": [ { "type": 0, - "value": "This team does not have any websites." + "value": "ក្រុមនេះមិនមានគេហទំព័រទេ។" } ], "message.no-teams": [ { "type": 0, - "value": "You have not created any teams." + "value": "អ្នកមិនទាន់បានបង្កើតក្រុមណាមួយទេ។" } ], "message.no-users": [ { "type": 0, - "value": "There are no users." + "value": "មិនមានអ្នកប្រើប្រាស់ទេ។" } ], "message.no-websites-configured": [ @@ -1628,7 +1624,7 @@ "message.reset-website": [ { "type": 0, - "value": "To reset this website, type " + "value": "ដើម្បីកែគេហទំព័រនេះឡើងវិញ សូមសរសេរ " }, { "type": 1, @@ -1636,7 +1632,7 @@ }, { "type": 0, - "value": " in the box below to confirm." + "value": " នៅក្នុងប្រអប់ខាងក្រោមដើម្បីបញ្ជាក់។" } ], "message.reset-website-warning": [ @@ -1654,7 +1650,7 @@ "message.share-url": [ { "type": 0, - "value": "នេះគឺជា URL ដែលបានចែករំលែកជាសាធារណៈសម្រាប់ " + "value": "នេះគឺជា URL ដែលអាចចែករំលែកជាសាធារណៈបានសម្រាប់ " }, { "type": 1, @@ -1668,43 +1664,57 @@ "message.team-already-member": [ { "type": 0, - "value": "You are already a member of the team." + "value": "អ្នកគឺជាសមាជិកនៃក្រុមរួចហើយ។" } ], "message.team-not-found": [ { "type": 0, - "value": "Team not found." + "value": "រកក្រុមមិនឃើញទេ។" } ], "message.team-websites-info": [ { "type": 0, - "value": "Websites can be viewed by anyone on the team." + "value": "គេហទំព័រនេះអាចមើលបានតែសមាជិកក្រុមតែប៉ុណ្ណោះ" } ], "message.tracking-code": [ { "type": 0, - "value": "លេខកូដតាមដាន" + "value": "ដើម្បីតាមដានស្ថិតិសម្រាប់គេហទំព័រអ្នក សូមដាក់កូដខាងក្រោមទៅក្នុងផ្នែក " + }, + { + "children": [ + { + "type": 0, + "value": "..." + } + ], + "type": 8, + "value": "head" + }, + { + "type": 0, + "value": " នៃ HTML របស់អ្នក។" } ], "message.transfer-team-website-to-user": [ { "type": 0, - "value": "Transfer this website to your account?" + "value": "ផ្ទេរគេហទំព័រនេះទៅគណនីរបស់អ្នក។?" } ], "message.transfer-user-website-to-team": [ { "type": 0, - "value": "Select the team to transfer this website to." + "value": "ជ្រើសក្រុមដែរត្រូវផ្ទេរគេហទំព័រនេះទៅ។" } ], "message.transfer-website": [ { "type": 0, - "value": "Transfer website ownership to your account or another team." + "value": "ផ្ទេរកម្មសិទ្ធិគេហទំព័រទៅគណនីរបស់អ្នក ឬក្រុមផ្សេងទៀត។" } ], "message.triggered-event": [ @@ -1716,13 +1726,13 @@ "message.user-deleted": [ { "type": 0, - "value": "User deleted." + "value": "អ្នកប្រើប្រាស់ត្រូវបានលុបចោល។" } ], "message.viewed-page": [ { "type": 0, - "value": "Viewed page" + "value": "ទំព័រដែលបានមើល" } ], "message.visitor-log": [ @@ -1762,7 +1772,7 @@ "message.visitors-dropped-off": [ { "type": 0, - "value": "Visitors dropped off" + "value": "ចំនួនអ្នកទស្សនាធ្លាក់ចុះ" } ] } diff --git a/src/app/(main)/reports/ReportsTable.tsx b/src/app/(main)/reports/ReportsTable.tsx index 7cb9cca7b1..c35468aaaa 100644 --- a/src/app/(main)/reports/ReportsTable.tsx +++ b/src/app/(main)/reports/ReportsTable.tsx @@ -1,4 +1,4 @@ -import { GridColumn, GridTable, Icon, Icons, Text, useBreakpoint } from 'react-basics'; +import { GridColumn, GridTable, Icon, Icons, Text } from 'react-basics'; import LinkButton from 'components/common/LinkButton'; import { useMessages, useLogin, useTeamUrl } from 'components/hooks'; import { REPORT_TYPES } from 'lib/constants'; @@ -7,11 +7,10 @@ import ReportDeleteButton from './ReportDeleteButton'; export function ReportsTable({ data = [], showDomain }: { data: any[]; showDomain?: boolean }) { const { formatMessage, labels } = useMessages(); const { user } = useLogin(); - const breakpoint = useBreakpoint(); const { renderTeamUrl } = useTeamUrl(); return ( - + diff --git a/src/app/(main)/reports/create/ReportTemplates.tsx b/src/app/(main)/reports/create/ReportTemplates.tsx index 7a754d5f29..dbff9f1031 100644 --- a/src/app/(main)/reports/create/ReportTemplates.tsx +++ b/src/app/(main)/reports/create/ReportTemplates.tsx @@ -4,6 +4,7 @@ import Magnet from 'assets/magnet.svg'; import Path from 'assets/path.svg'; import Tag from 'assets/tag.svg'; import Target from 'assets/target.svg'; +import Money from 'assets/money.svg'; import { useMessages, useTeamUrl } from 'components/hooks'; import PageHeader from 'components/layout/PageHeader'; import Link from 'next/link'; @@ -51,12 +52,12 @@ export function ReportTemplates({ showHeader = true }: { showHeader?: boolean }) url: renderTeamUrl('/reports/journey'), icon: , }, - // { - // title: formatMessage(labels.revenue), - // description: formatMessage(labels.revenueDescription), - // url: renderTeamUrl('/reports/revenue'), - // icon: , - // }, + { + title: formatMessage(labels.revenue), + description: formatMessage(labels.revenueDescription), + url: renderTeamUrl('/reports/revenue'), + icon: , + }, ]; return ( diff --git a/src/app/(main)/reports/revenue/RevenueReport.tsx b/src/app/(main)/reports/revenue/RevenueReport.tsx index d82f424ac2..d67f57c0d8 100644 --- a/src/app/(main)/reports/revenue/RevenueReport.tsx +++ b/src/app/(main)/reports/revenue/RevenueReport.tsx @@ -4,7 +4,7 @@ import Report from '../[reportId]/Report'; import ReportHeader from '../[reportId]/ReportHeader'; import ReportMenu from '../[reportId]/ReportMenu'; import ReportBody from '../[reportId]/ReportBody'; -import Target from 'assets/target.svg'; +import Money from 'assets/money.svg'; import { REPORT_TYPES } from 'lib/constants'; const defaultParameters = { @@ -15,12 +15,12 @@ const defaultParameters = { export default function RevenueReport({ reportId }: { reportId?: string }) { return ( - } /> + } /> - + ); diff --git a/src/app/(main)/settings/teams/TeamsTable.tsx b/src/app/(main)/settings/teams/TeamsTable.tsx index 787d51c7b0..a7a039580e 100644 --- a/src/app/(main)/settings/teams/TeamsTable.tsx +++ b/src/app/(main)/settings/teams/TeamsTable.tsx @@ -1,4 +1,4 @@ -import { GridColumn, GridTable, Icon, Text, useBreakpoint } from 'react-basics'; +import { GridColumn, GridTable, Icon, Text } from 'react-basics'; import { useMessages } from 'components/hooks'; import Icons from 'components/icons'; import { ROLES } from 'lib/constants'; @@ -13,10 +13,9 @@ export function TeamsTable({ showActions?: boolean; }) { const { formatMessage, labels } = useMessages(); - const breakpoint = useBreakpoint(); return ( - + {row => row.teamUser.find(({ role }) => role === ROLES.teamOwner)?.user?.username} diff --git a/src/app/(main)/settings/users/UsersTable.tsx b/src/app/(main)/settings/users/UsersTable.tsx index 76a0480915..e074be24ed 100644 --- a/src/app/(main)/settings/users/UsersTable.tsx +++ b/src/app/(main)/settings/users/UsersTable.tsx @@ -1,4 +1,4 @@ -import { Text, Icon, Icons, GridTable, GridColumn, useBreakpoint } from 'react-basics'; +import { Text, Icon, Icons, GridTable, GridColumn } from 'react-basics'; import { formatDistance } from 'date-fns'; import { ROLES } from 'lib/constants'; import { useMessages, useLocale } from 'components/hooks'; @@ -14,10 +14,9 @@ export function UsersTable({ }) { const { formatMessage, labels } = useMessages(); const { dateLocale } = useLocale(); - const breakpoint = useBreakpoint(); return ( - + {row => diff --git a/src/app/(main)/settings/websites/WebsitesTable.tsx b/src/app/(main)/settings/websites/WebsitesTable.tsx index dced90be50..5e9aef2c2f 100644 --- a/src/app/(main)/settings/websites/WebsitesTable.tsx +++ b/src/app/(main)/settings/websites/WebsitesTable.tsx @@ -1,5 +1,5 @@ import { ReactNode } from 'react'; -import { Text, Icon, Icons, GridTable, GridColumn, useBreakpoint } from 'react-basics'; +import { Text, Icon, Icons, GridTable, GridColumn } from 'react-basics'; import { useMessages, useTeamUrl } from 'components/hooks'; import LinkButton from 'components/common/LinkButton'; @@ -20,7 +20,6 @@ export function WebsitesTable({ children, }: WebsitesTableProps) { const { formatMessage, labels } = useMessages(); - const breakpoint = useBreakpoint(); const { renderTeamUrl } = useTeamUrl(); if (!data?.length) { @@ -28,7 +27,7 @@ export function WebsitesTable({ } return ( - + {showActions && ( diff --git a/src/app/(main)/teams/[teamId]/settings/members/TeamMembersTable.tsx b/src/app/(main)/teams/[teamId]/settings/members/TeamMembersTable.tsx index 7d1efbf2dd..67cb23c7d6 100644 --- a/src/app/(main)/teams/[teamId]/settings/members/TeamMembersTable.tsx +++ b/src/app/(main)/teams/[teamId]/settings/members/TeamMembersTable.tsx @@ -1,4 +1,4 @@ -import { GridColumn, GridTable, useBreakpoint } from 'react-basics'; +import { GridColumn, GridTable } from 'react-basics'; import { useMessages, useLogin } from 'components/hooks'; import { ROLES } from 'lib/constants'; import TeamMemberRemoveButton from './TeamMemberRemoveButton'; @@ -15,7 +15,6 @@ export function TeamMembersTable({ }) { const { formatMessage, labels } = useMessages(); const { user } = useLogin(); - const breakpoint = useBreakpoint(); const roles = { [ROLES.teamOwner]: formatMessage(labels.teamOwner), @@ -25,7 +24,7 @@ export function TeamMembersTable({ }; return ( - + {row => row?.user?.username} diff --git a/src/app/(main)/teams/[teamId]/settings/websites/TeamWebsitesTable.tsx b/src/app/(main)/teams/[teamId]/settings/websites/TeamWebsitesTable.tsx index c733e3e362..dc6760a6bb 100644 --- a/src/app/(main)/teams/[teamId]/settings/websites/TeamWebsitesTable.tsx +++ b/src/app/(main)/teams/[teamId]/settings/websites/TeamWebsitesTable.tsx @@ -1,4 +1,4 @@ -import { GridColumn, GridTable, Icon, Text, useBreakpoint } from 'react-basics'; +import { GridColumn, GridTable, Icon, Text } from 'react-basics'; import { useLogin, useMessages } from 'components/hooks'; import Icons from 'components/icons'; import LinkButton from 'components/common/LinkButton'; @@ -14,10 +14,9 @@ export function TeamWebsitesTable({ }) { const { user } = useLogin(); const { formatMessage, labels } = useMessages(); - const breakpoint = useBreakpoint(); return ( - + diff --git a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx index bdf0ba7d74..ef0d2b9063 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx @@ -52,7 +52,7 @@ export function WebsiteHeader({ }, { label: formatMessage(labels.events), - icon: , + icon: , path: '/events', }, ]; diff --git a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx index b3414097e2..261f8e4799 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx @@ -1,12 +1,13 @@ -import { GridTable, GridColumn } from 'react-basics'; +import { GridTable, GridColumn, Icon } from 'react-basics'; import { useLocale, useMessages, useTeamUrl } from 'components/hooks'; import Empty from 'components/common/Empty'; -import { formatDistance } from 'date-fns'; import Avatar from 'components/common/Avatar'; import Link from 'next/link'; +import Icons from 'components/icons'; +import { formatDate } from 'lib/date'; export function EventsTable({ data = [] }) { - const { dateLocale } = useLocale(); + const { locale } = useLocale(); const { formatMessage, labels } = useMessages(); const { renderTeamUrl } = useTeamUrl(); @@ -27,20 +28,15 @@ export function EventsTable({ data = [] }) { {row => { return ( <> + {row.eventName ? : } {formatMessage(row.eventName ? labels.triggeredEvent : labels.viewedPage)} - {row.eventName} + {row.eventName || row.urlPath} ); }} - - - {row => - formatDistance(new Date(row.createdAt), new Date(), { - addSuffix: true, - locale: dateLocale, - }) - } + + {row => formatDate(new Date(row.createdAt), 'PPPpp', locale)} ); diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionMetricsBar.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionMetricsBar.tsx deleted file mode 100644 index c4f21875d2..0000000000 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionMetricsBar.tsx +++ /dev/null @@ -1,5 +0,0 @@ -export function SessionMetricsBar({ websiteId }: { websiteId: string }) { - return

{websiteId}

; -} - -export default SessionMetricsBar; diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsMetricsBar.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsMetricsBar.tsx new file mode 100644 index 0000000000..6d3c357957 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsMetricsBar.tsx @@ -0,0 +1,42 @@ +import WebsiteDateFilter from 'components/input/WebsiteDateFilter'; +import { Flexbox, Loading } from 'react-basics'; +import MetricCard from 'components/metrics/MetricCard'; +import { useMessages } from 'components/hooks'; +import useWebsiteStats from 'components/hooks/queries/useWebsiteStats'; +import { formatLongNumber } from 'lib/format'; + +export function SessionsMetricsBar({ websiteId }: { websiteId: string }) { + const { formatMessage, labels } = useMessages(); + const { data, isLoading } = useWebsiteStats(websiteId); + + return ( + + + {isLoading && } + {!isLoading && data && ( + <> + + + + + + )} + + + + ); +} + +export default SessionsMetricsBar; diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx index e95145a7da..afb1873507 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx @@ -1,11 +1,13 @@ 'use client'; import WebsiteHeader from '../WebsiteHeader'; import SessionsDataTable from './SessionsDataTable'; +import SessionsMetricsBar from './SessionsMetricsBar'; export function SessionsPage({ websiteId }) { return ( <> + ); diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx index 9dd6a56dda..6e2e53ed00 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx @@ -1,29 +1,33 @@ import Link from 'next/link'; -import { GridColumn, GridTable, useBreakpoint } from 'react-basics'; +import { GridColumn, GridTable } from 'react-basics'; import { useFormat, useLocale, useMessages } from 'components/hooks'; import Avatar from 'components/common/Avatar'; import styles from './SessionsTable.module.css'; import { formatDate } from 'lib/date'; +import TypeIcon from 'components/common/TypeIcon'; export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean }) { const { locale } = useLocale(); const { formatMessage, labels } = useMessages(); - const breakpoint = useBreakpoint(); const { formatValue } = useFormat(); return ( - - + + {row => ( - {row.id} )} + - {row => formatValue(row.country, 'country')} + {row => ( + + {formatValue(row.country, 'country')} + + )} diff --git a/src/assets/location.svg b/src/assets/location.svg index b0f9af474c..f7f085e416 100644 --- a/src/assets/location.svg +++ b/src/assets/location.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/logo-white.svg b/src/assets/logo-white.svg index 12a76d3471..20c41fb291 100644 --- a/src/assets/logo-white.svg +++ b/src/assets/logo-white.svg @@ -1,4 +1 @@ - - - - + \ No newline at end of file diff --git a/src/assets/money.svg b/src/assets/money.svg new file mode 100644 index 0000000000..954f954d84 --- /dev/null +++ b/src/assets/money.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/common/DataTable.tsx b/src/components/common/DataTable.tsx index 041c2ba5ef..0f011c3331 100644 --- a/src/components/common/DataTable.tsx +++ b/src/components/common/DataTable.tsx @@ -4,13 +4,13 @@ import { Banner, Loading, SearchField } from 'react-basics'; import { useMessages, useNavigation } from 'components/hooks'; import Empty from 'components/common/Empty'; import Pager from 'components/common/Pager'; -import { FilterQueryResult } from 'lib/types'; +import { PagedQueryResult } from 'lib/types'; import styles from './DataTable.module.css'; const DEFAULT_SEARCH_DELAY = 600; export interface DataTableProps { - queryResult: FilterQueryResult; + queryResult: PagedQueryResult; searchDelay?: number; allowSearch?: boolean; allowPaging?: boolean; diff --git a/src/components/common/TypeIcon.tsx b/src/components/common/TypeIcon.tsx index 8952bb2628..475fe6436a 100644 --- a/src/components/common/TypeIcon.tsx +++ b/src/components/common/TypeIcon.tsx @@ -1,17 +1,24 @@ +import { ReactNode } from 'react'; + export function TypeIcon({ type, value, + children, }: { type: 'browser' | 'country' | 'device' | 'os'; value: string; + children?: ReactNode; }) { return ( - {value} + <> + {value} + {children} + ); } diff --git a/src/components/hooks/index.ts b/src/components/hooks/index.ts index e06a0414cd..d92329c78d 100644 --- a/src/components/hooks/index.ts +++ b/src/components/hooks/index.ts @@ -1,6 +1,6 @@ export * from './queries/useApi'; export * from './queries/useConfig'; -export * from './queries/useFilterQuery'; +export * from './queries/usePagedQuery'; export * from './queries/useLogin'; export * from './queries/useRealtime'; export * from './queries/useReport'; @@ -19,6 +19,7 @@ export * from './queries/useUsers'; export * from './queries/useWebsite'; export * from './queries/useWebsites'; export * from './queries/useWebsiteEvents'; +export * from './queries/useWebsiteEventsSeries'; export * from './queries/useWebsiteMetrics'; export * from './queries/useWebsiteValues'; export * from './useCountryNames'; diff --git a/src/components/hooks/queries/useFilterQuery.ts b/src/components/hooks/queries/usePagedQuery.ts similarity index 79% rename from src/components/hooks/queries/useFilterQuery.ts rename to src/components/hooks/queries/usePagedQuery.ts index 82fc4d614f..253f809254 100644 --- a/src/components/hooks/queries/useFilterQuery.ts +++ b/src/components/hooks/queries/usePagedQuery.ts @@ -1,14 +1,14 @@ import { UseQueryOptions } from '@tanstack/react-query'; import { useState } from 'react'; import { useApi } from './useApi'; -import { PageResult, PageParams, FilterQueryResult } from 'lib/types'; +import { PageResult, PageParams, PagedQueryResult } from 'lib/types'; import { useNavigation } from '../useNavigation'; -export function useFilterQuery({ +export function usePagedQuery({ queryKey, queryFn, ...options -}: Omit & { queryFn: (params?: object) => any }): FilterQueryResult { +}: Omit & { queryFn: (params?: object) => any }): PagedQueryResult { const { query: queryParams } = useNavigation(); const [params, setParams] = useState({ query: '', @@ -30,4 +30,4 @@ export function useFilterQuery({ }; } -export default useFilterQuery; +export default usePagedQuery; diff --git a/src/components/hooks/queries/useReports.ts b/src/components/hooks/queries/useReports.ts index 314880f861..88e4f02ebf 100644 --- a/src/components/hooks/queries/useReports.ts +++ b/src/components/hooks/queries/useReports.ts @@ -1,11 +1,11 @@ import useApi from './useApi'; -import useFilterQuery from './useFilterQuery'; +import usePagedQuery from './usePagedQuery'; import useModified from '../useModified'; export function useReports({ websiteId, teamId }: { websiteId?: string; teamId?: string }) { const { modified } = useModified(`reports`); const { get, del, useMutation } = useApi(); - const queryResult = useFilterQuery({ + const queryResult = usePagedQuery({ queryKey: ['reports', { websiteId, teamId, modified }], queryFn: (params: any) => { return get('/reports', { websiteId, teamId, ...params }); diff --git a/src/components/hooks/queries/useTeamMembers.ts b/src/components/hooks/queries/useTeamMembers.ts index 8586087087..a3e8bcd235 100644 --- a/src/components/hooks/queries/useTeamMembers.ts +++ b/src/components/hooks/queries/useTeamMembers.ts @@ -1,12 +1,12 @@ import useApi from './useApi'; -import useFilterQuery from './useFilterQuery'; +import usePagedQuery from './usePagedQuery'; import useModified from '../useModified'; export function useTeamMembers(teamId: string) { const { get } = useApi(); const { modified } = useModified(`teams:members`); - return useFilterQuery({ + return usePagedQuery({ queryKey: ['teams:members', { teamId, modified }], queryFn: (params: any) => { return get(`/teams/${teamId}/users`, params); diff --git a/src/components/hooks/queries/useTeamWebsites.ts b/src/components/hooks/queries/useTeamWebsites.ts index 7679d7672c..fac82d13bf 100644 --- a/src/components/hooks/queries/useTeamWebsites.ts +++ b/src/components/hooks/queries/useTeamWebsites.ts @@ -1,12 +1,12 @@ import useApi from './useApi'; -import useFilterQuery from './useFilterQuery'; +import usePagedQuery from './usePagedQuery'; import useModified from '../useModified'; export function useTeamWebsites(teamId: string) { const { get } = useApi(); const { modified } = useModified(`websites`); - return useFilterQuery({ + return usePagedQuery({ queryKey: ['teams:websites', { teamId, modified }], queryFn: (params: any) => { return get(`/teams/${teamId}/websites`, params); diff --git a/src/components/hooks/queries/useTeams.ts b/src/components/hooks/queries/useTeams.ts index be71080789..e1f7790a54 100644 --- a/src/components/hooks/queries/useTeams.ts +++ b/src/components/hooks/queries/useTeams.ts @@ -1,12 +1,12 @@ import useApi from './useApi'; -import useFilterQuery from './useFilterQuery'; +import usePagedQuery from './usePagedQuery'; import useModified from '../useModified'; export function useTeams(userId: string) { const { get } = useApi(); const { modified } = useModified(`teams`); - return useFilterQuery({ + return usePagedQuery({ queryKey: ['teams', { userId, modified }], queryFn: (params: any) => { return get(`/users/${userId}/teams`, params); diff --git a/src/components/hooks/queries/useUsers.ts b/src/components/hooks/queries/useUsers.ts index 169cf42419..519fb89916 100644 --- a/src/components/hooks/queries/useUsers.ts +++ b/src/components/hooks/queries/useUsers.ts @@ -1,12 +1,12 @@ import useApi from './useApi'; -import useFilterQuery from './useFilterQuery'; +import usePagedQuery from './usePagedQuery'; import useModified from '../useModified'; export function useUsers() { const { get } = useApi(); const { modified } = useModified(`users`); - return useFilterQuery({ + return usePagedQuery({ queryKey: ['users', { modified }], queryFn: (params: any) => { return get('/admin/users', { diff --git a/src/components/hooks/queries/useWebsiteEvents.ts b/src/components/hooks/queries/useWebsiteEvents.ts index f0132470fa..12febcc814 100644 --- a/src/components/hooks/queries/useWebsiteEvents.ts +++ b/src/components/hooks/queries/useWebsiteEvents.ts @@ -1,7 +1,7 @@ import useApi from './useApi'; import { UseQueryOptions } from '@tanstack/react-query'; import { useFilterParams } from '../useFilterParams'; -import { useFilterQuery } from 'components/hooks'; +import { usePagedQuery } from 'components/hooks'; export function useWebsiteEvents( websiteId: string, @@ -10,7 +10,7 @@ export function useWebsiteEvents( const { get } = useApi(); const params = useFilterParams(websiteId); - return useFilterQuery({ + return usePagedQuery({ queryKey: ['websites:events', { websiteId, ...params }], queryFn: pageParams => get(`/websites/${websiteId}/events`, { ...params, ...pageParams, pageSize: 20 }), diff --git a/src/components/hooks/queries/useWebsiteEventsSeries.ts b/src/components/hooks/queries/useWebsiteEventsSeries.ts new file mode 100644 index 0000000000..88b493759a --- /dev/null +++ b/src/components/hooks/queries/useWebsiteEventsSeries.ts @@ -0,0 +1,20 @@ +import useApi from './useApi'; +import { UseQueryOptions } from '@tanstack/react-query'; +import { useFilterParams } from '../useFilterParams'; + +export function useWebsiteEventsSeries( + websiteId: string, + options?: Omit, +) { + const { get, useQuery } = useApi(); + const params = useFilterParams(websiteId); + + return useQuery({ + queryKey: ['websites:events:series', { websiteId, ...params }], + queryFn: () => get(`/websites/${websiteId}/events/series`, { ...params }), + enabled: !!websiteId, + ...options, + }); +} + +export default useWebsiteEventsSeries; diff --git a/src/components/hooks/queries/useWebsiteSessions.ts b/src/components/hooks/queries/useWebsiteSessions.ts index 9430786b1e..e8a1ce2f82 100644 --- a/src/components/hooks/queries/useWebsiteSessions.ts +++ b/src/components/hooks/queries/useWebsiteSessions.ts @@ -1,17 +1,20 @@ import { useApi } from './useApi'; -import { useFilterQuery } from './useFilterQuery'; +import { usePagedQuery } from './usePagedQuery'; import useModified from '../useModified'; +import { useFilterParams } from 'components/hooks/useFilterParams'; export function useWebsiteSessions(websiteId: string, params?: { [key: string]: string | number }) { const { get } = useApi(); const { modified } = useModified(`sessions`); + const filters = useFilterParams(websiteId); - return useFilterQuery({ - queryKey: ['sessions', { websiteId, modified, ...params }], + return usePagedQuery({ + queryKey: ['sessions', { websiteId, modified, ...params, ...filters }], queryFn: (data: any) => { return get(`/websites/${websiteId}/sessions`, { ...data, ...params, + ...filters, }); }, }); diff --git a/src/components/hooks/queries/useWebsites.ts b/src/components/hooks/queries/useWebsites.ts index 941989750d..d6fe00df79 100644 --- a/src/components/hooks/queries/useWebsites.ts +++ b/src/components/hooks/queries/useWebsites.ts @@ -1,5 +1,5 @@ import { useApi } from './useApi'; -import { useFilterQuery } from './useFilterQuery'; +import { usePagedQuery } from './usePagedQuery'; import { useLogin } from './useLogin'; import useModified from '../useModified'; @@ -11,7 +11,7 @@ export function useWebsites( const { user } = useLogin(); const { modified } = useModified(`websites`); - return useFilterQuery({ + return usePagedQuery({ queryKey: ['websites', { userId, teamId, modified, ...params }], queryFn: (data: any) => { return get(teamId ? `/teams/${teamId}/websites` : `/users/${userId || user.id}/websites`, { diff --git a/src/components/messages.ts b/src/components/messages.ts index 703ccba3c8..75fa2d3e6c 100644 --- a/src/components/messages.ts +++ b/src/components/messages.ts @@ -88,8 +88,8 @@ export const labels = defineMessages({ leaveTeam: { id: 'label.leave-team', defaultMessage: 'Leave team' }, refresh: { id: 'label.refresh', defaultMessage: 'Refresh' }, pages: { id: 'label.pages', defaultMessage: 'Pages' }, - entry: { id: 'label.entry', defaultMessage: 'Entry URL' }, - exit: { id: 'label.exit', defaultMessage: 'Exit URL' }, + entry: { id: 'label.entry', defaultMessage: 'Entry path' }, + exit: { id: 'label.exit', defaultMessage: 'Exit path' }, referrers: { id: 'label.referrers', defaultMessage: 'Referrers' }, hosts: { id: 'label.hosts', defaultMessage: 'Hosts' }, screens: { id: 'label.screens', defaultMessage: 'Screens' }, @@ -165,6 +165,8 @@ export const labels = defineMessages({ }, url: { id: 'label.url', defaultMessage: 'URL' }, urls: { id: 'label.urls', defaultMessage: 'URLs' }, + path: { id: 'label.path', defaultMessage: 'Path' }, + paths: { id: 'label.paths', defaultMessage: 'Paths' }, add: { id: 'label.add', defaultMessage: 'Add' }, update: { id: 'label.update', defaultMessage: 'Update' }, window: { id: 'label.window', defaultMessage: 'Window' }, @@ -276,7 +278,6 @@ export const labels = defineMessages({ lastSeen: { id: 'label.last-seen', defaultMessage: 'Last seen' }, firstSeen: { id: 'label.first-seen', defaultMessage: 'First seen' }, properties: { id: 'label.properties', defaultMessage: 'Properties' }, - path: { id: 'label.path', defaultMessage: 'Path' }, }); export const messages = defineMessages({ diff --git a/src/components/metrics/EventsChart.tsx b/src/components/metrics/EventsChart.tsx index 36ec867233..0bb65eb298 100644 --- a/src/components/metrics/EventsChart.tsx +++ b/src/components/metrics/EventsChart.tsx @@ -2,7 +2,7 @@ import { useMemo } from 'react'; import { colord } from 'colord'; import BarChart from 'components/charts/BarChart'; import { getDateArray } from 'lib/date'; -import { useLocale, useDateRange, useWebsiteEvents } from 'components/hooks'; +import { useLocale, useDateRange, useWebsiteEventsSeries } from 'components/hooks'; import { CHART_COLORS } from 'lib/constants'; import { renderDateLabels } from 'lib/charts'; @@ -16,7 +16,7 @@ export function EventsChart({ websiteId, className }: EventsChartProps) { dateRange: { startDate, endDate, unit }, } = useDateRange(websiteId); const { locale } = useLocale(); - const { data, isLoading } = useWebsiteEvents(websiteId); + const { data, isLoading } = useWebsiteEventsSeries(websiteId); const chartData = useMemo(() => { if (!data) return []; diff --git a/src/components/metrics/MetricCard.tsx b/src/components/metrics/MetricCard.tsx index a5fd758ec3..64f2a1b6b2 100644 --- a/src/components/metrics/MetricCard.tsx +++ b/src/components/metrics/MetricCard.tsx @@ -10,7 +10,7 @@ export interface MetricCardProps { change?: number; label?: string; reverseColors?: boolean; - formatValue?: typeof formatNumber; + formatValue?: (n: any) => string; showLabel?: boolean; showChange?: boolean; showPrevious?: boolean; diff --git a/src/components/metrics/PagesTable.tsx b/src/components/metrics/PagesTable.tsx index 767c4764b4..b2d8ca9c30 100644 --- a/src/components/metrics/PagesTable.tsx +++ b/src/components/metrics/PagesTable.tsx @@ -25,7 +25,7 @@ export function PagesTable({ allowFilter, ...props }: PagesTableProps) { const buttons = [ { - label: 'URL', + label: formatMessage(labels.path), key: 'url', }, { diff --git a/src/lib/types.ts b/src/lib/types.ts index e659655202..3bce527151 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -41,7 +41,7 @@ export interface PageResult { sortDescending?: boolean; } -export interface FilterQueryResult { +export interface PagedQueryResult { result: PageResult; query: any; params: PageParams; diff --git a/src/pages/api/websites/[websiteId]/sessions/index.ts b/src/pages/api/websites/[websiteId]/sessions/index.ts index 4f242882fb..69f777f503 100644 --- a/src/pages/api/websites/[websiteId]/sessions/index.ts +++ b/src/pages/api/websites/[websiteId]/sessions/index.ts @@ -14,6 +14,8 @@ export interface ReportsRequestQuery extends PageParams { const schema = { GET: yup.object().shape({ websiteId: yup.string().uuid().required(), + startAt: yup.number().integer().required(), + endAt: yup.number().integer().min(yup.ref('startAt')).required(), ...pageInfo, }), }; @@ -26,14 +28,17 @@ export default async ( await useAuth(req, res); await useValidate(schema, req, res); - const { websiteId } = req.query; + const { websiteId, startAt, endAt } = req.query; + + const startDate = new Date(+startAt); + const endDate = new Date(+endAt); if (req.method === 'GET') { if (!(await canViewWebsite(req.auth, websiteId))) { return unauthorized(res); } - const data = await getWebsiteSessions(websiteId, {}, req.query); + const data = await getWebsiteSessions(websiteId, { startDate, endDate }, req.query); return ok(res, data); } diff --git a/src/queries/analytics/getWebsiteStats.ts b/src/queries/analytics/getWebsiteStats.ts index c5141d3be7..7f7f4f19e2 100644 --- a/src/queries/analytics/getWebsiteStats.ts +++ b/src/queries/analytics/getWebsiteStats.ts @@ -34,12 +34,14 @@ async function relationalQuery( sum(t.c) as "pageviews", count(distinct t.session_id) as "visitors", count(distinct t.visit_id) as "visits", + count(distinct t.country) as "countries", sum(case when t.c = 1 then 1 else 0 end) as "bounces", - sum(${getTimestampDiffSQL('t.min_time', 't.max_time')}) as "totaltime" + sum(${getTimestampDiffSQL('t.min_time', 't.max_time')}) as "totaltime", from ( select website_event.session_id, website_event.visit_id, + session.country, count(*) as "c", min(website_event.created_at) as "min_time", max(website_event.created_at) as "max_time" @@ -49,7 +51,7 @@ async function relationalQuery( and website_event.created_at between {{startDate}} and {{endDate}} and event_type = {{eventType}} ${filterQuery} - group by 1, 2 + group by 1, 2, 3 ) as t `, params, @@ -76,12 +78,14 @@ async function clickhouseQuery( sum(t.c) as "pageviews", uniq(t.session_id) as "visitors", uniq(t.visit_id) as "visits", + uniq(t.country) as "countries", sum(if(t.c = 1, 1, 0)) as "bounces", sum(max_time-min_time) as "totaltime" from ( select session_id, visit_id, + country, count(*) c, min(created_at) min_time, max(created_at) max_time @@ -90,7 +94,7 @@ async function clickhouseQuery( and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type = {eventType:UInt32} ${filterQuery} - group by session_id, visit_id + group by session_id, visit_id, country ) as t; `; } else { @@ -99,20 +103,23 @@ async function clickhouseQuery( sum(t.c) as "pageviews", uniq(session_id) as "visitors", uniq(visit_id) as "visits", + uniq(country) as "countries", sumIf(1, t.c = 1) as "bounces", sum(max_time-min_time) as "totaltime" - from (select - session_id, - visit_id, - sum(views) c, - min(min_time) min_time, - max(max_time) max_time - from umami.website_event_stats_hourly "website_event" - where website_id = {websiteId:UUID} - and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type = {eventType:UInt32} - ${filterQuery} - group by session_id, visit_id + from ( + select + session_id, + visit_id, + country, + sum(views) c, + min(min_time) min_time, + max(max_time) max_time + from umami.website_event_stats_hourly "website_event" + where website_id = {websiteId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} + and event_type = {eventType:UInt32} + ${filterQuery} + group by session_id, visit_id, country ) as t; `; } @@ -125,6 +132,7 @@ async function clickhouseQuery( visits: Number(a.visits), bounces: Number(a.bounces), totaltime: Number(a.totaltime), + countries: Number(a.countries), }; }); }); diff --git a/src/queries/analytics/sessions/getWebsiteSessions.ts b/src/queries/analytics/sessions/getWebsiteSessions.ts index c5fe6cec99..94d2827ee1 100644 --- a/src/queries/analytics/sessions/getWebsiteSessions.ts +++ b/src/queries/analytics/sessions/getWebsiteSessions.ts @@ -43,7 +43,8 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar city, min(created_at) as firstAt, max(created_at) as lastAt, - uniq(visit_id) as visits + uniq(visit_id) as visits, + count(*) as views from website_event where website_id = {websiteId:UUID} ${dateQuery} diff --git a/yarn.lock b/yarn.lock index eee877d39c..fc4169df90 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1199,17 +1199,17 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@clickhouse/client-common@1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@clickhouse/client-common/-/client-common-1.3.0.tgz#9fe4b88eedb233770832cb1c0794f4ad9fbec75c" - integrity sha512-fApbhu52WSQlBU0gMZQxd2akuWbI1c9BcWjIbDDgtNZX2OggrIB7a4oI845ZGXpeZCZDI9ZqtkXzbYQYS0Yqew== +"@clickhouse/client-common@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@clickhouse/client-common/-/client-common-1.4.0.tgz#6936f667ba901724bf9174cc2b783507e921868f" + integrity sha512-kglG8YyWnR1K24RckUf5ZdNTN0U0s+a1j/bpCO4ZjzjO87ICgWlXFVD22pZqSACW7/2IIi1IkzbwtxKI2s/MOw== "@clickhouse/client@^1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@clickhouse/client/-/client-1.3.0.tgz#298529b970a9ba2ae5e017258aaf6cf522cb2b51" - integrity sha512-baBiuwVpXg/DHCe9Y1pNf+tcE2ZalCAQqZsR9OhP7+01C3UqTjHeY4eYixNlpfZCb8c8R4GygdWJFbXF0aGklw== + version "1.4.0" + resolved "https://registry.yarnpkg.com/@clickhouse/client/-/client-1.4.0.tgz#84e17f8c0f2ba109d8e76c3d4f5830aeff5e9459" + integrity sha512-O4mbFPM/wQtFck01ghYI2mnNHv9jSFEiQBsTCH4t6MKeGHNAPkJGaFGv+KycLTv6zjnQNjiUGdXDMVRema5SyA== dependencies: - "@clickhouse/client-common" "1.3.0" + "@clickhouse/client-common" "1.4.0" "@colors/colors@1.5.0": version "1.5.0" @@ -1349,7 +1349,7 @@ resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-3.0.2.tgz#ea61ba7bb24be3502c6aaa3190ed231f4633a81e" integrity sha512-RpHaZ1h9LE7aALeQXmXrJkRG84ZxIsctEN2biEUmFyKpzFM3zZ35eUMcIzZFsw/2olQE6v69+esEqU2f1MKycg== -"@cypress/request@^3.0.0": +"@cypress/request@^3.0.0", "@cypress/request@^3.0.1": version "3.0.1" resolved "https://registry.yarnpkg.com/@cypress/request/-/request-3.0.1.tgz#72d7d5425236a2413bd3d8bb66d02d9dc3168960" integrity sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ== @@ -2188,19 +2188,19 @@ integrity sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw== "@netlify/plugin-nextjs@^5.1.0": - version "5.3.3" - resolved "https://registry.yarnpkg.com/@netlify/plugin-nextjs/-/plugin-nextjs-5.3.3.tgz#414cb0f4c21e6b80d6127b824efb00ae2f732a89" - integrity sha512-QhvZLOHhPuTnh6TZ5G0/jtjAJ1Y52A67b39eygKv6znQUPti8p+8y2WulcntpCRzVp2stzEULMNPlnptna1ikg== + version "5.6.0" + resolved "https://registry.yarnpkg.com/@netlify/plugin-nextjs/-/plugin-nextjs-5.6.0.tgz#970f96b11bee4fe115fad8e3e4f3c6121f97a370" + integrity sha512-PBrsd/GJZ9MN8BdyIoleTkY22lAUMfcRxrbb8wgxGzXtTW0RU0GW2mc99ISB6zOwWMZ11rSjeN0GS6znnukvww== "@next/env@14.2.5": version "14.2.5" resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.5.tgz#1d9328ab828711d3517d0a1d505acb55e5ef7ad0" integrity sha512-/zZGkrTOsraVfYjGP8uM0p6r0BDT6xWpkjdVbcz66PJVSpwXX3yNiRycxAuDfBKGWBrZBXRuK/YVlkNgxHGwmA== -"@next/eslint-plugin-next@14.2.4": - version "14.2.4" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.4.tgz#c7f965cb76f0b454e726ef0f69157c4fb4e28f53" - integrity sha512-svSFxW9f3xDaZA3idQmlFw7SusOuWTpDTAeBlO3AEPDltrraV+lqs7mAc6A27YdnpQVVIA3sODqUAAHdWhVWsA== +"@next/eslint-plugin-next@14.2.5": + version "14.2.5" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.5.tgz#f7e3ff3efe40a2855e5f29bc2692175f85913ba8" + integrity sha512-LY3btOpPh+OTIpviNojDpUdIbHW9j0JBYBjsIp8IxtDFfYFyORvw3yNq6N231FVqQA7n7lwaf7xHbVJlA1ED7g== dependencies: glob "10.3.10" @@ -2275,90 +2275,96 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@prisma/client@5.16.2": - version "5.16.2" - resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.16.2.tgz#21df4c092eac29dcc4431ebed8c92579ffc9d8bd" - integrity sha512-+1lmkhR9gHWcTC5oghm2ZKpWljyWdzfazCVlLKUWXVmwHSf52g81aZ8qb6Km5Bs025yBi7puLp3qSLEvktoUtw== +"@prisma/client@5.17.0": + version "5.17.0" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.17.0.tgz#9079947bd749689c2dabfb9ecc70a24ebefb1f43" + integrity sha512-N2tnyKayT0Zf7mHjwEyE8iG7FwTmXDHFZ1GnNhQp0pJUObsuel4ZZ1XwfuAYkq5mRIiC/Kot0kt0tGCfLJ70Jw== -"@prisma/debug@5.16.2": - version "5.16.2" - resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-5.16.2.tgz#bf774f4768a3d1634887e6964fcfe04576dc7496" - integrity sha512-ItzB4nR4O8eLzuJiuP3WwUJfoIvewMHqpGCad+64gvThcKEVOtaUza9AEJo2DPqAOa/AWkFyK54oM4WwHeew+A== +"@prisma/debug@5.17.0": + version "5.17.0" + resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-5.17.0.tgz#a765105848993984535b6066f8ebc6e6ead26533" + integrity sha512-l7+AteR3P8FXiYyo496zkuoiJ5r9jLQEdUuxIxNCN1ud8rdbH3GTxm+f+dCyaSv9l9WY+29L9czaVRXz9mULfg== -"@prisma/engines-version@5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303": - version "5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303" - resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303.tgz#63ceebefb7daa1eb17f250cad75d35999a50ee1b" - integrity sha512-HkT2WbfmFZ9WUPyuJHhkiADxazHg8Y4gByrTSVeb3OikP6tjQ7txtSUGu9OBOBH0C13dPKN2qqH12xKtHu/Hiw== +"@prisma/engines-version@5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053": + version "5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053.tgz#3c7cc1ef3ebc34cbd069e5873b9982f2aabf5acd" + integrity sha512-tUuxZZysZDcrk5oaNOdrBnnkoTtmNQPkzINFDjz7eG6vcs9AVDmA/F6K5Plsb2aQc/l5M2EnFqn3htng9FA4hg== -"@prisma/engines@5.16.2": - version "5.16.2" - resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.16.2.tgz#d624c816c25a5cbad7ca13c7842b71557b6020e0" - integrity sha512-qUxwMtrwoG3byd4PbX6T7EjHJ8AUhzTuwniOGkh/hIznBfcE2QQnGakyEq4VnwNuttMqvh/GgPFapHQ3lCuRHg== +"@prisma/engines@5.17.0": + version "5.17.0" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.17.0.tgz#74dd1aabb22675892760b3cf69a448e3aef4616b" + integrity sha512-+r+Nf+JP210Jur+/X8SIPLtz+uW9YA4QO5IXA+KcSOBe/shT47bCcRMTYCbOESw3FFYFTwe7vU6KTWHKPiwvtg== dependencies: - "@prisma/debug" "5.16.2" - "@prisma/engines-version" "5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303" - "@prisma/fetch-engine" "5.16.2" - "@prisma/get-platform" "5.16.2" + "@prisma/debug" "5.17.0" + "@prisma/engines-version" "5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053" + "@prisma/fetch-engine" "5.17.0" + "@prisma/get-platform" "5.17.0" "@prisma/extension-read-replicas@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@prisma/extension-read-replicas/-/extension-read-replicas-0.3.0.tgz#2842a7c928f957c1dd58a6256104797596d43426" integrity sha512-F9+rSmYday6GT2qjhJtkZcBOpLO5LtpvFcMGqrBDHf+78LEdSuxfFjOxYlNuqk4B+th62yxpbhfpmB9/Mca14Q== -"@prisma/fetch-engine@5.16.2": - version "5.16.2" - resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-5.16.2.tgz#e7e06cf246340e6ae06791f9133be275c5ee50e5" - integrity sha512-sq51lfHKfH2jjYSjBtMjP+AznFqOJzXpqmq6B9auWrlTJrMgZ7lPyhWUW7VU7LsQU48/TJ+DZeIz8s9bMYvcHg== +"@prisma/fetch-engine@5.17.0": + version "5.17.0" + resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-5.17.0.tgz#f718dc7426411d1ebeeee53e2d0d38652387f87c" + integrity sha512-ESxiOaHuC488ilLPnrv/tM2KrPhQB5TRris/IeIV4ZvUuKeaicCl4Xj/JCQeG9IlxqOgf1cCg5h5vAzlewN91Q== dependencies: - "@prisma/debug" "5.16.2" - "@prisma/engines-version" "5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303" - "@prisma/get-platform" "5.16.2" + "@prisma/debug" "5.17.0" + "@prisma/engines-version" "5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053" + "@prisma/get-platform" "5.17.0" -"@prisma/get-platform@5.16.2": - version "5.16.2" - resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-5.16.2.tgz#f9b397ddf807b71411fd9d0b17374bc21e91cc40" - integrity sha512-cXiHPgNLNyj22vLouPVNegklpRL/iX2jxTeap5GRO3DmCoVyIHmJAV1CgUMUJhHlcol9yYy7EHvsnXTDJ/PKEA== +"@prisma/get-platform@5.17.0": + version "5.17.0" + resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-5.17.0.tgz#89fdcae2adddebbbf0e7bd0474a6c49d6023519b" + integrity sha512-UlDgbRozCP1rfJ5Tlkf3Cnftb6srGrEQ4Nm3og+1Se2gWmCZ0hmPIi+tQikGDUVLlvOWx3Gyi9LzgRP+HTXV9w== dependencies: - "@prisma/debug" "5.16.2" + "@prisma/debug" "5.17.0" -"@react-spring/animated@~9.7.3": - version "9.7.3" - resolved "https://registry.yarnpkg.com/@react-spring/animated/-/animated-9.7.3.tgz#4211b1a6d48da0ff474a125e93c0f460ff816e0f" - integrity sha512-5CWeNJt9pNgyvuSzQH+uy2pvTg8Y4/OisoscZIR8/ZNLIOI+CatFBhGZpDGTF/OzdNFsAoGk3wiUYTwoJ0YIvw== +"@react-spring/animated@~9.7.4": + version "9.7.4" + resolved "https://registry.yarnpkg.com/@react-spring/animated/-/animated-9.7.4.tgz#c712b2d3dc9312ef41aa8886818b539151bda062" + integrity sha512-7As+8Pty2QlemJ9O5ecsuPKjmO0NKvmVkRR1n6mEotFgWar8FKuQt2xgxz3RTgxcccghpx1YdS1FCdElQNexmQ== dependencies: - "@react-spring/shared" "~9.7.3" - "@react-spring/types" "~9.7.3" + "@react-spring/shared" "~9.7.4" + "@react-spring/types" "~9.7.4" -"@react-spring/core@~9.7.3": - version "9.7.3" - resolved "https://registry.yarnpkg.com/@react-spring/core/-/core-9.7.3.tgz#60056bcb397f2c4f371c6c9a5f882db77ae90095" - integrity sha512-IqFdPVf3ZOC1Cx7+M0cXf4odNLxDC+n7IN3MDcVCTIOSBfqEcBebSv+vlY5AhM0zw05PDbjKrNmBpzv/AqpjnQ== +"@react-spring/core@~9.7.4": + version "9.7.4" + resolved "https://registry.yarnpkg.com/@react-spring/core/-/core-9.7.4.tgz#0eaa0b5da3d18036d87a571f23079819d45a9f46" + integrity sha512-GzjA44niEJBFUe9jN3zubRDDDP2E4tBlhNlSIkTChiNf9p4ZQlgXBg50qbXfSXHQPHak/ExYxwhipKVsQ/sUTw== dependencies: - "@react-spring/animated" "~9.7.3" - "@react-spring/shared" "~9.7.3" - "@react-spring/types" "~9.7.3" + "@react-spring/animated" "~9.7.4" + "@react-spring/shared" "~9.7.4" + "@react-spring/types" "~9.7.4" + +"@react-spring/rafz@~9.7.4": + version "9.7.4" + resolved "https://registry.yarnpkg.com/@react-spring/rafz/-/rafz-9.7.4.tgz#d53aa45a8cb116b81b27ba29e0cc15470ccfd449" + integrity sha512-mqDI6rW0Ca8IdryOMiXRhMtVGiEGLIO89vIOyFQXRIwwIMX30HLya24g9z4olDvFyeDW3+kibiKwtZnA4xhldA== -"@react-spring/shared@~9.7.3": - version "9.7.3" - resolved "https://registry.yarnpkg.com/@react-spring/shared/-/shared-9.7.3.tgz#4cf29797847c689912aec4e62e34c99a4d5d9e53" - integrity sha512-NEopD+9S5xYyQ0pGtioacLhL2luflh6HACSSDUZOwLHoxA5eku1UPuqcJqjwSD6luKjjLfiLOspxo43FUHKKSA== +"@react-spring/shared@~9.7.4": + version "9.7.4" + resolved "https://registry.yarnpkg.com/@react-spring/shared/-/shared-9.7.4.tgz#8ac57505072c2aee33d77c47c4269347061a3377" + integrity sha512-bEPI7cQp94dOtCFSEYpxvLxj0+xQfB5r9Ru1h8OMycsIq7zFZon1G0sHrBLaLQIWeMCllc4tVDYRTLIRv70C8w== dependencies: - "@react-spring/types" "~9.7.3" + "@react-spring/rafz" "~9.7.4" + "@react-spring/types" "~9.7.4" -"@react-spring/types@~9.7.3": - version "9.7.3" - resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.7.3.tgz#ea78fd447cbc2612c1f5d55852e3c331e8172a0b" - integrity sha512-Kpx/fQ/ZFX31OtlqVEFfgaD1ACzul4NksrvIgYfIFq9JpDHFwQkMVZ10tbo0FU/grje4rcL4EIrjekl3kYwgWw== +"@react-spring/types@~9.7.4": + version "9.7.4" + resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.7.4.tgz#c849a7f062b5163d078e5e75f28c8f6acf91792e" + integrity sha512-iQVztO09ZVfsletMiY+DpT/JRiBntdsdJ4uqk3UJFhrhS8mIC9ZOZbmfGSRs/kdbNPQkVyzucceDicQ/3Mlj9g== "@react-spring/web@^9.7.3": - version "9.7.3" - resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.7.3.tgz#d9f4e17fec259f1d65495a19502ada4f5b57fa3d" - integrity sha512-BXt6BpS9aJL/QdVqEIX9YoUy8CE6TJrU0mNCqSoxdXlIeNcEBWOfIyE6B14ENNsyQKS3wOWkiJfco0tCr/9tUg== + version "9.7.4" + resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.7.4.tgz#0086ab5dcf17e6a8f3d7e7f8041ccb4cc2fa10dc" + integrity sha512-UMvCZp7I5HCVIleSa4BwbNxynqvj+mJjG2m20VO2yPoi2pnCYANy58flvz9v/YcXTAvsmL655FV3pm5fbr6akA== dependencies: - "@react-spring/animated" "~9.7.3" - "@react-spring/core" "~9.7.3" - "@react-spring/shared" "~9.7.3" - "@react-spring/types" "~9.7.3" + "@react-spring/animated" "~9.7.4" + "@react-spring/core" "~9.7.4" + "@react-spring/shared" "~9.7.4" + "@react-spring/types" "~9.7.4" "@redis/bloom@1.2.0": version "1.2.0" @@ -2459,9 +2465,9 @@ picomatch "^2.3.1" "@rushstack/eslint-patch@^1.3.3": - version "1.10.3" - resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.10.3.tgz#391d528054f758f81e53210f1a1eebcf1a8b1d20" - integrity sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg== + version "1.10.4" + resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz#427d5549943a9c6fce808e39ea64dbe60d4047f1" + integrity sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA== "@sinclair/typebox@^0.27.8": version "0.27.8" @@ -2616,17 +2622,17 @@ "@swc/counter" "^0.1.3" tslib "^2.4.0" -"@tanstack/query-core@5.48.0": - version "5.48.0" - resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.48.0.tgz#a3308ec925d8c16d64c789899d6c084c2fe30cbc" - integrity sha512-lZAfPPeVIqXCswE9SSbG33B6/91XOWt/Iq41bFeWb/mnHwQSIfFRbkS4bfs+WhIk9abRArF9Id2fp0Mgo+hq6Q== +"@tanstack/query-core@5.51.21": + version "5.51.21" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.51.21.tgz#a510469c6c30d3de2a8b8798e340169a4b0fd08f" + integrity sha512-POQxm42IUp6n89kKWF4IZi18v3fxQWFRolvBA6phNVmA8psdfB1MvDnGacCJdS+EOX12w/CyHM62z//rHmYmvw== "@tanstack/react-query@^5.28.6": - version "5.48.0" - resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.48.0.tgz#7890620272b48aeb278498dfe082f27518f3ac6d" - integrity sha512-GDExbjYWzvDokyRqMSWXdrPiYpp95Aig0oeMIrxTaruOJJgWiWfUP//OAaowm2RrRkGVsavSZdko/XmIrrV2Nw== + version "5.51.21" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.51.21.tgz#cdd14677bcc809a83e01b6c38842c841ce7420af" + integrity sha512-Q/V81x3sAYgCsxjwOkfLXfrmoG+FmDhLeHH5okC/Bp8Aaw2c33lbEo/mMcMnkxUPVtB2FLpzHT0tq3c+OlZEbw== dependencies: - "@tanstack/query-core" "5.48.0" + "@tanstack/query-core" "5.51.21" "@trysound/sax@0.2.0": version "0.2.0" @@ -2833,12 +2839,12 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.5.tgz#ec10755e871497bcd83efe927e43ec46e8c0747e" integrity sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag== -"@types/node@*", "@types/node@^20.9.0": - version "20.14.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.9.tgz#12e8e765ab27f8c421a1820c99f5f313a933b420" - integrity sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg== +"@types/node@*": + version "22.1.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.1.0.tgz#6d6adc648b5e03f0e83c78dc788c2b037d0ad94b" + integrity sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw== dependencies: - undici-types "~5.26.4" + undici-types "~6.13.0" "@types/node@14": version "14.18.63" @@ -2850,6 +2856,13 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== +"@types/node@^20.9.0": + version "20.14.14" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.14.tgz#6b655d4a88623b0edb98300bb9dd2107225f885e" + integrity sha512-d64f00982fS9YoOgJkAMolK7MN8Iq3TDdVjchbYHdEmjth/DHowx82GnoA+tVUAN+7vxfYUgAzi+JXbKNd2SDQ== + dependencies: + undici-types "~5.26.4" + "@types/normalize-package-data@^2.4.0": version "2.4.3" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.3.tgz#291c243e4b94dbfbc0c0ee26b7666f1d5c030e2c" @@ -3471,16 +3484,6 @@ array.prototype.flatmap@^1.3.2: es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" -array.prototype.toreversed@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz#b989a6bf35c4c5051e1dc0325151bf8088954eba" - integrity sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - es-shim-unscopables "^1.0.0" - array.prototype.tosorted@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz#fe954678ff53034e717ea3352a03f0b0b86f7ffc" @@ -3533,7 +3536,7 @@ astral-regex@^2.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== -async@^3.2.0: +async@^3.2.0, async@^3.2.3: version "3.2.5" resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== @@ -3578,9 +3581,9 @@ aws4@^1.8.0: integrity sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g== axe-core@^4.9.1: - version "4.9.1" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.9.1.tgz#fcd0f4496dad09e0c899b44f6c4bb7848da912ae" - integrity sha512-QbUdXJVTpvUTHU7871ppZkdOLBeGUKBQWHkHrvN2V9IQWGMt61zf3B45BtzjxEJzYuj0JBjBZP/hmYS/R9pmAw== + version "4.10.0" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.0.tgz#d9e56ab0147278272739a000880196cdfe113b59" + integrity sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g== axobject-query@~3.1.1: version "3.1.1" @@ -3916,7 +3919,7 @@ chalk@^2.4.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -4448,11 +4451,11 @@ cypress@*: yauzl "^2.10.0" cypress@^13.6.6: - version "13.12.0" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.12.0.tgz#1a4ea89b7fa6855e32bc02eaf5e25fc45b9e273f" - integrity sha512-udzS2JilmI9ApO/UuqurEwOvThclin5ntz7K0BtnHBs+tg2Bl9QShLISXpSEMDv/u8b6mqdoAdyKeZiSqKWL8g== + version "13.13.2" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.13.2.tgz#c71f8d92056c430b1b879e5313f6de25ccce0eda" + integrity sha512-PvJQU33933NvS1StfzEb8/mu2kMy4dABwCF+yd5Bi7Qly1HOVf+Bufrygee/tlmty/6j5lX+KIi8j9Q3JUMbhA== dependencies: - "@cypress/request" "^3.0.0" + "@cypress/request" "^3.0.1" "@cypress/xvfb" "^1.2.4" "@types/sinonjs__fake-timers" "8.1.1" "@types/sizzle" "^2.3.2" @@ -4491,7 +4494,7 @@ cypress@^13.6.6: request-progress "^3.0.0" semver "^7.5.3" supports-color "^8.1.1" - tmp "~0.2.1" + tmp "~0.2.3" untildify "^4.0.0" yauzl "^2.10.0" @@ -4633,9 +4636,9 @@ dateformat@^5.0.3: integrity sha512-Kvr6HmPXUMerlLcLF+Pwq3K7apHpYmGDVqrxcDasBg86UcKeTSNWbEzU8bwdXnxnR44FtMhJAxI4Bov6Y/KUfA== dayjs@^1.10.4: - version "1.11.11" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.11.tgz#dfe0e9d54c5f8b68ccf8ca5f72ac603e7e5ed59e" - integrity sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg== + version "1.11.12" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.12.tgz#5245226cc7f40a15bf52e0b99fd2a04669ccac1d" + integrity sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg== debounce@^1.2.1: version "1.2.1" @@ -4657,9 +4660,9 @@ debug@^3.1.0, debug@^3.2.7: ms "^2.1.1" debug@^4.1.1, debug@^4.3.4: - version "4.3.5" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" - integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== + version "4.3.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" + integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== dependencies: ms "2.1.2" @@ -4905,6 +4908,13 @@ ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer "^5.0.1" +ejs@^3.1.10: + version "3.1.10" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" + integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== + dependencies: + jake "^10.8.5" + electron-to-chromium@^1.4.535: version "1.4.561" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.561.tgz#816f31d9ae01fe58abbf469fca7e125b16befd85" @@ -4938,9 +4948,9 @@ end-of-stream@^1.1.0: once "^1.4.0" enhanced-resolve@^5.12.0: - version "5.17.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz#d037603789dd9555b89aaec7eb78845c49089bc5" - integrity sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA== + version "5.17.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" + integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -5155,11 +5165,11 @@ escape-string-regexp@^4.0.0: integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== eslint-config-next@^14.0.4: - version "14.2.4" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-14.2.4.tgz#eb0bedfe4a894bc2aea918214bb5243ee4fa7d4b" - integrity sha512-Qr0wMgG9m6m4uYy2jrYJmyuNlYZzPRQq5Kvb9IDlYwn+7yq6W6sfMNFgb+9guM1KYwuIo6TIaiFhZJ6SnQ/Efw== + version "14.2.5" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-14.2.5.tgz#cdd43d89047eb7391ba25445d5855b4600b6adb9" + integrity sha512-zogs9zlOiZ7ka+wgUnmcM0KBEDjo4Jis7kxN1jvC0N4wynQ2MIx/KBkg4mVF63J5EK4W0QMCn7xO3vNisjaAoA== dependencies: - "@next/eslint-plugin-next" "14.2.4" + "@next/eslint-plugin-next" "14.2.5" "@rushstack/eslint-patch" "^1.3.3" "@typescript-eslint/parser" "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0" eslint-import-resolver-node "^0.3.6" @@ -5283,9 +5293,9 @@ eslint-plugin-prettier@^4.0.0: prettier-linter-helpers "^1.0.0" eslint-plugin-promise@^6.1.1: - version "6.2.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.2.0.tgz#e24ab0e3c0a25fa227d98d9ff612156b5af15945" - integrity sha512-QmAqwizauvnKOlifxyDj2ObfULpHQawlg/zQdgEixur9vl0CvZGv/LCJV2rtj3210QCoeGBzVMfMXqGAOr/4fA== + version "6.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz#acd3fd7d55cead7a10f92cf698f36c0aafcd717a" + integrity sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ== "eslint-plugin-react-hooks@^4.5.0 || 5.0.0-canary-7118f5dd7-20230705": version "4.6.2" @@ -5293,28 +5303,28 @@ eslint-plugin-promise@^6.1.1: integrity sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ== eslint-plugin-react@^7.33.2: - version "7.34.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.34.3.tgz#9965f27bd1250a787b5d4cfcc765e5a5d58dcb7b" - integrity sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA== + version "7.35.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.35.0.tgz#00b1e4559896710e58af6358898f2ff917ea4c41" + integrity sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA== dependencies: array-includes "^3.1.8" array.prototype.findlast "^1.2.5" array.prototype.flatmap "^1.3.2" - array.prototype.toreversed "^1.1.2" array.prototype.tosorted "^1.1.4" doctrine "^2.1.0" es-iterator-helpers "^1.0.19" estraverse "^5.3.0" + hasown "^2.0.2" jsx-ast-utils "^2.4.1 || ^3.0.0" minimatch "^3.1.2" object.entries "^1.1.8" object.fromentries "^2.0.8" - object.hasown "^1.1.4" object.values "^1.2.0" prop-types "^15.8.1" resolve "^2.0.0-next.5" semver "^6.3.1" string.prototype.matchall "^4.0.11" + string.prototype.repeat "^1.0.0" eslint-scope@^5.1.1: version "5.1.1" @@ -5661,6 +5671,13 @@ file-entry-cache@^7.0.0: dependencies: flat-cache "^3.2.0" +filelist@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" + integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== + dependencies: + minimatch "^5.0.1" + fill-range@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" @@ -5794,7 +5811,7 @@ function-bind@^1.1.2: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -function.prototype.name@^1.1.5, function.prototype.name@^1.1.6: +function.prototype.name@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== @@ -5869,9 +5886,9 @@ get-symbol-description@^1.0.2: get-intrinsic "^1.2.4" get-tsconfig@^4.5.0: - version "4.7.5" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.5.tgz#5e012498579e9a6947511ed0cd403272c7acbbaf" - integrity sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw== + version "4.7.6" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.6.tgz#118fd5b7b9bae234cc7705a00cd771d7eb65d62a" + integrity sha512-ZAqrLlu18NbDdRaHq+AKXzAmqIUPswPWKUchfytdAjiRFnCe5ojG2bstg6mRiZabkKfCoL/e98pbBELIV/YCeA== dependencies: resolve-pkg-maps "^1.0.0" @@ -6373,9 +6390,9 @@ is-ci@^3.0.1: ci-info "^3.2.0" is-core-module@^2.11.0, is-core-module@^2.13.0, is-core-module@^2.13.1: - version "2.14.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.14.0.tgz#43b8ef9f46a6a08888db67b1ffd4ec9e3dfd59d1" - integrity sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A== + version "2.15.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.0.tgz#71c72ec5442ace7e76b306e9d48db361f22699ea" + integrity sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA== dependencies: hasown "^2.0.2" @@ -6610,9 +6627,9 @@ isarray@^2.0.5: integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== isbot@^5.1.1: - version "5.1.11" - resolved "https://registry.yarnpkg.com/isbot/-/isbot-5.1.11.tgz#55e91e887c806a5f07d18ce04c2efe194215ba36" - integrity sha512-Asuou7OsKVbATgXlrlqdeSRw4fYnD6CNcc0IXy1f5m1AImL2S6IP0xRcRRzjGjoARbegvnBnju9kk5z/Itf70Q== + version "5.1.13" + resolved "https://registry.yarnpkg.com/isbot/-/isbot-5.1.13.tgz#c291ae9f33d346c070648cb44d6ab2bb163650d1" + integrity sha512-RXtBib4m9zChSb+187EpNQML7Z3u2i34zDdqcRFZnqSJs0xdh91xzJytc5apYVg+9Y4NGnUQ0AIeJvX9FAnCUw== isexe@^2.0.0: version "2.0.0" @@ -6697,6 +6714,16 @@ jackspeak@^2.3.5: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" +jake@^10.8.5: + version "10.9.2" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.9.2.tgz#6ae487e6a69afec3a5e167628996b59f35ae2b7f" + integrity sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA== + dependencies: + async "^3.2.3" + chalk "^4.0.2" + filelist "^1.0.4" + minimatch "^3.1.2" + jest-changed-files@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" @@ -7527,9 +7554,9 @@ lower-case@^2.0.2: tslib "^2.0.3" lru-cache@^10.2.0: - version "10.3.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.3.0.tgz#4a4aaf10c84658ab70f79a85a9a3f1e1fb11196b" - integrity sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ== + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== lru-cache@^5.1.1: version "5.1.1" @@ -7601,12 +7628,12 @@ mathml-tag-names@^2.1.3: integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== maxmind@^4.3.6: - version "4.3.20" - resolved "https://registry.yarnpkg.com/maxmind/-/maxmind-4.3.20.tgz#1b85c4b0d9a8df072c5cbe50dc5c54c130a02889" - integrity sha512-xwnLGghs96DyNK3ANs1jzW8+JwS6pBQ3eTh3kNsi23n5wySZb9FgqCghmde5JZFOv23bxGehcyh6lT522llNKw== + version "4.3.21" + resolved "https://registry.yarnpkg.com/maxmind/-/maxmind-4.3.21.tgz#cc3af09775e9b96434a56b6ac63c9bf4878d23b8" + integrity sha512-orda4yI01roTa4pP5Jf43u5HPOoEIGaLudTn9cdgCPyZDPipTJdbz1boZQR9QE96hvwNDrJwt1ak9vHIE6iZSQ== dependencies: mmdb-lib "2.1.1" - tiny-lru "11.2.6" + tiny-lru "11.2.11" md5@^2.3.0: version "2.3.0" @@ -8050,15 +8077,6 @@ object.groupby@^1.0.1: define-properties "^1.2.1" es-abstract "^1.23.2" -object.hasown@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.4.tgz#e270ae377e4c120cdcb7656ce66884a6218283dc" - integrity sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg== - dependencies: - define-properties "^1.2.1" - es-abstract "^1.23.2" - es-object-atoms "^1.0.0" - object.values@^1.1.6, object.values@^1.1.7, object.values@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.0.tgz#65405a9d92cee68ac2d303002e0b8470a4d9ab1b" @@ -8258,10 +8276,10 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.0.0, picocolors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" + integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" @@ -8880,12 +8898,12 @@ postcss@^8.4.28: source-map-js "^1.0.2" postcss@^8.4.31: - version "8.4.38" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e" - integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== + version "8.4.40" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.40.tgz#eb81f2a4dd7668ed869a6db25999e02e9ad909d8" + integrity sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q== dependencies: nanoid "^3.3.7" - picocolors "^1.0.0" + picocolors "^1.0.1" source-map-js "^1.2.0" prelude-ls@^1.2.1: @@ -8919,12 +8937,12 @@ pretty-format@^29.0.0, pretty-format@^29.7.0: ansi-styles "^5.0.0" react-is "^18.0.0" -prisma@5.16.2: - version "5.16.2" - resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.16.2.tgz#f46f130550bc148e603d25e06e82263fa6ab68e8" - integrity sha512-rFV/xoBR2hBGGlu4LPLQd4U8WVA+tSAmYyFWGPRVfj+xg7N4kiZV4lSk38htSpF+/IuHKzlrbh4SFk8Z18cI8A== +prisma@5.17.0: + version "5.17.0" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.17.0.tgz#267b43921ab94805b010537cffa5ccaf530fa066" + integrity sha512-m4UWkN5lBE6yevqeOxEvmepnL5cNPEjzMw2IqDB59AcEV6w7D8vGljDLd1gPFH+W6gUxw9x7/RmN5dCS/WTPxA== dependencies: - "@prisma/engines" "5.16.2" + "@prisma/engines" "5.17.0" process@^0.11.10: version "0.11.10" @@ -9025,10 +9043,10 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" -react-basics@^0.123.0: - version "0.123.0" - resolved "https://registry.yarnpkg.com/react-basics/-/react-basics-0.123.0.tgz#8b18c1c1a235858dd0d1e329d5b1c3ac9ab48df3" - integrity sha512-5K5b6pSp0uiBMytDeSlWuAHRjkLfwpySAIc1jKM4nuR8E6jf6UxGn4W2I7jI8p220vmdeK8Phsyxht0UxvIxHA== +react-basics@^0.124.0: + version "0.124.0" + resolved "https://registry.yarnpkg.com/react-basics/-/react-basics-0.124.0.tgz#c2c783f634aadccc13aae8a770960d298a10ebe4" + integrity sha512-5E2chyDE0PKKQ9x7kKM18/6UjFsjkPx7mfbTJb4PsWCC5gPi6m5zX7uPWF7omu4/I5Oms/nj16D3pa350WXIyw== dependencies: "@react-spring/web" "^9.7.3" classnames "^2.3.1" @@ -9600,9 +9618,9 @@ semver@^7.3.4, semver@^7.3.7: lru-cache "^6.0.0" semver@^7.5.3, semver@^7.5.4: - version "7.6.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" - integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== serialize-javascript@^4.0.0: version "4.0.0" @@ -9919,6 +9937,14 @@ string.prototype.padend@^3.0.0: define-properties "^1.2.0" es-abstract "^1.22.1" +string.prototype.repeat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz#e90872ee0308b29435aa26275f6e1b762daee01a" + integrity sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + string.prototype.trim@^1.2.9: version "1.2.9" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" @@ -10247,12 +10273,12 @@ tiny-invariant@^1.0.6: resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642" integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw== -tiny-lru@11.2.6: - version "11.2.6" - resolved "https://registry.yarnpkg.com/tiny-lru/-/tiny-lru-11.2.6.tgz#86a4fd0ad615eac1639adf92010e8b944e209fdb" - integrity sha512-0PU3c9PjMnltZaFo2sGYv/nnJsMjG0Cxx8X6FXHPPGjFyoo1SJDxvUXW1207rdiSxYizf31roo+GrkIByQeZoA== +tiny-lru@11.2.11: + version "11.2.11" + resolved "https://registry.yarnpkg.com/tiny-lru/-/tiny-lru-11.2.11.tgz#5089a6a4a157f5a97b82aa930b44d550ac5c4778" + integrity sha512-27BIW0dIWTYYoWNnqSmoNMKe5WIbkXsc0xaCQHd3/3xT2XMuMJrzHdrO9QBFR14emBz1Bu0dOAs2sCBBrvgPQA== -tmp@~0.2.1: +tmp@~0.2.1, tmp@~0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae" integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== @@ -10312,11 +10338,12 @@ ts-api-utils@^1.0.1: integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== ts-jest@^29.1.2: - version "29.1.5" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.5.tgz#d6c0471cc78bffa2cb4664a0a6741ef36cfe8f69" - integrity sha512-UuClSYxM7byvvYfyWdFI+/2UxMmwNyJb0NPkZPQE2hew3RurV7l7zURgOHAd/1I1ZdPpe3GUsXNXAcN8TFKSIg== + version "29.2.4" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.2.4.tgz#38ccf487407d7a63054a72689f6f99b075e296e5" + integrity sha512-3d6tgDyhCI29HlpwIq87sNuI+3Q6GLTTCeYRHCs7vDz+/3GCMwEtV9jezLyl4ZtnBgx00I7hm8PCP8cTksMGrw== dependencies: bs-logger "0.x" + ejs "^3.1.10" fast-json-stable-stringify "2.x" jest-util "^29.0.0" json5 "^2.2.3" @@ -10487,9 +10514,9 @@ typescript@^4.0, typescript@^4.5: integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== typescript@^5.5.3: - version "5.5.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.3.tgz#e1b0a3c394190838a0b168e771b0ad56a0af0faa" - integrity sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ== + version "5.5.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" + integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== unbox-primitive@^1.0.2: version "1.0.2" @@ -10506,6 +10533,11 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +undici-types@~6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.13.0.tgz#e3e79220ab8c81ed1496b5812471afd7cf075ea5" + integrity sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg== + unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" @@ -10668,12 +10700,12 @@ which-boxed-primitive@^1.0.2: is-symbol "^1.0.3" which-builtin-type@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.1.3.tgz#b1b8443707cc58b6e9bf98d32110ff0c2cbd029b" - integrity sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw== + version "1.1.4" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.1.4.tgz#592796260602fc3514a1b5ee7fa29319b72380c3" + integrity sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w== dependencies: - function.prototype.name "^1.1.5" - has-tostringtag "^1.0.0" + function.prototype.name "^1.1.6" + has-tostringtag "^1.0.2" is-async-function "^2.0.0" is-date-object "^1.0.5" is-finalizationregistry "^1.0.2" @@ -10682,10 +10714,10 @@ which-builtin-type@^1.1.3: is-weakref "^1.0.2" isarray "^2.0.5" which-boxed-primitive "^1.0.2" - which-collection "^1.0.1" - which-typed-array "^1.1.9" + which-collection "^1.0.2" + which-typed-array "^1.1.15" -which-collection@^1.0.1: +which-collection@^1.0.1, which-collection@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== @@ -10695,7 +10727,7 @@ which-collection@^1.0.1: is-weakmap "^2.0.2" is-weakset "^2.0.3" -which-typed-array@^1.1.13, which-typed-array@^1.1.14, which-typed-array@^1.1.15, which-typed-array@^1.1.9: +which-typed-array@^1.1.13, which-typed-array@^1.1.14, which-typed-array@^1.1.15: version "1.1.15" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== From c6b81149455fc772f8f86b622cab12e520987932 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Wed, 7 Aug 2024 22:39:36 -0700 Subject: [PATCH 070/132] Updated events page. --- .../[websiteId]/events/EventsDataTable.tsx | 7 +-- .../[websiteId]/events/EventsMetricsBar.tsx | 42 +++++++++++++++ .../[websiteId]/events/EventsPage.tsx | 21 +++++++- .../sessions/SessionsMetricsBar.tsx | 52 +++++++++---------- src/components/common/DataTable.tsx | 2 +- .../hooks/queries/useWebsiteSessions.ts | 1 + src/components/input/DateFilter.module.css | 3 ++ src/components/input/DateFilter.tsx | 4 +- src/components/layout/Grid.module.css | 4 ++ src/components/metrics/MetricsBar.module.css | 2 + .../analytics/events/getWebsiteEvents.ts | 4 +- src/queries/analytics/getWebsiteStats.ts | 22 ++++---- .../analytics/sessions/getWebsiteSessions.ts | 4 ++ 13 files changed, 122 insertions(+), 46 deletions(-) create mode 100644 src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx create mode 100644 src/components/input/DateFilter.module.css diff --git a/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx index f2bf7cb9d2..49b31656d3 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx @@ -5,7 +5,6 @@ import { ReactNode } from 'react'; export default function EventsDataTable({ websiteId, - children, }: { websiteId?: string; teamId?: string; @@ -13,12 +12,8 @@ export default function EventsDataTable({ }) { const queryResult = useWebsiteEvents(websiteId); - if (queryResult?.result?.data?.length === 0) { - return children; - } - return ( - + {({ data }) => } ); diff --git a/src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx b/src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx new file mode 100644 index 0000000000..f77c6c04de --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx @@ -0,0 +1,42 @@ +import WebsiteDateFilter from 'components/input/WebsiteDateFilter'; +import { Flexbox } from 'react-basics'; +import MetricsBar from 'components/metrics/MetricsBar'; +import MetricCard from 'components/metrics/MetricCard'; +import { useMessages } from 'components/hooks'; +import useWebsiteStats from 'components/hooks/queries/useWebsiteStats'; +import { formatLongNumber } from 'lib/format'; + +export function EventsMetricsBar({ websiteId }: { websiteId: string }) { + const { formatMessage, labels } = useMessages(); + const { data, isLoading, isFetched, error } = useWebsiteStats(websiteId); + + return ( + + + + + + + + + + ); +} + +export default EventsMetricsBar; diff --git a/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx b/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx index 8f793d81ed..e35442d065 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx @@ -1,12 +1,31 @@ 'use client'; import WebsiteHeader from '../WebsiteHeader'; import EventsDataTable from './EventsDataTable'; +import EventsMetricsBar from './EventsMetricsBar'; +import EventsChart from 'components/metrics/EventsChart'; +import { GridRow } from 'components/layout/Grid'; +import MetricsTable from 'components/metrics/MetricsTable'; +import { useMessages } from 'components/hooks'; export default function EventsPage({ websiteId }) { + const { formatMessage, labels } = useMessages(); + return ( <> - + + + + + + + + ); } diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsMetricsBar.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsMetricsBar.tsx index 6d3c357957..9133ca71c6 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsMetricsBar.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsMetricsBar.tsx @@ -1,5 +1,6 @@ import WebsiteDateFilter from 'components/input/WebsiteDateFilter'; -import { Flexbox, Loading } from 'react-basics'; +import { Flexbox } from 'react-basics'; +import MetricsBar from 'components/metrics/MetricsBar'; import MetricCard from 'components/metrics/MetricCard'; import { useMessages } from 'components/hooks'; import useWebsiteStats from 'components/hooks/queries/useWebsiteStats'; @@ -7,33 +8,32 @@ import { formatLongNumber } from 'lib/format'; export function SessionsMetricsBar({ websiteId }: { websiteId: string }) { const { formatMessage, labels } = useMessages(); - const { data, isLoading } = useWebsiteStats(websiteId); + const { data, isLoading, isFetched, error } = useWebsiteStats(websiteId); return ( - - - {isLoading && } - {!isLoading && data && ( - <> - - - - - - )} - + + + + + + + ); diff --git a/src/components/common/DataTable.tsx b/src/components/common/DataTable.tsx index 0f011c3331..775e123c8c 100644 --- a/src/components/common/DataTable.tsx +++ b/src/components/common/DataTable.tsx @@ -34,7 +34,7 @@ export function DataTable({ const { page, pageSize, count, data } = result || {}; const { query } = params || {}; const hasData = Boolean(!isLoading && data?.length); - const noResults = Boolean(!isLoading && query && !hasData); + const noResults = Boolean(query && !hasData); const { router, renderUrl } = useNavigation(); const handleSearch = (query: string) => { diff --git a/src/components/hooks/queries/useWebsiteSessions.ts b/src/components/hooks/queries/useWebsiteSessions.ts index e8a1ce2f82..ce65512c2f 100644 --- a/src/components/hooks/queries/useWebsiteSessions.ts +++ b/src/components/hooks/queries/useWebsiteSessions.ts @@ -15,6 +15,7 @@ export function useWebsiteSessions(websiteId: string, params?: { [key: string]: ...data, ...params, ...filters, + pageSize: 20, }); }, }); diff --git a/src/components/input/DateFilter.module.css b/src/components/input/DateFilter.module.css new file mode 100644 index 0000000000..bd9ec1a8c4 --- /dev/null +++ b/src/components/input/DateFilter.module.css @@ -0,0 +1,3 @@ +.dropdown span { + white-space: nowrap; +} diff --git a/src/components/input/DateFilter.tsx b/src/components/input/DateFilter.tsx index 6e7c099a55..e486551d65 100644 --- a/src/components/input/DateFilter.tsx +++ b/src/components/input/DateFilter.tsx @@ -5,6 +5,8 @@ import DatePickerForm from 'components/metrics/DatePickerForm'; import { useLocale, useMessages } from 'components/hooks'; import Icons from 'components/icons'; import { formatDate, parseDateValue } from 'lib/date'; +import styles from './DateFilter.module.css'; +import classNames from 'classnames'; export interface DateFilterProps { value: string; @@ -123,7 +125,7 @@ export function DateFilter({ return ( <> 0)` : ''} order by created_at desc `, - params, + { ...params, query }, pageParams, ); } diff --git a/src/queries/analytics/getWebsiteStats.ts b/src/queries/analytics/getWebsiteStats.ts index 7f7f4f19e2..a33011ff5c 100644 --- a/src/queries/analytics/getWebsiteStats.ts +++ b/src/queries/analytics/getWebsiteStats.ts @@ -1,5 +1,4 @@ import clickhouse from 'lib/clickhouse'; -import { EVENT_TYPE } from 'lib/constants'; import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; import prisma from 'lib/prisma'; import { QueryFilters } from 'lib/types'; @@ -25,7 +24,6 @@ async function relationalQuery( const { getTimestampDiffSQL, parseFilters, rawQuery } = prisma; const { filterQuery, joinSession, params } = await parseFilters(websiteId, { ...filters, - eventType: EVENT_TYPE.pageView, }); return rawQuery( @@ -35,12 +33,15 @@ async function relationalQuery( count(distinct t.session_id) as "visitors", count(distinct t.visit_id) as "visits", count(distinct t.country) as "countries", + count(t.event_name) as "countries", + sum(case when t.event_type = 2 then 1 else 0 end) as "events", sum(case when t.c = 1 then 1 else 0 end) as "bounces", sum(${getTimestampDiffSQL('t.min_time', 't.max_time')}) as "totaltime", from ( select website_event.session_id, website_event.visit_id, + website_event.event_type, session.country, count(*) as "c", min(website_event.created_at) as "min_time", @@ -49,9 +50,8 @@ async function relationalQuery( ${joinSession} where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} - and event_type = {{eventType}} ${filterQuery} - group by 1, 2, 3 + group by 1, 2, 3, 4 ) as t `, params, @@ -67,7 +67,6 @@ async function clickhouseQuery( const { rawQuery, parseFilters } = clickhouse; const { filterQuery, params } = await parseFilters(websiteId, { ...filters, - eventType: EVENT_TYPE.pageView, }); let sql = ''; @@ -79,12 +78,14 @@ async function clickhouseQuery( uniq(t.session_id) as "visitors", uniq(t.visit_id) as "visits", uniq(t.country) as "countries", - sum(if(t.c = 1, 1, 0)) as "bounces", + sumIf(1, t.event_type = 2) as "events", + sumIf(1, t.c = 1) as "bounces", sum(max_time-min_time) as "totaltime" from ( select session_id, visit_id, + event_type, country, count(*) c, min(created_at) min_time, @@ -92,9 +93,8 @@ async function clickhouseQuery( from website_event where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type = {eventType:UInt32} ${filterQuery} - group by session_id, visit_id, country + group by session_id, visit_id, event_type, country ) as t; `; } else { @@ -104,12 +104,14 @@ async function clickhouseQuery( uniq(session_id) as "visitors", uniq(visit_id) as "visits", uniq(country) as "countries", + sumIf(1, t.event_type = 2) as "events", sumIf(1, t.c = 1) as "bounces", sum(max_time-min_time) as "totaltime" from ( select session_id, visit_id, + event_type, country, sum(views) c, min(min_time) min_time, @@ -117,9 +119,8 @@ async function clickhouseQuery( from umami.website_event_stats_hourly "website_event" where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type = {eventType:UInt32} ${filterQuery} - group by session_id, visit_id, country + group by session_id, visit_id, event_type, country ) as t; `; } @@ -133,6 +134,7 @@ async function clickhouseQuery( bounces: Number(a.bounces), totaltime: Number(a.totaltime), countries: Number(a.countries), + events: Number(a.events), }; }); }); diff --git a/src/queries/analytics/sessions/getWebsiteSessions.ts b/src/queries/analytics/sessions/getWebsiteSessions.ts index 94d2827ee1..26852616fc 100644 --- a/src/queries/analytics/sessions/getWebsiteSessions.ts +++ b/src/queries/analytics/sessions/getWebsiteSessions.ts @@ -14,10 +14,12 @@ export async function getWebsiteSessions( async function relationalQuery(websiteId: string, filters: QueryFilters, pageParams: PageParams) { const { pagedQuery } = prisma; + const { query } = pageParams; const where = { ...filters, id: websiteId, + ...prisma.getSearchParameters(query, [{ eventName: 'contains' }, { urlPath: 'contains' }]), }; return pagedQuery('session', { where }, pageParams); @@ -26,6 +28,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters, pagePar async function clickhouseQuery(websiteId: string, filters: QueryFilters, pageParams?: PageParams) { const { pagedQuery, parseFilters } = clickhouse; const { params, dateQuery, filterQuery } = await parseFilters(websiteId, filters); + const { query } = pageParams; return pagedQuery( ` @@ -49,6 +52,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar where website_id = {websiteId:UUID} ${dateQuery} ${filterQuery} + ${query ? `and (positionCaseInsensitive(event_name, {query:String}) > 0)` : ''} group by session_id, website_id, hostname, browser, os, device, screen, language, country, subdivision1, city order by lastAt desc `, From 60d53382cac60afeb8930028eee88f7346e3e1b7 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Thu, 8 Aug 2024 09:20:46 -0700 Subject: [PATCH 071/132] remove revenue report --- src/app/(main)/reports/create/ReportTemplates.tsx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/app/(main)/reports/create/ReportTemplates.tsx b/src/app/(main)/reports/create/ReportTemplates.tsx index dbff9f1031..fcfa6f6a92 100644 --- a/src/app/(main)/reports/create/ReportTemplates.tsx +++ b/src/app/(main)/reports/create/ReportTemplates.tsx @@ -4,7 +4,6 @@ import Magnet from 'assets/magnet.svg'; import Path from 'assets/path.svg'; import Tag from 'assets/tag.svg'; import Target from 'assets/target.svg'; -import Money from 'assets/money.svg'; import { useMessages, useTeamUrl } from 'components/hooks'; import PageHeader from 'components/layout/PageHeader'; import Link from 'next/link'; @@ -52,12 +51,12 @@ export function ReportTemplates({ showHeader = true }: { showHeader?: boolean }) url: renderTeamUrl('/reports/journey'), icon: , }, - { - title: formatMessage(labels.revenue), - description: formatMessage(labels.revenueDescription), - url: renderTeamUrl('/reports/revenue'), - icon: , - }, + // { + // title: formatMessage(labels.revenue), + // description: formatMessage(labels.revenueDescription), + // url: renderTeamUrl('/reports/revenue'), + // icon: , + // }, ]; return ( From 166b704166bb13d7656bf1f458d90156befcb956 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Thu, 8 Aug 2024 10:30:45 -0700 Subject: [PATCH 072/132] fix transfer website button disable logic --- .../websites/[websiteId]/WebsiteData.tsx | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/app/(main)/settings/websites/[websiteId]/WebsiteData.tsx b/src/app/(main)/settings/websites/[websiteId]/WebsiteData.tsx index 017a91f940..bc6a316969 100644 --- a/src/app/(main)/settings/websites/[websiteId]/WebsiteData.tsx +++ b/src/app/(main)/settings/websites/[websiteId]/WebsiteData.tsx @@ -13,11 +13,18 @@ export function WebsiteData({ websiteId, onSave }: { websiteId: string; onSave?: const { teamId, renderTeamUrl } = useTeamUrl(); const router = useRouter(); const { result } = useTeams(user.id); - const hasTeams = result?.data?.length > 0; - const isTeamOwner = - (!teamId && hasTeams) || - (hasTeams && - result?.data + const canTransferWebsite = + ( + !teamId && + result.data.filter(({ teamUser }) => + teamUser.find( + ({ role, userId }) => + [ROLES.teamOwner, ROLES.teamManager].includes(role) && userId === user.id, + ), + ) + ).length > 0 || + (teamId && + !!result?.data ?.find(({ id }) => id === teamId) ?.teamUser.find(({ role, userId }) => role === ROLES.teamOwner && userId === user.id)); @@ -37,8 +44,8 @@ export function WebsiteData({ websiteId, onSave }: { websiteId: string; onSave?: label={formatMessage(labels.transferWebsite)} description={formatMessage(messages.transferWebsite)} > - - From f135e4ffbb81f058bf957ec33aadb96f0bf6f028 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Thu, 8 Aug 2024 12:00:38 -0700 Subject: [PATCH 073/132] Moved events components to events page. Updated map data loading. --- .../websites/[websiteId]/WebsiteHeader.tsx | 3 +- .../websites/[websiteId]/WebsiteTableView.tsx | 19 ++++++---- src/assets/lightning.svg | 1 + .../hooks/queries/useWebsiteMetrics.ts | 2 +- src/components/metrics/CountriesTable.tsx | 12 +----- src/components/metrics/WorldMap.tsx | 37 ++++++++++++++----- .../api/websites/[websiteId]/events/index.ts | 7 +++- .../websites/[websiteId]/sessions/index.ts | 6 +-- 8 files changed, 52 insertions(+), 35 deletions(-) create mode 100644 src/assets/lightning.svg diff --git a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx index ef0d2b9063..1e14c4c5fe 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx @@ -7,6 +7,7 @@ import Link from 'next/link'; import { usePathname } from 'next/navigation'; import { ReactNode } from 'react'; import { Button, Icon, Text } from 'react-basics'; +import Lightning from 'assets/lightning.svg'; import styles from './WebsiteHeader.module.css'; export function WebsiteHeader({ @@ -52,7 +53,7 @@ export function WebsiteHeader({ }, { label: formatMessage(labels.events), - icon: , + icon: , path: '/events', }, ]; diff --git a/src/app/(main)/websites/[websiteId]/WebsiteTableView.tsx b/src/app/(main)/websites/[websiteId]/WebsiteTableView.tsx index e530f2bae7..2782cac62d 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteTableView.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteTableView.tsx @@ -1,4 +1,3 @@ -import { useState } from 'react'; import { Grid, GridRow } from 'components/layout/Grid'; import PagesTable from 'components/metrics/PagesTable'; import ReferrersTable from 'components/metrics/ReferrersTable'; @@ -9,13 +8,15 @@ import WorldMap from 'components/metrics/WorldMap'; import CountriesTable from 'components/metrics/CountriesTable'; import EventsTable from 'components/metrics/EventsTable'; import EventsChart from 'components/metrics/EventsChart'; +import { usePathname } from 'next/navigation'; export default function WebsiteTableView({ websiteId }: { websiteId: string }) { - const [countryData, setCountryData] = useState(); + const pathname = usePathname(); const tableProps = { websiteId, limit: 10, }; + const isSharePage = pathname.includes('/share/'); return ( @@ -29,13 +30,15 @@ export default function WebsiteTableView({ websiteId }: { websiteId: string }) { - - - - - - + + + {isSharePage && ( + + + + + )} ); } diff --git a/src/assets/lightning.svg b/src/assets/lightning.svg new file mode 100644 index 0000000000..14cb95d9c0 --- /dev/null +++ b/src/assets/lightning.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/hooks/queries/useWebsiteMetrics.ts b/src/components/hooks/queries/useWebsiteMetrics.ts index 088b31ac45..184fd4d44c 100644 --- a/src/components/hooks/queries/useWebsiteMetrics.ts +++ b/src/components/hooks/queries/useWebsiteMetrics.ts @@ -4,7 +4,7 @@ import { useFilterParams } from '../useFilterParams'; export function useWebsiteMetrics( websiteId: string, - queryParams: { type: string; limit: number; search: string; startAt?: number; endAt?: number }, + queryParams: { type: string; limit?: number; search?: string; startAt?: number; endAt?: number }, options?: Omit void }, 'queryKey' | 'queryFn'>, ) { const { get, useQuery } = useApi(); diff --git a/src/components/metrics/CountriesTable.tsx b/src/components/metrics/CountriesTable.tsx index 87a7838e2d..592ade8a0d 100644 --- a/src/components/metrics/CountriesTable.tsx +++ b/src/components/metrics/CountriesTable.tsx @@ -4,21 +4,12 @@ import { useLocale, useMessages, useFormat } from 'components/hooks'; import MetricsTable, { MetricsTableProps } from './MetricsTable'; import TypeIcon from 'components/common/TypeIcon'; -export function CountriesTable({ - onDataLoad, - ...props -}: { - onDataLoad: (data: any) => void; -} & MetricsTableProps) { +export function CountriesTable({ ...props }: MetricsTableProps) { const { locale } = useLocale(); const { countryNames } = useCountryNames(locale); const { formatMessage, labels } = useMessages(); const { formatCountry } = useFormat(); - const handleDataLoad = (data: any) => { - onDataLoad?.(data); - }; - const renderLink = ({ x: code }) => { return ( ); } diff --git a/src/components/metrics/WorldMap.tsx b/src/components/metrics/WorldMap.tsx index 54483270c6..5cebd428d3 100644 --- a/src/components/metrics/WorldMap.tsx +++ b/src/components/metrics/WorldMap.tsx @@ -4,7 +4,7 @@ import classNames from 'classnames'; import { colord } from 'colord'; import HoverTooltip from 'components/common/HoverTooltip'; import { ISO_COUNTRIES, MAP_FILE } from 'lib/constants'; -import { useTheme } from 'components/hooks'; +import { useDateRange, useTheme, useWebsiteMetrics } from 'components/hooks'; import { useCountryNames } from 'components/hooks'; import { useLocale } from 'components/hooks'; import { useMessages } from 'components/hooks'; @@ -12,16 +12,35 @@ import { formatLongNumber } from 'lib/format'; import { percentFilter } from 'lib/filters'; import styles from './WorldMap.module.css'; -export function WorldMap({ data = [], className }: { data?: any[]; className?: string }) { +export function WorldMap({ + websiteId, + data, + className, +}: { + websiteId?: string; + data?: any[]; + className?: string; +}) { const [tooltip, setTooltipPopup] = useState(); const { theme, colors } = useTheme(); const { locale } = useLocale(); const { formatMessage, labels } = useMessages(); const { countryNames } = useCountryNames(locale); const visitorsLabel = formatMessage(labels.visitors).toLocaleLowerCase(locale); - const metrics = useMemo(() => (data ? percentFilter(data) : []), [data]); + const { + dateRange: { startDate, endDate }, + } = useDateRange(websiteId); + const { data: mapData } = useWebsiteMetrics(websiteId, { + type: 'country', + startAt: +startDate, + endAt: +endDate, + }); + const metrics = useMemo( + () => (data || mapData ? percentFilter((data || mapData) as any[]) : []), + [data, mapData], + ); - function getFillColor(code: string) { + const getFillColor = (code: string) => { if (code === 'AQ') return; const country = metrics?.find(({ x }) => x === code); @@ -32,19 +51,19 @@ export function WorldMap({ data = [], className }: { data?: any[]; className?: s return colord(colors.map.baseColor) [theme === 'light' ? 'lighten' : 'darken'](0.4 * (1.0 - country.z / 100)) .toHex(); - } + }; - function getOpacity(code) { + const getOpacity = (code: string) => { return code === 'AQ' ? 0 : 1; - } + }; - function handleHover(code) { + const handleHover = (code: string) => { if (code === 'AQ') return; const country = metrics?.find(({ x }) => x === code); setTooltipPopup( `${countryNames[code]}: ${formatLongNumber(country?.y || 0)} ${visitorsLabel}` as any, ); - } + }; return (
Date: Thu, 8 Aug 2024 16:22:24 -0700 Subject: [PATCH 074/132] Fixed components build. --- src/components/hooks/queries/useRealtime.ts | 2 +- .../hooks/queries/useWebsiteEvents.ts | 2 +- src/index.ts | 24 +++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/components/hooks/queries/useRealtime.ts b/src/components/hooks/queries/useRealtime.ts index d5186ee0da..9c0c54f6be 100644 --- a/src/components/hooks/queries/useRealtime.ts +++ b/src/components/hooks/queries/useRealtime.ts @@ -1,7 +1,7 @@ import { RealtimeData } from 'lib/types'; import { useApi } from './useApi'; import { REALTIME_INTERVAL } from 'lib/constants'; -import { useTimezone } from 'components/hooks'; +import { useTimezone } from '../useTimezone'; export function useRealtime(websiteId: string) { const { get, useQuery } = useApi(); diff --git a/src/components/hooks/queries/useWebsiteEvents.ts b/src/components/hooks/queries/useWebsiteEvents.ts index 12febcc814..5d9bd19660 100644 --- a/src/components/hooks/queries/useWebsiteEvents.ts +++ b/src/components/hooks/queries/useWebsiteEvents.ts @@ -1,7 +1,7 @@ import useApi from './useApi'; import { UseQueryOptions } from '@tanstack/react-query'; import { useFilterParams } from '../useFilterParams'; -import { usePagedQuery } from 'components/hooks'; +import { usePagedQuery } from './usePagedQuery'; export function useWebsiteEvents( websiteId: string, diff --git a/src/index.ts b/src/index.ts index 465de67ee7..553a44b546 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,19 +1,19 @@ export * from 'components/hooks'; -export * from 'app/(main)/settings/teams/[teamId]/members/TeamMemberEditButton'; -export * from 'app/(main)/settings/teams/[teamId]/members/TeamMemberEditForm'; -export * from 'app/(main)/settings/teams/[teamId]/members/TeamMemberRemoveButton'; -export * from 'app/(main)/settings/teams/[teamId]/members/TeamMembersDataTable'; -export * from 'app/(main)/settings/teams/[teamId]/members/TeamMembersTable'; +export * from 'app/(main)/teams/[teamId]/settings/members/TeamMemberEditButton'; +export * from 'app/(main)/teams/[teamId]/settings/members/TeamMemberEditForm'; +export * from 'app/(main)/teams/[teamId]/settings/members/TeamMemberRemoveButton'; +export * from 'app/(main)/teams/[teamId]/settings/members/TeamMembersDataTable'; +export * from 'app/(main)/teams/[teamId]/settings/members/TeamMembersTable'; -export * from 'app/(main)/settings/teams/[teamId]/team/TeamDeleteForm'; -export * from 'app/(main)/settings/teams/[teamId]/team/TeamDetails'; -export * from 'app/(main)/settings/teams/[teamId]/team/TeamEditForm'; -export * from 'app/(main)/settings/teams/[teamId]/team/TeamManage'; +export * from 'app/(main)/teams/[teamId]/settings/team/TeamDeleteForm'; +export * from 'app/(main)/teams/[teamId]/settings/team/TeamDetails'; +export * from 'app/(main)/teams/[teamId]/settings/team/TeamEditForm'; +export * from 'app/(main)/teams/[teamId]/settings/team/TeamManage'; -export * from 'app/(main)/settings/teams/[teamId]/websites/TeamWebsiteRemoveButton'; -export * from 'app/(main)/settings/teams/[teamId]/websites/TeamWebsitesDataTable'; -export * from 'app/(main)/settings/teams/[teamId]/websites/TeamWebsitesTable'; +export * from 'app/(main)/teams/[teamId]/settings/websites/TeamWebsiteRemoveButton'; +export * from 'app/(main)/teams/[teamId]/settings/websites/TeamWebsitesDataTable'; +export * from 'app/(main)/teams/[teamId]/settings/websites/TeamWebsitesTable'; export * from 'app/(main)/settings/teams/TeamAddForm'; export * from 'app/(main)/settings/teams/TeamJoinForm'; From 8ee37d124656f16ac94835e7151d01c5e2566540 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 9 Aug 2024 01:09:54 -0700 Subject: [PATCH 075/132] New event data screen. --- package.json | 4 +- public/intl/messages/am-ET.json | 74 +++++++++++- public/intl/messages/ar-SA.json | 74 +++++++++++- public/intl/messages/be-BY.json | 74 +++++++++++- public/intl/messages/bg-BG.json | 74 +++++++++++- public/intl/messages/bn-BD.json | 74 +++++++++++- public/intl/messages/bs-BA.json | 74 +++++++++++- public/intl/messages/ca-ES.json | 74 +++++++++++- public/intl/messages/cs-CZ.json | 74 +++++++++++- public/intl/messages/da-DK.json | 74 +++++++++++- public/intl/messages/de-CH.json | 74 +++++++++++- public/intl/messages/de-DE.json | 74 +++++++++++- public/intl/messages/el-GR.json | 74 +++++++++++- public/intl/messages/en-GB.json | 74 +++++++++++- public/intl/messages/en-US.json | 80 ++++++++++++- public/intl/messages/es-ES.json | 74 +++++++++++- public/intl/messages/es-MX.json | 2 +- public/intl/messages/fa-IR.json | 74 +++++++++++- public/intl/messages/fi-FI.json | 74 +++++++++++- public/intl/messages/fo-FO.json | 74 +++++++++++- public/intl/messages/fr-FR.json | 74 +++++++++++- public/intl/messages/ga-ES.json | 74 +++++++++++- public/intl/messages/he-IL.json | 74 +++++++++++- public/intl/messages/hi-IN.json | 74 +++++++++++- public/intl/messages/hr-HR.json | 74 +++++++++++- public/intl/messages/hu-HU.json | 74 +++++++++++- public/intl/messages/id-ID.json | 74 +++++++++++- public/intl/messages/it-IT.json | 74 +++++++++++- public/intl/messages/ja-JP.json | 74 +++++++++++- public/intl/messages/km-KH.json | 74 +++++++++++- public/intl/messages/ko-KR.json | 74 +++++++++++- public/intl/messages/lt-LT.json | 74 +++++++++++- public/intl/messages/mn-MN.json | 74 +++++++++++- public/intl/messages/ms-MY.json | 74 +++++++++++- public/intl/messages/my-MM.json | 74 +++++++++++- public/intl/messages/nb-NO.json | 74 +++++++++++- public/intl/messages/nl-NL.json | 74 +++++++++++- public/intl/messages/pl-PL.json | 74 +++++++++++- public/intl/messages/pt-BR.json | 74 +++++++++++- public/intl/messages/pt-PT.json | 74 +++++++++++- public/intl/messages/ro-RO.json | 74 +++++++++++- public/intl/messages/ru-RU.json | 74 +++++++++++- public/intl/messages/si-LK.json | 74 +++++++++++- public/intl/messages/sk-SK.json | 74 +++++++++++- public/intl/messages/sl-SI.json | 74 +++++++++++- public/intl/messages/sv-SE.json | 74 +++++++++++- public/intl/messages/ta-IN.json | 74 +++++++++++- public/intl/messages/th-TH.json | 74 +++++++++++- public/intl/messages/tr-TR.json | 74 +++++++++++- public/intl/messages/uk-UA.json | 110 +++++++++++++++++- public/intl/messages/ur-PK.json | 74 +++++++++++- public/intl/messages/vi-VN.json | 74 +++++++++++- public/intl/messages/zh-CN.json | 74 +++++++++++- public/intl/messages/zh-TW.json | 74 +++++++++++- .../websites/[websiteId]/WebsiteHeader.tsx | 20 ++-- .../events/EventDataMetricsBar.module.css | 26 ----- .../events/EventDataMetricsBar.tsx | 37 ------ .../events/EventDataValueTable.tsx | 30 ----- .../events/EventProperties.module.css | 14 +++ .../[websiteId]/events/EventProperties.tsx | 52 +++++++++ .../[websiteId]/events/EventsPage.tsx | 19 ++- .../events/WebsiteEventData.module.css | 7 -- .../[websiteId]/events/WebsiteEventData.tsx | 41 ------- .../[websiteId]/realtime/RealtimeLog.tsx | 4 +- src/app/layout.tsx | 1 - src/components/charts/PieChart.tsx | 4 +- src/components/common/LoadingPanel.module.css | 5 + src/components/common/LoadingPanel.tsx | 36 ++++++ src/components/hooks/index.ts | 3 + .../hooks/queries/useEventDataEvents.ts | 20 ++++ .../hooks/queries/useEventDataProperties.ts | 20 ++++ .../hooks/queries/useEventDataValues.ts | 21 ++++ src/components/messages.ts | 2 +- src/lang/am-ET.json | 14 ++- src/lang/ar-SA.json | 14 ++- src/lang/be-BY.json | 14 ++- src/lang/bg-BG.json | 14 ++- src/lang/bn-BD.json | 14 ++- src/lang/bs-BA.json | 14 ++- src/lang/ca-ES.json | 14 ++- src/lang/cs-CZ.json | 14 ++- src/lang/da-DK.json | 14 ++- src/lang/de-CH.json | 14 ++- src/lang/de-DE.json | 14 ++- src/lang/el-GR.json | 14 ++- src/lang/en-GB.json | 14 ++- src/lang/en-US.json | 18 ++- src/lang/es-ES.json | 14 ++- src/lang/fa-IR.json | 14 ++- src/lang/fi-FI.json | 14 ++- src/lang/fo-FO.json | 14 ++- src/lang/fr-FR.json | 14 ++- src/lang/ga-ES.json | 14 ++- src/lang/he-IL.json | 14 ++- src/lang/hi-IN.json | 14 ++- src/lang/hr-HR.json | 14 ++- src/lang/hu-HU.json | 14 ++- src/lang/id-ID.json | 14 ++- src/lang/it-IT.json | 14 ++- src/lang/ja-JP.json | 14 ++- src/lang/km-KH.json | 14 ++- src/lang/ko-KR.json | 14 ++- src/lang/lt-LT.json | 14 ++- src/lang/mn-MN.json | 14 ++- src/lang/ms-MY.json | 14 ++- src/lang/my-MM.json | 14 ++- src/lang/nb-NO.json | 14 ++- src/lang/nl-NL.json | 14 ++- src/lang/pl-PL.json | 14 ++- src/lang/pt-BR.json | 14 ++- src/lang/pt-PT.json | 14 ++- src/lang/ro-RO.json | 14 ++- src/lang/ru-RU.json | 14 ++- src/lang/si-LK.json | 14 ++- src/lang/sk-SK.json | 14 ++- src/lang/sl-SI.json | 14 ++- src/lang/sv-SE.json | 14 ++- src/lang/ta-IN.json | 14 ++- src/lang/th-TH.json | 14 ++- src/lang/tr-TR.json | 14 ++- src/lang/uk-UA.json | 20 +++- src/lang/ur-PK.json | 14 ++- src/lang/vi-VN.json | 14 ++- src/lang/zh-CN.json | 14 ++- src/lang/zh-TW.json | 14 ++- src/lib/types.ts | 4 +- .../[websiteId]/event-data/events.ts} | 0 .../[websiteId]/event-data/properties.ts | 49 ++++++++ .../[websiteId]/event-data/values.ts} | 11 +- .../[websiteId]/events}/stats.ts | 0 .../sessions/[sessionId]/properties.ts} | 0 .../analytics/events/getEventDataEvents.ts | 20 ++-- ...ataFields.ts => getEventDataProperties.ts} | 37 +++--- .../analytics/events/getEventDataStats.ts | 10 +- .../analytics/events/getEventDataValues.ts | 70 +++++++++++ src/queries/index.ts | 3 +- yarn.lock | 39 +++++-- 137 files changed, 4918 insertions(+), 321 deletions(-) delete mode 100644 src/app/(main)/websites/[websiteId]/events/EventDataMetricsBar.module.css delete mode 100644 src/app/(main)/websites/[websiteId]/events/EventDataMetricsBar.tsx delete mode 100644 src/app/(main)/websites/[websiteId]/events/EventDataValueTable.tsx create mode 100644 src/app/(main)/websites/[websiteId]/events/EventProperties.module.css create mode 100644 src/app/(main)/websites/[websiteId]/events/EventProperties.tsx delete mode 100644 src/app/(main)/websites/[websiteId]/events/WebsiteEventData.module.css delete mode 100644 src/app/(main)/websites/[websiteId]/events/WebsiteEventData.tsx create mode 100644 src/components/common/LoadingPanel.module.css create mode 100644 src/components/common/LoadingPanel.tsx create mode 100644 src/components/hooks/queries/useEventDataEvents.ts create mode 100644 src/components/hooks/queries/useEventDataProperties.ts create mode 100644 src/components/hooks/queries/useEventDataValues.ts rename src/pages/api/{events/[eventId]/data.ts => websites/[websiteId]/event-data/events.ts} (100%) create mode 100644 src/pages/api/websites/[websiteId]/event-data/properties.ts rename src/pages/api/{event-data/fields.ts => websites/[websiteId]/event-data/values.ts} (80%) rename src/pages/api/{event-data => websites/[websiteId]/events}/stats.ts (100%) rename src/pages/api/{sessions/[sessionId]/data.ts => websites/[websiteId]/sessions/[sessionId]/properties.ts} (100%) rename src/queries/analytics/events/{getEventDataFields.ts => getEventDataProperties.ts} (61%) create mode 100644 src/queries/analytics/events/getEventDataValues.ts diff --git a/package.json b/package.json index 9ca56fe356..af873918c3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "umami", - "version": "2.12.1", + "version": "2.13.0", "description": "A simple, fast, privacy-focused alternative to Google Analytics.", "author": "Umami Software, Inc. ", "license": "MIT", @@ -106,7 +106,7 @@ "npm-run-all": "^4.1.5", "prisma": "5.17.0", "react": "^18.2.0", - "react-basics": "^0.124.0", + "react-basics": "^0.125.0", "react-beautiful-dnd": "^13.1.0", "react-dom": "^18.2.0", "react-error-boundary": "^4.0.4", diff --git a/public/intl/messages/am-ET.json b/public/intl/messages/am-ET.json index 65427a024d..6ab57eb203 100644 --- a/public/intl/messages/am-ET.json +++ b/public/intl/messages/am-ET.json @@ -11,7 +11,7 @@ "value": "Actions" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Password" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Profile" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Tracking code" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Unique visitors" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/ar-SA.json b/public/intl/messages/ar-SA.json index d6a1855e1f..b466054af5 100644 --- a/public/intl/messages/ar-SA.json +++ b/public/intl/messages/ar-SA.json @@ -11,7 +11,7 @@ "value": "الإجراءات" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "سجل الأحداث" @@ -473,6 +473,12 @@ "value": "التصفيات" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "كلمة المرور" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "الملف الشخصي" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "قس مدى ثبات موقعك على الويب من خلال تتبع عدد مرات عودة المستخدمين." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "حدد موقع" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "كود التتبع" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "زائرون فريدون" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "المستخدم" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/be-BY.json b/public/intl/messages/be-BY.json index 5d84ef4e09..d5ca0a1e78 100644 --- a/public/intl/messages/be-BY.json +++ b/public/intl/messages/be-BY.json @@ -11,7 +11,7 @@ "value": "Дзеянні" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Журнал актыўнасці" @@ -473,6 +473,12 @@ "value": "Фільтры" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " месяцаў" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Пароль" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Профіль" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Ацаніць прыцягальнасць сайта, адсочваючы павяртанні карыстальнікаў." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Выбраць сайт" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Код адсочвання" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Унікальныя наведвальнікі" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "Карыстальнік" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/bg-BG.json b/public/intl/messages/bg-BG.json index e970ec52e5..d81c4c69fa 100644 --- a/public/intl/messages/bg-BG.json +++ b/public/intl/messages/bg-BG.json @@ -11,7 +11,7 @@ "value": "Действия" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Активностти" @@ -473,6 +473,12 @@ "value": "Филтри" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " месеца" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Парола" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Профил" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Измерете привързаността към вашия уебсайт, като проследявате колко често потребителите се връщат." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Избери уебсайт" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Код за проследяване" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Уникални посетители" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "Потребител" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/bn-BD.json b/public/intl/messages/bn-BD.json index 31e04cfa49..a50db62261 100644 --- a/public/intl/messages/bn-BD.json +++ b/public/intl/messages/bn-BD.json @@ -11,7 +11,7 @@ "value": "অ্যাকশনস" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "একটিভিটি দেখুন" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "পাসওয়ার্ড" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 1, @@ -895,6 +919,12 @@ "value": "প্রোফাইল" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "ট্র্যাকিং কোড" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "অনন্য ভিজিটর" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/bs-BA.json b/public/intl/messages/bs-BA.json index 3b5c10ff53..71d870e25c 100644 --- a/public/intl/messages/bs-BA.json +++ b/public/intl/messages/bs-BA.json @@ -11,7 +11,7 @@ "value": "Akcije" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Log aktivnosti" @@ -473,6 +473,12 @@ "value": "Filtri" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " mjeseci" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Šifra" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Profil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Izmjeri 'ljepljivost' svoje web stranice praćenjem koliko često set korisnici vraćaju." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Odaberi web stranicu" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Kod za praćenje" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Jedinstvenih posjetitelja" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "Korisnik" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/ca-ES.json b/public/intl/messages/ca-ES.json index dc7b27a87b..c112c5c700 100644 --- a/public/intl/messages/ca-ES.json +++ b/public/intl/messages/ca-ES.json @@ -11,7 +11,7 @@ "value": "Accions" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Registre d'activitat" @@ -473,6 +473,12 @@ "value": "Filtres" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " mesos" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Contrasenya" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Perfil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Mesuri la retenció del seu lloc web fent un seguiment de la freqüència amb què tornen els usuaris." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Seleccionar lloc web" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Codi de seguiment" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Visitants únics" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "Usuari" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/cs-CZ.json b/public/intl/messages/cs-CZ.json index eaef42457d..26ae71415c 100644 --- a/public/intl/messages/cs-CZ.json +++ b/public/intl/messages/cs-CZ.json @@ -11,7 +11,7 @@ "value": "Akce" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Heslo" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Profil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Sledovací kód" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Jedinečné návštěvy" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/da-DK.json b/public/intl/messages/da-DK.json index aac48bbc9b..87e5f8a95d 100644 --- a/public/intl/messages/da-DK.json +++ b/public/intl/messages/da-DK.json @@ -11,7 +11,7 @@ "value": "Handlinger" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Adgangskode" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Profil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Sporingskode" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Unikke besøgende" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/de-CH.json b/public/intl/messages/de-CH.json index 0d06a0e03e..7b46c539b0 100644 --- a/public/intl/messages/de-CH.json +++ b/public/intl/messages/de-CH.json @@ -11,7 +11,7 @@ "value": "Aktione" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Aktivitätsverlauf" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Passwort" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Profil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Websiite uuswähle" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Tracking Code" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Eidütigi Bsuecher" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "Benutzer" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/de-DE.json b/public/intl/messages/de-DE.json index 713238bc75..84bc60e136 100644 --- a/public/intl/messages/de-DE.json +++ b/public/intl/messages/de-DE.json @@ -11,7 +11,7 @@ "value": "Aktionen" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Aktivitätsverlauf" @@ -473,6 +473,12 @@ "value": "Filter" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " Monate" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Passwort" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Profil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Messen Sie die Verweildauer auf Ihrer Website, indem Sie verfolgen, wie oft die Nutzer zurückkehren." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Webseite auswählen" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Tracking Code" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Eindeutige Besucher" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "Benutzer" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/el-GR.json b/public/intl/messages/el-GR.json index af0f11be3e..29c3efba6b 100644 --- a/public/intl/messages/el-GR.json +++ b/public/intl/messages/el-GR.json @@ -11,7 +11,7 @@ "value": "Ενέργειες" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Κωδικός" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Προφίλ" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Κωδικός παρακολούθησης" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Μοναδικοί επισκέπτες" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/en-GB.json b/public/intl/messages/en-GB.json index 38a7857ccb..c8c38be149 100644 --- a/public/intl/messages/en-GB.json +++ b/public/intl/messages/en-GB.json @@ -11,7 +11,7 @@ "value": "Actions" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Password" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Profile" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Tracking code" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Unique visitors" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/en-US.json b/public/intl/messages/en-US.json index 52b968ddf1..76a68dba1b 100644 --- a/public/intl/messages/en-US.json +++ b/public/intl/messages/en-US.json @@ -11,10 +11,10 @@ "value": "Actions" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, - "value": "Activity log" + "value": "Activity" } ], "label.add": [ @@ -404,7 +404,7 @@ "label.entry": [ { "type": 0, - "value": "Entry URL" + "value": "Entry path" } ], "label.event": [ @@ -428,7 +428,7 @@ "label.exit": [ { "type": 0, - "value": "Exit URL" + "value": "Exit path" } ], "label.false": [ @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Password" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Profile" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Tracking code" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Unique visitors" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/es-ES.json b/public/intl/messages/es-ES.json index 825877e4fa..540c9bbd0e 100644 --- a/public/intl/messages/es-ES.json +++ b/public/intl/messages/es-ES.json @@ -11,7 +11,7 @@ "value": "Acciones" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Registro de actividad" @@ -473,6 +473,12 @@ "value": "Filtros" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " meses" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Contraseña" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Perfil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Medir la frecuencia con la que los usuarios vuelven a tu sitio web." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Seleccionar sitio web" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Código de rastreo" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Visitantes únicos" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "Usuario" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/es-MX.json b/public/intl/messages/es-MX.json index 8051cde5ee..9dc4b4c965 100644 --- a/public/intl/messages/es-MX.json +++ b/public/intl/messages/es-MX.json @@ -11,7 +11,7 @@ "value": "Acciones" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Registro de actividad" diff --git a/public/intl/messages/fa-IR.json b/public/intl/messages/fa-IR.json index d5408e6458..34b9a3633b 100644 --- a/public/intl/messages/fa-IR.json +++ b/public/intl/messages/fa-IR.json @@ -11,7 +11,7 @@ "value": "اقدامات" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "رمز" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "پروفایل" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "کد رهگیری" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "بازدیدکننده‌های یکتا" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/fi-FI.json b/public/intl/messages/fi-FI.json index 0af4dc6f4f..9a1f066f23 100644 --- a/public/intl/messages/fi-FI.json +++ b/public/intl/messages/fi-FI.json @@ -11,7 +11,7 @@ "value": "Toiminnat" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Salasana" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Profiili" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Seurantakoodi" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Yksittäiset kävijät" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/fo-FO.json b/public/intl/messages/fo-FO.json index c9df65e1e2..f6bdf4ca65 100644 --- a/public/intl/messages/fo-FO.json +++ b/public/intl/messages/fo-FO.json @@ -11,7 +11,7 @@ "value": "Gerðir" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Loyniorð" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Vangi" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Spori kota" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Einsýna vitjanir" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/fr-FR.json b/public/intl/messages/fr-FR.json index bc4c51bb3f..0bfe6e7a0e 100644 --- a/public/intl/messages/fr-FR.json +++ b/public/intl/messages/fr-FR.json @@ -11,7 +11,7 @@ "value": "Actions" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Journal d'activité" @@ -473,6 +473,12 @@ "value": "Filtres" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -635,6 +641,12 @@ "value": " derniers mois" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -849,6 +861,18 @@ "value": "Mot de passe" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -883,6 +907,12 @@ "value": "Profil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -997,6 +1027,24 @@ "value": "Mesure de l'attractivité du site en visualisant les taux de visiteurs qui reviennent." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1051,6 +1099,12 @@ "value": "Choisir un site" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1219,6 +1273,12 @@ "value": "Code de suivi" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1255,6 +1315,12 @@ "value": "Visiteurs uniques" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1291,6 +1357,12 @@ "value": "Utilisateur" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/ga-ES.json b/public/intl/messages/ga-ES.json index 7e182d73c7..6ce820e69d 100644 --- a/public/intl/messages/ga-ES.json +++ b/public/intl/messages/ga-ES.json @@ -11,7 +11,7 @@ "value": "Accións" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Contrasinal" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Perfil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1017,6 +1047,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1071,6 +1119,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1239,6 +1293,12 @@ "value": "Código de seguimento" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1275,6 +1335,12 @@ "value": "Visitas únicas" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1311,6 +1377,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/he-IL.json b/public/intl/messages/he-IL.json index ee8599f349..d3ada47cc7 100644 --- a/public/intl/messages/he-IL.json +++ b/public/intl/messages/he-IL.json @@ -11,7 +11,7 @@ "value": "פעולות" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -639,6 +645,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -853,6 +865,18 @@ "value": "סיסמה" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -887,6 +911,12 @@ "value": "פרופיל" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1001,6 +1031,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1055,6 +1103,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1223,6 +1277,12 @@ "value": "קוד מעקב" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1259,6 +1319,12 @@ "value": "מבקרים ייחודיים" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1295,6 +1361,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/hi-IN.json b/public/intl/messages/hi-IN.json index 5fdffe3755..fc133a9349 100644 --- a/public/intl/messages/hi-IN.json +++ b/public/intl/messages/hi-IN.json @@ -11,7 +11,7 @@ "value": "कार्य" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "पासवर्ड" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 1, @@ -895,6 +919,12 @@ "value": "प्रोफ़ाइल" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "ट्रैकिंग कोड" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "अद्वितीय आगंतुकों" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/hr-HR.json b/public/intl/messages/hr-HR.json index 87f37e73ad..4718f0c2f2 100644 --- a/public/intl/messages/hr-HR.json +++ b/public/intl/messages/hr-HR.json @@ -11,7 +11,7 @@ "value": "Actions" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Lozinka" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Profil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Kod za praćenje" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Unique visitors" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/hu-HU.json b/public/intl/messages/hu-HU.json index 2cc464e990..6c51c3dc33 100644 --- a/public/intl/messages/hu-HU.json +++ b/public/intl/messages/hu-HU.json @@ -11,7 +11,7 @@ "value": "Műveletek" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Jelszó" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Profil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Követési kód" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Egyedi látogatók" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/id-ID.json b/public/intl/messages/id-ID.json index 8123d03b3a..c6bb04a286 100644 --- a/public/intl/messages/id-ID.json +++ b/public/intl/messages/id-ID.json @@ -11,7 +11,7 @@ "value": "Aksi" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -639,6 +645,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -853,6 +865,18 @@ "value": "Kata sandi" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -887,6 +911,12 @@ "value": "Profil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1001,6 +1031,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1055,6 +1103,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1223,6 +1277,12 @@ "value": "Kode lacak" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1259,6 +1319,12 @@ "value": "Pengunjung unik" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1295,6 +1361,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/it-IT.json b/public/intl/messages/it-IT.json index d755b277a6..38e6113630 100644 --- a/public/intl/messages/it-IT.json +++ b/public/intl/messages/it-IT.json @@ -11,7 +11,7 @@ "value": "Azioni" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Password" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Profilo" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Codice di tracking" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Visitatori unici" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/ja-JP.json b/public/intl/messages/ja-JP.json index b2bc66a09a..f38a82d459 100644 --- a/public/intl/messages/ja-JP.json +++ b/public/intl/messages/ja-JP.json @@ -11,7 +11,7 @@ "value": "アクション" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "アクティビティログ" @@ -473,6 +473,12 @@ "value": "フィルター" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "パスワード" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "プロフィール" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "ユーザーの再訪問回数を記録して、Webサイトのリテンション率を計測します。" } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Webサイトを選択" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "トラッキングコード" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "ユニーク訪問者数" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "ユーザー" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/km-KH.json b/public/intl/messages/km-KH.json index ad273d2932..ca8a40a934 100644 --- a/public/intl/messages/km-KH.json +++ b/public/intl/messages/km-KH.json @@ -11,7 +11,7 @@ "value": "សកម្មភាព" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "កំណត់ហេតុ​សកម្មភាព" @@ -473,6 +473,12 @@ "value": "ចម្រោះ" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -635,6 +641,12 @@ "value": " ខែចុងក្រោយ" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -849,6 +861,18 @@ "value": "ពាក្យសម្ងាត់​" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -883,6 +907,12 @@ "value": "គណនី" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -997,6 +1027,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1051,6 +1099,12 @@ "value": "ជ្រើសរើសគេហទំព័រ" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1219,6 +1273,12 @@ "value": "លេខកូដតាមដាន" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1255,6 +1315,12 @@ "value": "អ្នកចូលមើលម្នាក់ៗ" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1291,6 +1357,12 @@ "value": "អ្នកប្រើប្រាស់" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/ko-KR.json b/public/intl/messages/ko-KR.json index deab1408bc..9f6520ea40 100644 --- a/public/intl/messages/ko-KR.json +++ b/public/intl/messages/ko-KR.json @@ -11,7 +11,7 @@ "value": "액션" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "활동 기록" @@ -473,6 +473,12 @@ "value": "필터" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " 개월" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "비밀번호" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -899,6 +923,12 @@ "value": "프로필" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1013,6 +1043,24 @@ "value": "사용자가 얼마나 자주 돌아오는지를 추적하여 웹사이트의 리텐션을 측정하십시오." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1067,6 +1115,12 @@ "value": "웹사이트 선택" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1235,6 +1289,12 @@ "value": "추적 코드" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1271,6 +1331,12 @@ "value": "순방문자(UV)" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1307,6 +1373,12 @@ "value": "사용자" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/lt-LT.json b/public/intl/messages/lt-LT.json index e6c6cd1857..d5eec3c81a 100644 --- a/public/intl/messages/lt-LT.json +++ b/public/intl/messages/lt-LT.json @@ -11,7 +11,7 @@ "value": "Veiksmai" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Veiklos žurnalas" @@ -473,6 +473,12 @@ "value": "Filtrai" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -752,6 +758,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -966,6 +978,18 @@ "value": "Slaptažodis" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -1000,6 +1024,12 @@ "value": "Profilis" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1114,6 +1144,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1168,6 +1216,12 @@ "value": "Pasirinkti svetainę" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1336,6 +1390,12 @@ "value": "Sekimo kodas" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1372,6 +1432,12 @@ "value": "Unikalūs lankytojai" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1408,6 +1474,12 @@ "value": "Vartotojas" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/mn-MN.json b/public/intl/messages/mn-MN.json index d9461929e1..f1a76b9b87 100644 --- a/public/intl/messages/mn-MN.json +++ b/public/intl/messages/mn-MN.json @@ -11,7 +11,7 @@ "value": "Үйлдлүүд" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Үйл ажиллагааны бүртгэл" @@ -473,6 +473,12 @@ "value": "Шүүлтүүр" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Нууц үг" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 1, @@ -895,6 +919,12 @@ "value": "Бүртгэл" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Хэрэглэгчид таны веб рүү дахин хандах буюу хэрэглэгчдээ хэр тогтоож буйг хэмжих." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Веб сонгох" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Мөрдөх код" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Зочин" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "Хэрэглэгч" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/ms-MY.json b/public/intl/messages/ms-MY.json index 8f47f3cd7a..6886b74c60 100644 --- a/public/intl/messages/ms-MY.json +++ b/public/intl/messages/ms-MY.json @@ -11,7 +11,7 @@ "value": "Aksi" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -639,6 +645,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -853,6 +865,18 @@ "value": "Kata laluan" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -887,6 +911,12 @@ "value": "Profil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1001,6 +1031,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1055,6 +1103,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1223,6 +1277,12 @@ "value": "Kod penjejakan" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1259,6 +1319,12 @@ "value": "Pelawat unik" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1295,6 +1361,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/my-MM.json b/public/intl/messages/my-MM.json index 9451a0febb..7fc610e4dd 100644 --- a/public/intl/messages/my-MM.json +++ b/public/intl/messages/my-MM.json @@ -11,7 +11,7 @@ "value": "လုပ်ဆောင်ချက်များ" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "လုပ်ဆောင်ချက်စာရင်း" @@ -473,6 +473,12 @@ "value": "Filter များ" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "စကားဝှက်" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 1, @@ -895,6 +919,12 @@ "value": "ပရိုဖိုင်း" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "ဝဘက်ဘ်ဆိုဒ်ရွေးပါ" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "ထရက်လုပ်သည့် ကုဒ်" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "ဝင်ရောက်သူ (ထပ်ခြင်းမရှိ)" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "အသုံးပြုသူ" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/nb-NO.json b/public/intl/messages/nb-NO.json index 3e73b45ee8..051c362c26 100644 --- a/public/intl/messages/nb-NO.json +++ b/public/intl/messages/nb-NO.json @@ -11,7 +11,7 @@ "value": "Handlinger" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Passord" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Profil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Sporingskode" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Unike besøkende" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/nl-NL.json b/public/intl/messages/nl-NL.json index a186de8625..d7ea5c9f27 100644 --- a/public/intl/messages/nl-NL.json +++ b/public/intl/messages/nl-NL.json @@ -11,7 +11,7 @@ "value": "Acties" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activiteiten logboek" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Wachtwoord" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Profiel" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Meet de retentie van je website door door bij te houden hoe vaak gebruikers terugkeren." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Website selecteren" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Volgcode" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Unieke bezoekers" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "Gebruiker" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/pl-PL.json b/public/intl/messages/pl-PL.json index 7157a4f26c..97782a412a 100644 --- a/public/intl/messages/pl-PL.json +++ b/public/intl/messages/pl-PL.json @@ -11,7 +11,7 @@ "value": "Działania" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Dziennik aktywności" @@ -473,6 +473,12 @@ "value": "Filtry" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " miesięcy" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Hasło" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Profil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Mierz przyciągającą siłę swojej strony internetowej, śledząc, jak często użytkownicy powracają." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Wybierz witrynę" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Kod śledzenia" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Unikalni odwiedzający" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "Użytkownik" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/pt-BR.json b/public/intl/messages/pt-BR.json index ae5b0de4ba..26985407af 100644 --- a/public/intl/messages/pt-BR.json +++ b/public/intl/messages/pt-BR.json @@ -11,7 +11,7 @@ "value": "Ações do usuário" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Registro de atividades" @@ -473,6 +473,12 @@ "value": "Filtros" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " meses" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Senha" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Perfil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Avalie a fidelidade dos seus usuários medindo a frequência com que eles retornam." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Selecionar site" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Código de rastreamento" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Visitantes únicos" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "Usuário" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/pt-PT.json b/public/intl/messages/pt-PT.json index 02704e9e0f..6efd9834bc 100644 --- a/public/intl/messages/pt-PT.json +++ b/public/intl/messages/pt-PT.json @@ -11,7 +11,7 @@ "value": "Ações" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Senha" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Perfil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Código de rastreamento" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Visitantes únicos" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/ro-RO.json b/public/intl/messages/ro-RO.json index 311199cf39..1e5ac13af9 100644 --- a/public/intl/messages/ro-RO.json +++ b/public/intl/messages/ro-RO.json @@ -11,7 +11,7 @@ "value": "Acțiuni" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Jurnal de activități" @@ -473,6 +473,12 @@ "value": "Filtre" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Parolă" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Profil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Măsoară atractivitatea site-ului tău prin urmărirea frecvenței cu care utilizatorii se întorc." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Selectează website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Cod de urmărire" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Vizitatori unici" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "Utilizator" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/ru-RU.json b/public/intl/messages/ru-RU.json index 30974dd64e..e8f7f5b4fc 100644 --- a/public/intl/messages/ru-RU.json +++ b/public/intl/messages/ru-RU.json @@ -11,7 +11,7 @@ "value": "Действия" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Журнал активности" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Пароль" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Профиль" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Выбрать сайт" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Код отслеживания" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Уникальные посетители" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "Пользователь" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/si-LK.json b/public/intl/messages/si-LK.json index e6b06c6ba7..e18561c9dd 100644 --- a/public/intl/messages/si-LK.json +++ b/public/intl/messages/si-LK.json @@ -11,7 +11,7 @@ "value": "Actions" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "මුරපදය" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "පැතිකඩ" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "ලුහුබැඳීමේ කේතය" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Unique visitors" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/sk-SK.json b/public/intl/messages/sk-SK.json index 738ead435d..0908ec4f6d 100644 --- a/public/intl/messages/sk-SK.json +++ b/public/intl/messages/sk-SK.json @@ -11,7 +11,7 @@ "value": "Akcie" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Heslo" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Profil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Sledovací kód" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Jedinečné návštevy" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/sl-SI.json b/public/intl/messages/sl-SI.json index 67f65ad407..ae11c067d0 100644 --- a/public/intl/messages/sl-SI.json +++ b/public/intl/messages/sl-SI.json @@ -11,7 +11,7 @@ "value": "Dejanja" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Dnevnik dejavnosti" @@ -473,6 +473,12 @@ "value": "Filtri" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Geslo" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Profil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Izberi spletno mesto" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Koda za sledenje" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Unikatni obiskovalci" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "Uporabnik" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/sv-SE.json b/public/intl/messages/sv-SE.json index c2b2c5e06f..830a7bd329 100644 --- a/public/intl/messages/sv-SE.json +++ b/public/intl/messages/sv-SE.json @@ -11,7 +11,7 @@ "value": "Händelser" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Aktivitetslogg" @@ -473,6 +473,12 @@ "value": "Filter" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Lösenord" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Profil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Mät din webbplats engagemang genom att följa hur ofta användare återvänder." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Välj webbplats" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "Spårningskod" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Unika besökare" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "Användare" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/ta-IN.json b/public/intl/messages/ta-IN.json index 13ab67980f..87a38137c4 100644 --- a/public/intl/messages/ta-IN.json +++ b/public/intl/messages/ta-IN.json @@ -11,7 +11,7 @@ "value": "செயல்கள்" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "கடவுச்சொல்" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 1, @@ -895,6 +919,12 @@ "value": "சுயவிவரம்" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "கண்காணிப்பு குறியீடு" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "தனிப்பட்ட பார்வையாளர்கள்" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/th-TH.json b/public/intl/messages/th-TH.json index 43158951dc..a8e68c99fe 100644 --- a/public/intl/messages/th-TH.json +++ b/public/intl/messages/th-TH.json @@ -11,7 +11,7 @@ "value": "การกระทำ" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -639,6 +645,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -853,6 +865,18 @@ "value": "รหัสผ่าน" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -887,6 +911,12 @@ "value": "โปรไฟล์" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1001,6 +1031,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1055,6 +1103,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1223,6 +1277,12 @@ "value": "โค้ดสำหรับใช้ติดตาม" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1259,6 +1319,12 @@ "value": "ผู้เข้าชม" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1295,6 +1361,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/tr-TR.json b/public/intl/messages/tr-TR.json index 1edfdbbc4e..9b23b29c37 100644 --- a/public/intl/messages/tr-TR.json +++ b/public/intl/messages/tr-TR.json @@ -11,7 +11,7 @@ "value": "Hareketler" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Aktivite Kaydı" @@ -473,6 +473,12 @@ "value": "Filtreler" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " ay" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "Parola" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "Profil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Kullanıcıların ne sıklıkla geri döndüğünü takip ederek web sitenizin kalıcılığını ölçün." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Web sitesi seç" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "İzleme kodu" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "Tekil kullanıcı" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "Kullanıcı" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/uk-UA.json b/public/intl/messages/uk-UA.json index 33579a3373..0f68c8052e 100644 --- a/public/intl/messages/uk-UA.json +++ b/public/intl/messages/uk-UA.json @@ -11,7 +11,7 @@ "value": "Дії" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Журнал" @@ -377,6 +377,24 @@ "value": "Редагувати" } ], + "label.edit-dashboard": [ + { + "type": 0, + "value": "Edit dashboard" + } + ], + "label.edit-member": [ + { + "type": 0, + "value": "Edit member" + } + ], + "label.enable-share-url": [ + { + "type": 0, + "value": "Enable share URL" + } + ], "label.end-step": [ { "type": 0, @@ -455,6 +473,12 @@ "value": "Фільтри" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -629,6 +653,12 @@ "value": " місяців" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -671,6 +701,12 @@ "value": "Керувати" } ], + "label.manager": [ + { + "type": 0, + "value": "Manager" + } + ], "label.max": [ { "type": 0, @@ -837,6 +873,18 @@ "value": "Пароль" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -871,6 +919,12 @@ "value": "Профіль" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -985,6 +1039,24 @@ "value": "Виміряйте липкість вашого сайту, відстежуючи, як часто користувачі повертаються на нього." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1039,6 +1111,12 @@ "value": "Вибрати сайт" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1099,6 +1177,12 @@ "value": "Ідентифікатор команди" } ], + "label.team-manager": [ + { + "type": 0, + "value": "Team manager" + } + ], "label.team-member": [ { "type": 0, @@ -1201,6 +1285,12 @@ "value": "Код для відслідковування" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1237,6 +1327,12 @@ "value": "Унікальні відвідувачі" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1273,6 +1369,12 @@ "value": "Користувач" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, @@ -1333,6 +1435,12 @@ "value": "Перегляди за одне відвідування" } ], + "label.visit-duration": [ + { + "type": 0, + "value": "Visit duration" + } + ], "label.visitors": [ { "type": 0, diff --git a/public/intl/messages/ur-PK.json b/public/intl/messages/ur-PK.json index 9d3c932b6c..2652fcfa2c 100644 --- a/public/intl/messages/ur-PK.json +++ b/public/intl/messages/ur-PK.json @@ -11,7 +11,7 @@ "value": "اعمال" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "پاس ورڈ" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -895,6 +919,12 @@ "value": "پروفائل" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1009,6 +1039,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1063,6 +1111,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1231,6 +1285,12 @@ "value": "ٹریکنگ کوڈ" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1267,6 +1327,12 @@ "value": "منفرد زائرین" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1303,6 +1369,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/vi-VN.json b/public/intl/messages/vi-VN.json index 424d30a691..68a301ae5c 100644 --- a/public/intl/messages/vi-VN.json +++ b/public/intl/messages/vi-VN.json @@ -11,7 +11,7 @@ "value": "Hành động" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "Activity log" @@ -473,6 +473,12 @@ "value": "Filters" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -639,6 +645,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -853,6 +865,18 @@ "value": "Mật khẩu" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -887,6 +911,12 @@ "value": "Hồ sơ" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1001,6 +1031,24 @@ "value": "Measure your website stickiness by tracking how often users return." } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1055,6 +1103,12 @@ "value": "Select website" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1223,6 +1277,12 @@ "value": "Mã theo dõi" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1259,6 +1319,12 @@ "value": "Khách truy cập một lần" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1295,6 +1361,12 @@ "value": "User" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/zh-CN.json b/public/intl/messages/zh-CN.json index bb77d83b98..b3e97b9b0f 100644 --- a/public/intl/messages/zh-CN.json +++ b/public/intl/messages/zh-CN.json @@ -11,7 +11,7 @@ "value": "用户行为" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "活动日志" @@ -473,6 +473,12 @@ "value": "筛选" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " 个月" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -865,6 +877,18 @@ "value": "密码" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -903,6 +927,12 @@ "value": "个人资料" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1017,6 +1047,24 @@ "value": "通过跟踪用户返回的频率来衡量网站的用户粘性。" } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1071,6 +1119,12 @@ "value": "选择网站" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1239,6 +1293,12 @@ "value": "跟踪代码" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1275,6 +1335,12 @@ "value": "独立访客" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1311,6 +1377,12 @@ "value": "用户" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/public/intl/messages/zh-TW.json b/public/intl/messages/zh-TW.json index 37c490d7c4..e03059f8d6 100644 --- a/public/intl/messages/zh-TW.json +++ b/public/intl/messages/zh-TW.json @@ -11,7 +11,7 @@ "value": "行動" } ], - "label.activity-log": [ + "label.activity": [ { "type": 0, "value": "活動日誌" @@ -473,6 +473,12 @@ "value": "篩選器" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -647,6 +653,12 @@ "value": " months" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -861,6 +873,18 @@ "value": "密碼" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -899,6 +923,12 @@ "value": "個人資料" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -1013,6 +1043,24 @@ "value": "透過追蹤使用者回訪的頻率來衡量您的網站黏著度。" } ], + "label.revenue": [ + { + "type": 0, + "value": "Revenue" + } + ], + "label.revenue-description": [ + { + "type": 0, + "value": "Look into your revenue across time." + } + ], + "label.revenue-property": [ + { + "type": 0, + "value": "Revenue Property" + } + ], "label.role": [ { "type": 0, @@ -1067,6 +1115,12 @@ "value": "選擇網站" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1235,6 +1289,12 @@ "value": "追蹤代碼" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, @@ -1271,6 +1331,12 @@ "value": "獨立訪客" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1307,6 +1373,12 @@ "value": "使用者" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, diff --git a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx index 1e14c4c5fe..3b8cddaeaa 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx @@ -36,16 +36,6 @@ export function WebsiteHeader({ icon: , path: '/compare', }, - { - label: formatMessage(labels.realtime), - icon: , - path: '/realtime', - }, - { - label: formatMessage(labels.reports), - icon: , - path: '/reports', - }, { label: formatMessage(labels.sessions), icon: , @@ -56,6 +46,16 @@ export function WebsiteHeader({ icon: , path: '/events', }, + { + label: formatMessage(labels.realtime), + icon: , + path: '/realtime', + }, + { + label: formatMessage(labels.reports), + icon: , + path: '/reports', + }, ]; return ( diff --git a/src/app/(main)/websites/[websiteId]/events/EventDataMetricsBar.module.css b/src/app/(main)/websites/[websiteId]/events/EventDataMetricsBar.module.css deleted file mode 100644 index 408396c37b..0000000000 --- a/src/app/(main)/websites/[websiteId]/events/EventDataMetricsBar.module.css +++ /dev/null @@ -1,26 +0,0 @@ -.container { - display: grid; - grid-template-columns: 1fr 1fr; - justify-content: space-between; - align-items: center; - padding: 10px 0; - min-height: 90px; - margin-bottom: 20px; - background: var(--base50); - z-index: var(--z-index-above); -} - -.actions { - display: flex; - flex-direction: row; - align-items: center; - justify-content: flex-end; - flex: 1; -} - -@media only screen and (max-width: 992px) { - .container { - grid-template-columns: 1fr; - grid-template-rows: 1fr 1fr; - } -} diff --git a/src/app/(main)/websites/[websiteId]/events/EventDataMetricsBar.tsx b/src/app/(main)/websites/[websiteId]/events/EventDataMetricsBar.tsx deleted file mode 100644 index 86417c96d3..0000000000 --- a/src/app/(main)/websites/[websiteId]/events/EventDataMetricsBar.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { useApi, useDateRange, useMessages } from 'components/hooks'; -import MetricCard from 'components/metrics/MetricCard'; -import WebsiteDateFilter from 'components/input/WebsiteDateFilter'; -import MetricsBar from 'components/metrics/MetricsBar'; -import styles from './EventDataMetricsBar.module.css'; - -export function EventDataMetricsBar({ websiteId }: { websiteId: string }) { - const { formatMessage, labels } = useMessages(); - const { get, useQuery } = useApi(); - const { dateRange } = useDateRange(websiteId); - const { startDate, endDate } = dateRange; - - const { data, error, isLoading, isFetched } = useQuery({ - queryKey: ['event-data:stats', { websiteId, startDate, endDate }], - queryFn: () => - get(`/event-data/stats`, { - websiteId, - startAt: +startDate, - endAt: +endDate, - }), - }); - - return ( -
- - - - - -
- -
-
- ); -} - -export default EventDataMetricsBar; diff --git a/src/app/(main)/websites/[websiteId]/events/EventDataValueTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventDataValueTable.tsx deleted file mode 100644 index cddeea23fd..0000000000 --- a/src/app/(main)/websites/[websiteId]/events/EventDataValueTable.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { GridTable, GridColumn } from 'react-basics'; -import { useMessages } from 'components/hooks'; -import PageHeader from 'components/layout/PageHeader'; -import Empty from 'components/common/Empty'; -import { DATA_TYPES } from 'lib/constants'; - -export function EventDataValueTable({ data = [], event }: { data: any[]; event: string }) { - const { formatMessage, labels } = useMessages(); - - return ( - <> - - {data.length <= 0 && } - {data.length > 0 && ( - - - - {row => DATA_TYPES[row.dataType]} - - - - {({ total }) => total.toLocaleString()} - - - )} - - ); -} - -export default EventDataValueTable; diff --git a/src/app/(main)/websites/[websiteId]/events/EventProperties.module.css b/src/app/(main)/websites/[websiteId]/events/EventProperties.module.css new file mode 100644 index 0000000000..b476ebd87a --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/events/EventProperties.module.css @@ -0,0 +1,14 @@ +.container { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(600px, 1fr)); + gap: 30px; +} + +.table { + align-self: start; +} + +.link:hover { + cursor: pointer; + color: var(--primary400); +} diff --git a/src/app/(main)/websites/[websiteId]/events/EventProperties.tsx b/src/app/(main)/websites/[websiteId]/events/EventProperties.tsx new file mode 100644 index 0000000000..d6e52c59ea --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/events/EventProperties.tsx @@ -0,0 +1,52 @@ +import { GridColumn, GridTable } from 'react-basics'; +import { useEventDataProperties, useEventDataValues, useMessages } from 'components/hooks'; +import { LoadingPanel } from 'components/common/LoadingPanel'; +import styles from './EventProperties.module.css'; +import PieChart from 'components/charts/PieChart'; +import { useState } from 'react'; +import { CHART_COLORS } from 'lib/constants'; + +export function EventProperties({ websiteId }: { websiteId: string }) { + const [propertyName, setPropertyName] = useState(''); + const { formatMessage, labels } = useMessages(); + const { data, isLoading, isFetched, error } = useEventDataProperties(websiteId); + const { data: values } = useEventDataValues(websiteId, propertyName); + const chartData = + propertyName && values + ? { + labels: values.map(({ value }) => value), + datasets: [ + { + data: values.map(({ total }) => total), + backgroundColor: CHART_COLORS, + borderWidth: 0, + }, + ], + } + : null; + + return ( + +
+ + + {row => ( +
setPropertyName(row.propertyName)}> + {row.propertyName} +
+ )} +
+ +
+ {propertyName && ( +
+ {propertyName} + +
+ )} +
+
+ ); +} + +export default EventProperties; diff --git a/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx b/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx index e35442d065..7dfc0394c6 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx @@ -6,8 +6,12 @@ import EventsChart from 'components/metrics/EventsChart'; import { GridRow } from 'components/layout/Grid'; import MetricsTable from 'components/metrics/MetricsTable'; import { useMessages } from 'components/hooks'; +import { Item, Tabs } from 'react-basics'; +import { useState } from 'react'; +import EventProperties from './EventProperties'; export default function EventsPage({ websiteId }) { + const [tab, setTab] = useState('activity'); const { formatMessage, labels } = useMessages(); return ( @@ -23,9 +27,18 @@ export default function EventsPage({ websiteId }) { metric={formatMessage(labels.actions)} /> - - - +
+ setTab(value)} + style={{ marginBottom: 30 }} + > + {formatMessage(labels.activity)} + {formatMessage(labels.properties)} + + {tab === 'activity' && } + {tab === 'properties' && } +
); } diff --git a/src/app/(main)/websites/[websiteId]/events/WebsiteEventData.module.css b/src/app/(main)/websites/[websiteId]/events/WebsiteEventData.module.css deleted file mode 100644 index 1d1782312d..0000000000 --- a/src/app/(main)/websites/[websiteId]/events/WebsiteEventData.module.css +++ /dev/null @@ -1,7 +0,0 @@ -.container a { - color: var(--font-color100); -} - -.container a:hover { - color: var(--primary400); -} diff --git a/src/app/(main)/websites/[websiteId]/events/WebsiteEventData.tsx b/src/app/(main)/websites/[websiteId]/events/WebsiteEventData.tsx deleted file mode 100644 index 296c8a66ba..0000000000 --- a/src/app/(main)/websites/[websiteId]/events/WebsiteEventData.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { Flexbox, Loading } from 'react-basics'; -import EventsTable from './EventsTable'; -import EventDataValueTable from './EventDataValueTable'; -import { EventDataMetricsBar } from './EventDataMetricsBar'; -import { useDateRange, useApi, useNavigation } from 'components/hooks'; -import styles from './WebsiteEventData.module.css'; - -function useData(websiteId: string, event: string) { - const { dateRange } = useDateRange(websiteId); - const { startDate, endDate } = dateRange; - const { get, useQuery } = useApi(); - const { data, error, isLoading } = useQuery({ - queryKey: ['event-data:events', { websiteId, startDate, endDate, event }], - queryFn: () => - get('/event-data/events', { - websiteId, - startAt: +startDate, - endAt: +endDate, - event, - }), - enabled: !!(websiteId && startDate && endDate), - }); - - return { data, error, isLoading }; -} - -export default function WebsiteEventData({ websiteId }) { - const { - query: { event }, - } = useNavigation(); - const { data, isLoading } = useData(websiteId, event); - - return ( - - - {!event && } - {isLoading && } - {event && data && } - - ); -} diff --git a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx index ad0314e7ad..b1836d4f1d 100644 --- a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx +++ b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx @@ -54,7 +54,7 @@ export function RealtimeLog({ data }: { data: RealtimeData }) { }, ]; - const getTime = ({ createdAt }) => format(new Date(createdAt), 'h:mm:ss'); + const getTime = ({ createdAt, firstAt }) => format(new Date(firstAt || createdAt), 'h:mm:ss'); const getColor = ({ id, sessionId }) => stringToColor(sessionId || id); @@ -181,7 +181,7 @@ export function RealtimeLog({ data }: { data: RealtimeData }) {
-
{formatMessage(labels.activityLog)}
+
{formatMessage(labels.activity)}
{logs?.length === 0 && } {logs?.length > 0 && ( diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 33ae9fa991..92285c63dd 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -21,7 +21,6 @@ export default function ({ children }) { - diff --git a/src/components/charts/PieChart.tsx b/src/components/charts/PieChart.tsx index 11ad125cfb..57d676caec 100644 --- a/src/components/charts/PieChart.tsx +++ b/src/components/charts/PieChart.tsx @@ -9,7 +9,7 @@ export interface PieChartProps extends ChartProps { export default function PieChart(props: PieChartProps) { const [tooltip, setTooltip] = useState(null); - const { type } = props; + const { type = 'pie' } = props; const handleTooltip = ({ tooltip }) => { const { labelColors, dataPoints } = tooltip; @@ -23,5 +23,5 @@ export default function PieChart(props: PieChartProps) { ); }; - return ; + return ; } diff --git a/src/components/common/LoadingPanel.module.css b/src/components/common/LoadingPanel.module.css new file mode 100644 index 0000000000..d82e27d69b --- /dev/null +++ b/src/components/common/LoadingPanel.module.css @@ -0,0 +1,5 @@ +.panel { + display: flex; + flex-direction: column; + position: relative; +} diff --git a/src/components/common/LoadingPanel.tsx b/src/components/common/LoadingPanel.tsx new file mode 100644 index 0000000000..890e8e3918 --- /dev/null +++ b/src/components/common/LoadingPanel.tsx @@ -0,0 +1,36 @@ +import { ReactNode } from 'react'; +import styles from './LoadingPanel.module.css'; +import classNames from 'classnames'; +import ErrorMessage from 'components/common/ErrorMessage'; +import { Loading } from 'react-basics'; +import Empty from 'components/common/Empty'; + +export function LoadingPanel({ + data, + error, + isFetched, + isLoading, + loadingIcon = 'dots', + className, + children, +}: { + data?: any; + error?: Error; + isFetched?: boolean; + isLoading?: boolean; + loadingIcon?: 'dots' | 'spinner'; + isEmpty?: boolean; + className?: string; + children: ReactNode; +}) { + const isEmpty = !data?.length && !isLoading && isFetched; + + return ( +
+ {isLoading && !isFetched && } + {error && } + {!error && isEmpty && } + {!error && !isEmpty && data && children} +
+ ); +} diff --git a/src/components/hooks/index.ts b/src/components/hooks/index.ts index d92329c78d..42cac81a65 100644 --- a/src/components/hooks/index.ts +++ b/src/components/hooks/index.ts @@ -1,5 +1,8 @@ export * from './queries/useApi'; export * from './queries/useConfig'; +export * from './queries/useEventDataEvents'; +export * from './queries/useEventDataProperties'; +export * from './queries/useEventDataValues'; export * from './queries/usePagedQuery'; export * from './queries/useLogin'; export * from './queries/useRealtime'; diff --git a/src/components/hooks/queries/useEventDataEvents.ts b/src/components/hooks/queries/useEventDataEvents.ts new file mode 100644 index 0000000000..1d7ccf2da1 --- /dev/null +++ b/src/components/hooks/queries/useEventDataEvents.ts @@ -0,0 +1,20 @@ +import useApi from './useApi'; +import { UseQueryOptions } from '@tanstack/react-query'; +import { useFilterParams } from '../useFilterParams'; + +export function useEventDataEvents( + websiteId: string, + options?: Omit, +) { + const { get, useQuery } = useApi(); + const params = useFilterParams(websiteId); + + return useQuery({ + queryKey: ['websites:event-data:events', { websiteId, ...params }], + queryFn: () => get(`/websites/${websiteId}/event-data/events`, { ...params }), + enabled: !!websiteId, + ...options, + }); +} + +export default useEventDataEvents; diff --git a/src/components/hooks/queries/useEventDataProperties.ts b/src/components/hooks/queries/useEventDataProperties.ts new file mode 100644 index 0000000000..4eabd0519e --- /dev/null +++ b/src/components/hooks/queries/useEventDataProperties.ts @@ -0,0 +1,20 @@ +import useApi from './useApi'; +import { UseQueryOptions } from '@tanstack/react-query'; +import { useFilterParams } from '../useFilterParams'; + +export function useEventDataProperties( + websiteId: string, + options?: Omit, +) { + const { get, useQuery } = useApi(); + const params = useFilterParams(websiteId); + + return useQuery({ + queryKey: ['websites:event-data:properties', { websiteId, ...params }], + queryFn: () => get(`/websites/${websiteId}/event-data/properties`, { ...params }), + enabled: !!websiteId, + ...options, + }); +} + +export default useEventDataProperties; diff --git a/src/components/hooks/queries/useEventDataValues.ts b/src/components/hooks/queries/useEventDataValues.ts new file mode 100644 index 0000000000..47b5a513ce --- /dev/null +++ b/src/components/hooks/queries/useEventDataValues.ts @@ -0,0 +1,21 @@ +import useApi from './useApi'; +import { UseQueryOptions } from '@tanstack/react-query'; +import { useFilterParams } from '../useFilterParams'; + +export function useEventDataValues( + websiteId: string, + propertyName: string, + options?: Omit, +) { + const { get, useQuery } = useApi(); + const params = useFilterParams(websiteId); + + return useQuery({ + queryKey: ['websites:event-data:values', { websiteId, propertyName, ...params }], + queryFn: () => get(`/websites/${websiteId}/event-data/values`, { ...params, propertyName }), + enabled: !!(websiteId && propertyName), + ...options, + }); +} + +export default useEventDataValues; diff --git a/src/components/messages.ts b/src/components/messages.ts index 75fa2d3e6c..f711a4f6cf 100644 --- a/src/components/messages.ts +++ b/src/components/messages.ts @@ -133,7 +133,7 @@ export const labels = defineMessages({ session: { id: 'label.session', defaultMessage: 'Session' }, sessions: { id: 'label.sessions', defaultMessage: 'Sessions' }, pageNotFound: { id: 'message.page-not-found', defaultMessage: 'Page not found' }, - activityLog: { id: 'label.activity-log', defaultMessage: 'Activity log' }, + activity: { id: 'label.activity', defaultMessage: 'Activity' }, dismiss: { id: 'label.dismiss', defaultMessage: 'Dismiss' }, poweredBy: { id: 'label.powered-by', defaultMessage: 'Powered by {name}' }, pageViews: { id: 'label.page-views', defaultMessage: 'Page views' }, diff --git a/src/lang/am-ET.json b/src/lang/am-ET.json index 7c533031ab..ab31121851 100644 --- a/src/lang/am-ET.json +++ b/src/lang/am-ET.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "Actions", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Combined", "label.filter-raw": "Raw", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Last {x} days", "label.last-hours": "Last {x} hours", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "Pages", "label.password": "Password", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Powered by {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Profile", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Reset statistics", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "Save", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Settings", "label.share-url": "Share URL", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "Tracking code", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Unique visitors", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Unknown", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "Username", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/ar-SA.json b/src/lang/ar-SA.json index f764f29e0a..981fea8407 100644 --- a/src/lang/ar-SA.json +++ b/src/lang/ar-SA.json @@ -1,7 +1,7 @@ { "label.access-code": "كود الدعوة", "label.actions": "الإجراءات", - "label.activity-log": "سجل الأحداث", + "label.activity": "سجل الأحداث", "label.add": "أضِف", "label.add-description": "أضِف وصف", "label.add-member": "أضِف عضو", @@ -78,6 +78,7 @@ "label.filter-combined": "مُجمّعة", "label.filter-raw": "خام", "label.filters": "التصفيات", + "label.first-seen": "First seen", "label.funnel": "قمع", "label.funnel-description": "فهم معدل التحويل والانقطاع عن المستخدمين.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "آخر {x} يوم/ايام", "label.last-hours": "آخر {x} ساعة", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "غادر", "label.leave-team": "مغادرة المجموعة", "label.less-than": "أقل مِن", @@ -132,11 +134,14 @@ "label.pageTitle": "عنوان الصفحة", "label.pages": "الصفحات", "label.password": "كلمة المرور", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "مشغل بواسطة {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "الملف الشخصي", + "label.properties": "Properties", "label.property": "Property", "label.queries": "استعلامات", "label.query": "استعلام", @@ -156,6 +161,9 @@ "label.reset-website": "اعادة تعيين الإحصائيات", "label.retention": "الاحتفاظ", "label.retention-description": "قس مدى ثبات موقعك على الويب من خلال تتبع عدد مرات عودة المستخدمين.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "الصلاحية", "label.run-query": "شغّل الاستعلام", "label.save": "حفظ", @@ -165,6 +173,7 @@ "label.select-date": "حدد التاريخ", "label.select-role": "حدد الدور", "label.select-website": "حدد موقع", + "label.session": "Session", "label.sessions": "الزيارات", "label.settings": "الإعدادات", "label.share-url": "مشاركة الرابط", @@ -193,18 +202,21 @@ "label.total": "الإجمالي", "label.total-records": "إجمالي السجلات", "label.tracking-code": "كود التتبع", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "انقل الموقع", "label.true": "حقيقي", "label.type": "النوع", "label.unique": "فريد", "label.unique-visitors": "زائرون فريدون", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "غير معروف", "label.untitled": "بدون عنوان", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "المستخدم", + "label.user-property": "User Property", "label.username": "اسم المستخدم", "label.users": "المستخدمين", "label.utm": "UTM", diff --git a/src/lang/be-BY.json b/src/lang/be-BY.json index 129e6702af..b417afaa44 100644 --- a/src/lang/be-BY.json +++ b/src/lang/be-BY.json @@ -1,7 +1,7 @@ { "label.access-code": "Код доступу", "label.actions": "Дзеянні", - "label.activity-log": "Журнал актыўнасці", + "label.activity": "Журнал актыўнасці", "label.add": "Дадаць", "label.add-description": "Дадаць апісанне", "label.add-member": "Дадаць удзельніка", @@ -78,6 +78,7 @@ "label.filter-combined": "Камбініраваны", "label.filter-raw": "Сырыя", "label.filters": "Фільтры", + "label.first-seen": "First seen", "label.funnel": "Варонка", "label.funnel-description": "Разумець паказчыкі канверсіі і адмоваў.", "label.goal": "Мэта", @@ -103,6 +104,7 @@ "label.last-days": "Апошнія {x} дзён", "label.last-hours": "Апошнія {x} гадзіны", "label.last-months": "Апошнія {x} месяцаў", + "label.last-seen": "Last seen", "label.leave": "Пакінуць", "label.leave-team": "Пакінуць каманду", "label.less-than": "Менш чым", @@ -132,11 +134,14 @@ "label.pageTitle": "Загаловак старонкі", "label.pages": "Старонкі", "label.password": "Пароль", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Зроблена {name}", "label.previous": "Папярэдні", "label.previous-period": "Папярэдні перыяд", "label.previous-year": "Папярэдні год", "label.profile": "Профіль", + "label.properties": "Properties", "label.property": "Уласцівасць", "label.queries": "Запыты", "label.query": "Запыт", @@ -156,6 +161,9 @@ "label.reset-website": "Скінуць статыстыку", "label.retention": "Утрыманне", "label.retention-description": "Ацаніць прыцягальнасць сайта, адсочваючы павяртанні карыстальнікаў.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Роля", "label.run-query": "Запусціць запыт", "label.save": "Захаваць", @@ -165,6 +173,7 @@ "label.select-date": "Выбраць дату", "label.select-role": "Выбраць ролю", "label.select-website": "Выбраць сайт", + "label.session": "Session", "label.sessions": "Сесіі", "label.settings": "Налады", "label.share-url": "Падзяліцца спасылкай", @@ -193,18 +202,21 @@ "label.total": "Агульная колькасць", "label.total-records": "Агульная колькасць запісаў", "label.tracking-code": "Код адсочвання", + "label.transactions": "Transactions", "label.transfer": "Перадаць", "label.transfer-website": "Перадаць сайт", "label.true": "Ісціна", "label.type": "Тып", "label.unique": "Унікальны", "label.unique-visitors": "Унікальныя наведвальнікі", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Невядома", "label.untitled": "Без назвы", "label.update": "Абнавіць", "label.url": "URL", "label.urls": "URLs", "label.user": "Карыстальнік", + "label.user-property": "User Property", "label.username": "Імя карыстальніка", "label.users": "Карыстальнікі", "label.utm": "UTM", diff --git a/src/lang/bg-BG.json b/src/lang/bg-BG.json index 5095b38799..1fd87de2e4 100644 --- a/src/lang/bg-BG.json +++ b/src/lang/bg-BG.json @@ -1,7 +1,7 @@ { "label.access-code": "Код за достъп", "label.actions": "Действия", - "label.activity-log": "Активностти", + "label.activity": "Активностти", "label.add": "Добави", "label.add-description": "Добави описание", "label.add-member": "Добави член", @@ -78,6 +78,7 @@ "label.filter-combined": "Комбиниран", "label.filter-raw": "Суров", "label.filters": "Филтри", + "label.first-seen": "First seen", "label.funnel": "Фуния", "label.funnel-description": "Разберете процента на конверсия и отпадане на потребителите.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Последните {x} дни", "label.last-hours": "Последните {x} часа", "label.last-months": "Последните {x} месеца", + "label.last-seen": "Last seen", "label.leave": "Напусни", "label.leave-team": "Напусни екип", "label.less-than": "По-малко от", @@ -132,11 +134,14 @@ "label.pageTitle": "Заглавие на страница", "label.pages": "Страници", "label.password": "Парола", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Поддържано от {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Профил", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Запитвания", "label.query": "Запитване", @@ -156,6 +161,9 @@ "label.reset-website": "Нулирай уебсайт", "label.retention": "Привързване", "label.retention-description": "Измерете привързаността към вашия уебсайт, като проследявате колко често потребителите се връщат.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Роля", "label.run-query": "Изпълни запитване", "label.save": "Запази", @@ -165,6 +173,7 @@ "label.select-date": "Избери дата", "label.select-role": "Избери роля", "label.select-website": "Избери уебсайт", + "label.session": "Session", "label.sessions": "Сесии", "label.settings": "Настройки", "label.share-url": "Сподели Линк", @@ -193,18 +202,21 @@ "label.total": "Общо", "label.total-records": "Общо записи", "label.tracking-code": "Код за проследяване", + "label.transactions": "Transactions", "label.transfer": "Прехвърли", "label.transfer-website": "Прехвърляне на уебсайт", "label.true": "Вярно", "label.type": "Вид", "label.unique": "Уникален", "label.unique-visitors": "Уникални посетители", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Неизвестен", "label.untitled": "Без заглавие", "label.update": "Актуализирай", "label.url": "URL адрес", "label.urls": "URL адреси", "label.user": "Потребител", + "label.user-property": "User Property", "label.username": "Потребителско име", "label.users": "Потребители", "label.utm": "UTM", diff --git a/src/lang/bn-BD.json b/src/lang/bn-BD.json index 18eb1d896e..7a22d76c17 100644 --- a/src/lang/bn-BD.json +++ b/src/lang/bn-BD.json @@ -1,7 +1,7 @@ { "label.access-code": "এক্সেস কোড", "label.actions": "অ্যাকশনস", - "label.activity-log": "একটিভিটি দেখুন", + "label.activity": "একটিভিটি দেখুন", "label.add": "যুক্ত করুন", "label.add-description": "বর্ননা যোগ করুন", "label.add-member": "সদস্য যোগ করুন", @@ -78,6 +78,7 @@ "label.filter-combined": "সম্মিলিত", "label.filter-raw": "অপরিশোধিত", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "শেষ {x} দিন", "label.last-hours": "শেষ {x} ঘন্টা", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "পৃষ্ঠাগুলি", "label.password": "পাসওয়ার্ড", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "{name} দ্বারা চালিত", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "প্রোফাইল", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "ওয়েবসাইট রিসেট করুন", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "সংরক্ষণ", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "সেটিংস", "label.share-url": "ইউআরএল শেয়ার করুন", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "ট্র্যাকিং কোড", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "অনন্য ভিজিটর", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "অজানা", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "ব্যবহারকারীর নাম", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/bs-BA.json b/src/lang/bs-BA.json index 000af0dc20..b9d9e8a8e8 100644 --- a/src/lang/bs-BA.json +++ b/src/lang/bs-BA.json @@ -1,7 +1,7 @@ { "label.access-code": "Pristupni kod", "label.actions": "Akcije", - "label.activity-log": "Log aktivnosti", + "label.activity": "Log aktivnosti", "label.add": "Dodaj", "label.add-description": "Dodaj opis", "label.add-member": "Dodaj člana", @@ -78,6 +78,7 @@ "label.filter-combined": "Kombinovano", "label.filter-raw": "Sirovo", "label.filters": "Filtri", + "label.first-seen": "First seen", "label.funnel": "Lijevak", "label.funnel-description": "Razumite koverziju i drop-off učestalost korisnika.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Zadnjih {x} dana", "label.last-hours": "Zadnjih {x} sati", "label.last-months": "Zadnjih {x} mjeseci", + "label.last-seen": "Last seen", "label.leave": "Napusti", "label.leave-team": "Napusti tim", "label.less-than": "Manje od", @@ -132,11 +134,14 @@ "label.pageTitle": "Naslov stranice", "label.pages": "Stranice", "label.password": "Šifra", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Omogućeno s {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Profil", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queryji", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Resetuj web stranicu", "label.retention": "Retention", "label.retention-description": "Izmjeri 'ljepljivost' svoje web stranice praćenjem koliko često set korisnici vraćaju.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Rola", "label.run-query": "Pokreni query", "label.save": "Sačuvaj", @@ -165,6 +173,7 @@ "label.select-date": "Odaberi datum", "label.select-role": "Odaberi rolu", "label.select-website": "Odaberi web stranicu", + "label.session": "Session", "label.sessions": "Sesije", "label.settings": "Postavke", "label.share-url": "Share URL", @@ -193,18 +202,21 @@ "label.total": "Ukupno", "label.total-records": "Ukupno redova", "label.tracking-code": "Kod za praćenje", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer web stranice", "label.true": "Da", "label.type": "Tip", "label.unique": "Jedinstveno", "label.unique-visitors": "Jedinstvenih posjetitelja", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Nepoznato", "label.untitled": "Bezimeno", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "Korisnik", + "label.user-property": "User Property", "label.username": "Korisničko ime", "label.users": "Korisnici", "label.utm": "UTM", diff --git a/src/lang/ca-ES.json b/src/lang/ca-ES.json index dae977fc2c..5083af70ea 100644 --- a/src/lang/ca-ES.json +++ b/src/lang/ca-ES.json @@ -1,7 +1,7 @@ { "label.access-code": "Codi d'accés", "label.actions": "Accions", - "label.activity-log": "Registre d'activitat", + "label.activity": "Registre d'activitat", "label.add": "Afegir", "label.add-description": "Afegir descripció", "label.add-member": "Afegir membre", @@ -78,6 +78,7 @@ "label.filter-combined": "Combinat", "label.filter-raw": "En cru", "label.filters": "Filtres", + "label.first-seen": "First seen", "label.funnel": "Embut", "label.funnel-description": "Entengui la taxa de conversió i abandonament dels usuaris.", "label.goal": "Meta", @@ -103,6 +104,7 @@ "label.last-days": "Últims {x} dies", "label.last-hours": "Últimes {x} hores", "label.last-months": "Últims {x} mesos", + "label.last-seen": "Last seen", "label.leave": "Abandonar", "label.leave-team": "Abandonar equip", "label.less-than": "Menor que", @@ -132,11 +134,14 @@ "label.pageTitle": "Títol de la pàgina", "label.pages": "Pàgines", "label.password": "Contrasenya", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Funciona amb {name}", "label.previous": "Anterior", "label.previous-period": "Període anterior", "label.previous-year": "Any anterior", "label.profile": "Perfil", + "label.properties": "Properties", "label.property": "Propietat", "label.queries": "Consultes", "label.query": "Consulta", @@ -156,6 +161,9 @@ "label.reset-website": "Restableix estadístiques", "label.retention": "Retenció", "label.retention-description": "Mesuri la retenció del seu lloc web fent un seguiment de la freqüència amb què tornen els usuaris.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Rol", "label.run-query": "Executar consulta", "label.save": "Desa", @@ -165,6 +173,7 @@ "label.select-date": "Seleccionar data", "label.select-role": "Seleccionar rol", "label.select-website": "Seleccionar lloc web", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Configuració", "label.share-url": "Enllaç per compartir", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total de registres", "label.tracking-code": "Codi de seguiment", + "label.transactions": "Transactions", "label.transfer": "Transferir", "label.transfer-website": "Transferir lloc web", "label.true": "Cert", "label.type": "Tipus", "label.unique": "Únic", "label.unique-visitors": "Visitants únics", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Desconegut", "label.untitled": "Sense títol", "label.update": "Actualitzar", "label.url": "URL", "label.urls": "URLs", "label.user": "Usuari", + "label.user-property": "User Property", "label.username": "Nom d'usuari", "label.users": "Usuaris", "label.utm": "UTM", diff --git a/src/lang/cs-CZ.json b/src/lang/cs-CZ.json index 3a57f6b28c..717bd97019 100644 --- a/src/lang/cs-CZ.json +++ b/src/lang/cs-CZ.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "Akce", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Kombinace", "label.filter-raw": "Nezpracované", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Posledních {x} dnů", "label.last-hours": "Posledních {x} hodin", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "Stránky", "label.password": "Heslo", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Běží na {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Profil", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Reset statistics", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "Uložit", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Nastavení", "label.share-url": "Sdílet URL", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "Sledovací kód", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Jedinečné návštěvy", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Neznámý", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "Uživatelské jméno", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/da-DK.json b/src/lang/da-DK.json index 715df99e21..143d079dca 100644 --- a/src/lang/da-DK.json +++ b/src/lang/da-DK.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "Handlinger", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Kombineret", "label.filter-raw": "Rå", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Sidste {x} dage", "label.last-hours": "Sidste {x} timer", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "Sider", "label.password": "Adgangskode", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Drevet af {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Profil", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Nulstil statistikker", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "Gem", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Indstillinger", "label.share-url": "Del URL", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "Sporingskode", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Unikke besøgende", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Ukendt", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "Brugernavn", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/de-CH.json b/src/lang/de-CH.json index 6891480fa9..aa57f90c9c 100644 --- a/src/lang/de-CH.json +++ b/src/lang/de-CH.json @@ -1,7 +1,7 @@ { "label.access-code": "Zuegangscode", "label.actions": "Aktione", - "label.activity-log": "Aktivitätsverlauf", + "label.activity": "Aktivitätsverlauf", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Kombiniert", "label.filter-raw": "Rohdate", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Letzti {x} Täg", "label.last-hours": "Letzti {x} Stunde", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Verlah", "label.leave-team": "Team verlah", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "Siite", "label.password": "Passwort", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Betribe dur {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Profil", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Abfrage", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Statistik zruggsetze", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Rollä", "label.run-query": "Run query", "label.save": "Speichere", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Websiite uuswähle", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Istellige", "label.share-url": "Freigab-URL", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "Tracking Code", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Eidütigi Bsuecher", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Unbekannt", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "Benutzer", + "label.user-property": "User Property", "label.username": "Benutzername", "label.users": "Benutzer", "label.utm": "UTM", diff --git a/src/lang/de-DE.json b/src/lang/de-DE.json index cb9ba089ca..5151910533 100644 --- a/src/lang/de-DE.json +++ b/src/lang/de-DE.json @@ -1,7 +1,7 @@ { "label.access-code": "Zugangscode", "label.actions": "Aktionen", - "label.activity-log": "Aktivitätsverlauf", + "label.activity": "Aktivitätsverlauf", "label.add": "Hinzufügen", "label.add-description": "Beschreibung hinzufügen", "label.add-member": "Mitglied hinzufügen", @@ -78,6 +78,7 @@ "label.filter-combined": "Kombiniert", "label.filter-raw": "Rohdaten", "label.filters": "Filter", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Verstehe die Konversions- und Abbruchrate der Nutzer.", "label.goal": "Ziel", @@ -103,6 +104,7 @@ "label.last-days": "Letzten {x} Tage", "label.last-hours": "Letzten {x} Stunden", "label.last-months": "Letzten {x} Monate", + "label.last-seen": "Last seen", "label.leave": "Verlassen", "label.leave-team": "Team verlassen", "label.less-than": "Kleiner als", @@ -132,11 +134,14 @@ "label.pageTitle": "Seitentitel", "label.pages": "Seiten", "label.password": "Passwort", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Betrieben durch {name}", "label.previous": "Vorheriges", "label.previous-period": "Vorherige Periode", "label.previous-year": "Vorheriges Jahr", "label.profile": "Profil", + "label.properties": "Properties", "label.property": "Eigentum", "label.queries": "Abfragen", "label.query": "Abfrage", @@ -156,6 +161,9 @@ "label.reset-website": "Statistik zurücksetzen", "label.retention": "Bewahrung", "label.retention-description": "Messen Sie die Verweildauer auf Ihrer Website, indem Sie verfolgen, wie oft die Nutzer zurückkehren.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Rolle", "label.run-query": "Abfrage starten", "label.save": "Speichern", @@ -165,6 +173,7 @@ "label.select-date": "Datum auswählen", "label.select-role": "Rolle auswählen", "label.select-website": "Webseite auswählen", + "label.session": "Session", "label.sessions": "Sitzungen", "label.settings": "Einstellungen", "label.share-url": "Freigabe-URL", @@ -193,18 +202,21 @@ "label.total": "Gesamt", "label.total-records": "Datensätze insgesamt", "label.tracking-code": "Tracking Code", + "label.transactions": "Transactions", "label.transfer": "Übertragung", "label.transfer-website": "Website übertragen", "label.true": "Wahr", "label.type": "Typ", "label.unique": "Eindeutig", "label.unique-visitors": "Eindeutige Besucher", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Unbekannt", "label.untitled": "Unbenannt", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "Benutzer", + "label.user-property": "User Property", "label.username": "Benutzername", "label.users": "Benutzer", "label.utm": "UTM", diff --git a/src/lang/el-GR.json b/src/lang/el-GR.json index ceb0812bdd..263ea7e9f0 100644 --- a/src/lang/el-GR.json +++ b/src/lang/el-GR.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "Ενέργειες", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Σε συνδυασμό", "label.filter-raw": "Ακατέργαστο", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Τελευταίες {x} ημέρες", "label.last-hours": "Τελευταίες {x} ώρες", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "Σελίδες", "label.password": "Κωδικός", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Με την υποστήριξη του {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Προφίλ", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Reset statistics", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "Αποθήκευση", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Ρυθμίσεις", "label.share-url": "Κοινοποίηση διεύθυνσης URL", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "Κωδικός παρακολούθησης", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Μοναδικοί επισκέπτες", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Άγνωστο", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "Όνομα χρήστη", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/en-GB.json b/src/lang/en-GB.json index 9bb61811b4..80bbbd7b89 100644 --- a/src/lang/en-GB.json +++ b/src/lang/en-GB.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "Actions", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Combined", "label.filter-raw": "Raw", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Last {x} days", "label.last-hours": "Last {x} hours", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "Pages", "label.password": "Password", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Powered by {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Profile", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Reset statistics", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "Save", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Settings", "label.share-url": "Share URL", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "Tracking code", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Unique visitors", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Unknown", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "Username", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/en-US.json b/src/lang/en-US.json index e895036cc1..8fe3314592 100644 --- a/src/lang/en-US.json +++ b/src/lang/en-US.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "Actions", - "label.activity-log": "Activity log", + "label.activity": "Activity", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -66,11 +66,11 @@ "label.edit-member": "Edit member", "label.enable-share-url": "Enable share URL", "label.end-step": "End Step", - "label.entry": "Entry URL", + "label.entry": "Entry path", "label.event": "Event", "label.event-data": "Event data", "label.events": "Events", - "label.exit": "Exit URL", + "label.exit": "Exit path", "label.false": "False", "label.field": "Field", "label.fields": "Fields", @@ -78,6 +78,7 @@ "label.filter-combined": "Combined", "label.filter-raw": "Raw", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Last {x} days", "label.last-hours": "Last {x} hours", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "Pages", "label.password": "Password", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Powered by {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Profile", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Reset website", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "Save", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Settings", "label.share-url": "Share URL", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "Tracking code", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Unique visitors", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Unknown", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "Username", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/es-ES.json b/src/lang/es-ES.json index 532e4d20c9..5f930be7b6 100644 --- a/src/lang/es-ES.json +++ b/src/lang/es-ES.json @@ -1,7 +1,7 @@ { "label.access-code": "Código de acceso", "label.actions": "Acciones", - "label.activity-log": "Registro de actividad", + "label.activity": "Registro de actividad", "label.add": "Añadir", "label.add-description": "Añadir descripción", "label.add-member": "Añadir miembro", @@ -78,6 +78,7 @@ "label.filter-combined": "Combinado", "label.filter-raw": "En crudo", "label.filters": "Filtros", + "label.first-seen": "First seen", "label.funnel": "Embudo", "label.funnel-description": "Comprender conversión y abandono de usuarios.", "label.goal": "Objetivo", @@ -103,6 +104,7 @@ "label.last-days": "Últimos {x} días", "label.last-hours": "Últimas {x} horas", "label.last-months": "Últimos {x} meses", + "label.last-seen": "Last seen", "label.leave": "Abandonar", "label.leave-team": "Abandonar equipo", "label.less-than": "Menor que", @@ -132,11 +134,14 @@ "label.pageTitle": "Título de página", "label.pages": "Páginas", "label.password": "Contraseña", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Analíticas de {name}", "label.previous": "Anterior", "label.previous-period": "Periodo anterior", "label.previous-year": "Año anterior", "label.profile": "Perfil", + "label.properties": "Properties", "label.property": "Propiedad", "label.queries": "Consultas", "label.query": "Consulta", @@ -156,6 +161,9 @@ "label.reset-website": "Reiniciar analíticas", "label.retention": "Retención", "label.retention-description": "Medir la frecuencia con la que los usuarios vuelven a tu sitio web.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Rol", "label.run-query": "Ejecutar consulta", "label.save": "Guardar", @@ -165,6 +173,7 @@ "label.select-date": "Seleccionar fecha", "label.select-role": "Seleccionar rol", "label.select-website": "Seleccionar sitio web", + "label.session": "Session", "label.sessions": "Sesiones", "label.settings": "Ajustes", "label.share-url": "Compartir URL", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total de registros", "label.tracking-code": "Código de rastreo", + "label.transactions": "Transactions", "label.transfer": "Transferir", "label.transfer-website": "Transferir sitio web", "label.true": "Verdadero", "label.type": "Tipo", "label.unique": "Único", "label.unique-visitors": "Visitantes únicos", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Desconocida", "label.untitled": "Sin título", "label.update": "Actualizar", "label.url": "URL", "label.urls": "URLs", "label.user": "Usuario", + "label.user-property": "User Property", "label.username": "Nombre de usuario", "label.users": "Usuarios", "label.utm": "UTM", diff --git a/src/lang/fa-IR.json b/src/lang/fa-IR.json index fe1705e30a..d5b480218c 100644 --- a/src/lang/fa-IR.json +++ b/src/lang/fa-IR.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "اقدامات", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "ترکیب شده", "label.filter-raw": "خام", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "لیست {x} روز گذشته", "label.last-hours": "لیست {x} ساعت گذشته", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "صفحه‌ها", "label.password": "رمز", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "قدرت گرفته توسط {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "پروفایل", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "بازنشانی آمار", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "ذخیره", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "تنظیمات", "label.share-url": "به اشتراک گذاری URL", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "کد رهگیری", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "بازدیدکننده‌های یکتا", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "ناشناخته", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "نام کاربری", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/fi-FI.json b/src/lang/fi-FI.json index 02561dcb23..a47df265d2 100644 --- a/src/lang/fi-FI.json +++ b/src/lang/fi-FI.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "Toiminnat", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Yhdistetty", "label.filter-raw": "Käsittelemätön", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Viimeisimmät {x} päivää", "label.last-hours": "Viimeisimmät {x} tuntia", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "Sivut", "label.password": "Salasana", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Voimanlähteenä {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Profiili", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Nollaa tilastot", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "Tallenna", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Asetukset", "label.share-url": "Jaa URL", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "Seurantakoodi", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Yksittäiset kävijät", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Tuntematon", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "Käyttäjänimi", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/fo-FO.json b/src/lang/fo-FO.json index e9ca2aae91..231753029a 100644 --- a/src/lang/fo-FO.json +++ b/src/lang/fo-FO.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "Gerðir", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Samansett", "label.filter-raw": "Óviðgjørt", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Seinastu {x} dagarnar", "label.last-hours": "Seinastu {x} tímarnar", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "Síðir", "label.password": "Loyniorð", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Powered by {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Vangi", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Reset statistics", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "Goym", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Stillingar", "label.share-url": "Deil leinku", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "Spori kota", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Einsýna vitjanir", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Ókent", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "Brúkaranavn", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/fr-FR.json b/src/lang/fr-FR.json index edb2abd36b..e1c9befef6 100644 --- a/src/lang/fr-FR.json +++ b/src/lang/fr-FR.json @@ -1,7 +1,7 @@ { "label.access-code": "Code d'accès", "label.actions": "Actions", - "label.activity-log": "Journal d'activité", + "label.activity": "Journal d'activité", "label.add": "Ajouter", "label.add-description": "Ajouter une description", "label.add-member": "Ajouter un membre", @@ -78,6 +78,7 @@ "label.filter-combined": "Combiné", "label.filter-raw": "Brut", "label.filters": "Filtres", + "label.first-seen": "First seen", "label.funnel": "Entonnoir", "label.funnel-description": "Suivi des conversions et des taux d'abandons.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "{x} derniers jours", "label.last-hours": "{x} dernières heures", "label.last-months": "{x} derniers mois", + "label.last-seen": "Last seen", "label.leave": "Quitter", "label.leave-team": "Quitter l'équipe", "label.less-than": "Inférieur à", @@ -132,11 +134,14 @@ "label.pageTitle": "Titre de page", "label.pages": "Pages", "label.password": "Mot de passe", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Propulsé par {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Profil", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Requêtes", "label.query": "Requête", @@ -156,6 +161,9 @@ "label.reset-website": "Réinitialiser les statistiques", "label.retention": "Rétention", "label.retention-description": "Mesure de l'attractivité du site en visualisant les taux de visiteurs qui reviennent.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Rôle", "label.run-query": "Éxécuter la requête", "label.save": "Enregistrer", @@ -165,6 +173,7 @@ "label.select-date": "Choisir une période", "label.select-role": "Choisir un rôle", "label.select-website": "Choisir un site", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Paramètres", "label.share-url": "URL de partage", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Nombre d'enregistrements", "label.tracking-code": "Code de suivi", + "label.transactions": "Transactions", "label.transfer": "Transférer", "label.transfer-website": "Transférer le site", "label.true": "Vrai", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Visiteurs uniques", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Inconnu", "label.untitled": "Sans titre", "label.update": "Modifier", "label.url": "URL", "label.urls": "URLs", "label.user": "Utilisateur", + "label.user-property": "User Property", "label.username": "Nom d'utilisateur", "label.users": "Utilisateurs", "label.utm": "UTM", diff --git a/src/lang/ga-ES.json b/src/lang/ga-ES.json index d267137f1a..14b9a47411 100644 --- a/src/lang/ga-ES.json +++ b/src/lang/ga-ES.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "Accións", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Combinado", "label.filter-raw": "Raw", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Últimos {x} días", "label.last-hours": "Últimas {x} horas", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "Páxinas", "label.password": "Contrasinal", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Funciona grazas a {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Perfil", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "To reset this website, type {confirmation} in the box below to confirm.", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "Gardar", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Axustes", "label.share-url": "Compartir URL", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "Código de seguimento", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Visitas únicas", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Descoñecido", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "Identificador", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/he-IL.json b/src/lang/he-IL.json index d7ebf875b6..fd25cd144a 100644 --- a/src/lang/he-IL.json +++ b/src/lang/he-IL.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "פעולות", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "משותף", "label.filter-raw": "גולמי", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "{x} ימים אחרונים", "label.last-hours": "{x} שעות אחרונות", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "דפים", "label.password": "סיסמה", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Powered by {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "פרופיל", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Reset statistics", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "שמירה", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "הגדרות", "label.share-url": "שיתוף URL", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "קוד מעקב", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "מבקרים ייחודיים", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "לא ידוע", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "שם משתמש", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/hi-IN.json b/src/lang/hi-IN.json index 1fbee2c02e..2fabe0c6e1 100644 --- a/src/lang/hi-IN.json +++ b/src/lang/hi-IN.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "कार्य", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "संयुक्त", "label.filter-raw": "रॉ", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "पिछले {x} दिन", "label.last-hours": "पिछले {x} घंटे", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "पृष्ठों", "label.password": "पासवर्ड", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "{name} द्वारा संचालित", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "प्रोफ़ाइल", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Reset statistics", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "सहेजें", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "समायोजन", "label.share-url": "यूआरएल साझा करें", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "ट्रैकिंग कोड", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "अद्वितीय आगंतुकों", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "अज्ञात", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "उपयोगकर्ता नाम", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/hr-HR.json b/src/lang/hr-HR.json index 39d4d9d1dd..6bf12237ab 100644 --- a/src/lang/hr-HR.json +++ b/src/lang/hr-HR.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "Actions", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Combined", "label.filter-raw": "Raw", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Zadnjih {x} dana", "label.last-hours": "Zadnjih {x} sati", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "Pages", "label.password": "Lozinka", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Powered by {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Profil", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Resetirati web stranicu", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "Spremi", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Postavke", "label.share-url": "Podijeli poveznicu", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "Kod za praćenje", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Unique visitors", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Nepoznato", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "Korisničko ime", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/hu-HU.json b/src/lang/hu-HU.json index 6762181bd7..8593b37e7a 100644 --- a/src/lang/hu-HU.json +++ b/src/lang/hu-HU.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "Műveletek", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Összevont", "label.filter-raw": "Nyers", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Legutóbbi {x} nap", "label.last-hours": "Legutóbbi {x} óra", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "Oldalak", "label.password": "Jelszó", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Működteti az {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Profil", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Reset statistics", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "Mentés", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Beállítások", "label.share-url": "URL megosztása", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "Követési kód", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Egyedi látogatók", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Ismeretlen", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "Felhasználónév", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/id-ID.json b/src/lang/id-ID.json index 930b02da5c..6cf7659caa 100644 --- a/src/lang/id-ID.json +++ b/src/lang/id-ID.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "Aksi", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Gabungan", "label.filter-raw": "Mentah", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "{x} hari terakhir", "label.last-hours": "{x} jam terakhir", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "Halaman", "label.password": "Kata sandi", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Didukung oleh {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Profil", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Atur ulang statistik", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "Simpan", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Pengaturan", "label.share-url": "Bagikan URL", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "Kode lacak", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Pengunjung unik", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Tidak diketahui", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "Nama pengguna", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/it-IT.json b/src/lang/it-IT.json index 229254d56a..4df7599a45 100644 --- a/src/lang/it-IT.json +++ b/src/lang/it-IT.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "Azioni", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Aggregati", "label.filter-raw": "Raw", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Ultimi {x} giorni", "label.last-hours": "Ultime {x} ore", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "Pagine", "label.password": "Password", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Powered by {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Profilo", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Resetta le statistiche", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "Salva", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Impostazioni", "label.share-url": "Condividi link", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "Codice di tracking", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Visitatori unici", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Sconosciuto", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "Nome utente", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/ja-JP.json b/src/lang/ja-JP.json index 02e300bbb8..72e2998b5c 100644 --- a/src/lang/ja-JP.json +++ b/src/lang/ja-JP.json @@ -1,7 +1,7 @@ { "label.access-code": "アクセスコード", "label.actions": "アクション", - "label.activity-log": "アクティビティログ", + "label.activity": "アクティビティログ", "label.add": "追加", "label.add-description": "説明を追加", "label.add-member": "メンバーの追加", @@ -78,6 +78,7 @@ "label.filter-combined": "結合", "label.filter-raw": "RAW", "label.filters": "フィルター", + "label.first-seen": "First seen", "label.funnel": "ファネル", "label.funnel-description": "ユーザーのコンバージョン率と離脱率を分析します。", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "過去{x}日間", "label.last-hours": "過去{x}時間", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "離脱", "label.leave-team": "チームを離脱", "label.less-than": "未満", @@ -132,11 +134,14 @@ "label.pageTitle": "ページタイトル", "label.pages": "ページ", "label.password": "パスワード", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Powered by {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "プロフィール", + "label.properties": "Properties", "label.property": "Property", "label.queries": "クエリ", "label.query": "クエリ", @@ -156,6 +161,9 @@ "label.reset-website": "Webサイトをリセットする", "label.retention": "リテンション", "label.retention-description": "ユーザーの再訪問回数を記録して、Webサイトのリテンション率を計測します。", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "ロール", "label.run-query": "クエリ実行", "label.save": "保存", @@ -165,6 +173,7 @@ "label.select-date": "日付を選択", "label.select-role": "ロールを選択", "label.select-website": "Webサイトを選択", + "label.session": "Session", "label.sessions": "セッション", "label.settings": "設定", "label.share-url": "共有URL", @@ -193,18 +202,21 @@ "label.total": "累計", "label.total-records": "総記録数", "label.tracking-code": "トラッキングコード", + "label.transactions": "Transactions", "label.transfer": "移管", "label.transfer-website": "Webサイトの移管", "label.true": "真", "label.type": "種別", "label.unique": "ユニーク", "label.unique-visitors": "ユニーク訪問者数", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "不明", "label.untitled": "無題", "label.update": "Update", "label.url": "URL", "label.urls": "URL", "label.user": "ユーザー", + "label.user-property": "User Property", "label.username": "ユーザー名", "label.users": "ユーザー", "label.utm": "UTM", diff --git a/src/lang/km-KH.json b/src/lang/km-KH.json index f45ed44945..8a87834ace 100644 --- a/src/lang/km-KH.json +++ b/src/lang/km-KH.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "សកម្មភាព", - "label.activity-log": "កំណត់ហេតុ​សកម្មភាព", + "label.activity": "កំណត់ហេតុ​សកម្មភាព", "label.add": "បង្កើតបន្ថែម", "label.add-description": "បន្ថែមពិពណ៌នា", "label.add-member": "បន្ថែមសមាជិក", @@ -78,6 +78,7 @@ "label.filter-combined": "រួមបញ្ចូលគ្នា", "label.filter-raw": "ដើម", "label.filters": "ចម្រោះ", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "គោលដៅ", @@ -103,6 +104,7 @@ "label.last-days": "{x} ថ្ងៃចុងក្រោយ", "label.last-hours": "{x} ម៉ោងចុងក្រោយ", "label.last-months": "{x} ខែចុងក្រោយ", + "label.last-seen": "Last seen", "label.leave": "ចាកចេញ", "label.leave-team": "ចេញពីក្រុម", "label.less-than": "តិច​ជាង", @@ -132,11 +134,14 @@ "label.pageTitle": "ចំណងជើងទំព័រ", "label.pages": "ទំព័រ", "label.password": "ពាក្យសម្ងាត់​", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "ដំណើរការដោយ {name}", "label.previous": "មុន", "label.previous-period": "មួយរយៈពេលមុន", "label.previous-year": "ឆ្នាំ​មុន", "label.profile": "គណនី", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "កំណត់ស្ថិតិឡើងវិញ", "label.retention": "ការរក្សាទុក", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "មុខងារ", "label.run-query": "Run query", "label.save": "រក្សាទុក", @@ -165,6 +173,7 @@ "label.select-date": "ជ្រើសរើសកាលបរិច្ឆេទ", "label.select-role": "ជ្រើសរើសមុខងារ", "label.select-website": "ជ្រើសរើសគេហទំព័រ", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "ការកំណត់", "label.share-url": "ចែករំលែក URL", @@ -193,18 +202,21 @@ "label.total": "សរុប", "label.total-records": "កំណត់ត្រាសរុប", "label.tracking-code": "លេខកូដតាមដាន", + "label.transactions": "Transactions", "label.transfer": "ការផ្ទេរ", "label.transfer-website": "ការផ្ទេរគេហទំព័រ", "label.true": "ពិត", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "អ្នកចូលមើលម្នាក់ៗ", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "មិនស្គាល់", "label.untitled": "គ្មានចំណងជើង", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "អ្នកប្រើប្រាស់", + "label.user-property": "User Property", "label.username": "ឈ្មោះ​អ្នកប្រើប្រាស់", "label.users": "អ្នកប្រើប្រាស់", "label.utm": "UTM", diff --git a/src/lang/ko-KR.json b/src/lang/ko-KR.json index b3bee85f7a..8a2de47b14 100644 --- a/src/lang/ko-KR.json +++ b/src/lang/ko-KR.json @@ -1,7 +1,7 @@ { "label.access-code": "액세스 코드", "label.actions": "액션", - "label.activity-log": "활동 기록", + "label.activity": "활동 기록", "label.add": "추가", "label.add-description": "설명 추가", "label.add-member": "멤버 추가", @@ -78,6 +78,7 @@ "label.filter-combined": "합쳐서 보기", "label.filter-raw": "전체 보기", "label.filters": "필터", + "label.first-seen": "First seen", "label.funnel": "퍼널", "label.funnel-description": "사용자 전환율 및 이탈률을 살펴보세요.", "label.goal": "목표", @@ -103,6 +104,7 @@ "label.last-days": "최근 {x} 일", "label.last-hours": "최근 {x} 시간", "label.last-months": "최근 {x} 개월", + "label.last-seen": "Last seen", "label.leave": "떠나기", "label.leave-team": "팀 떠나기", "label.less-than": "미만", @@ -132,11 +134,14 @@ "label.pageTitle": "페이지 제목", "label.pages": "페이지", "label.password": "비밀번호", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "이 시스템은 {name}에서 구동되고 있습니다.", "label.previous": "이전", "label.previous-period": "이전 기간", "label.previous-year": "이전 연도", "label.profile": "프로필", + "label.properties": "Properties", "label.property": "속성", "label.queries": "쿼리", "label.query": "쿼리", @@ -156,6 +161,9 @@ "label.reset-website": "웹사이트 초기화", "label.retention": "리텐션", "label.retention-description": "사용자가 얼마나 자주 돌아오는지를 추적하여 웹사이트의 리텐션을 측정하십시오.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "역할", "label.run-query": "쿼리 실행", "label.save": "저장", @@ -165,6 +173,7 @@ "label.select-date": "날짜 선택", "label.select-role": "역할 선택", "label.select-website": "웹사이트 선택", + "label.session": "Session", "label.sessions": "세션", "label.settings": "설정", "label.share-url": "공유 URL", @@ -193,18 +202,21 @@ "label.total": "합계", "label.total-records": "총 레코드", "label.tracking-code": "추적 코드", + "label.transactions": "Transactions", "label.transfer": "전송", "label.transfer-website": "웹사이트 전송", "label.true": "참", "label.type": "유형", "label.unique": "고유", "label.unique-visitors": "순방문자(UV)", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "알 수 없음", "label.untitled": "제목 없음", "label.update": "업데이트", "label.url": "URL", "label.urls": "URL", "label.user": "사용자", + "label.user-property": "User Property", "label.username": "사용자명", "label.users": "사용자", "label.utm": "UTM", diff --git a/src/lang/lt-LT.json b/src/lang/lt-LT.json index 424c06ce3e..2e7776a373 100644 --- a/src/lang/lt-LT.json +++ b/src/lang/lt-LT.json @@ -1,7 +1,7 @@ { "label.access-code": "Prieigos kodas", "label.actions": "Veiksmai", - "label.activity-log": "Veiklos žurnalas", + "label.activity": "Veiklos žurnalas", "label.add": "Pridėti", "label.add-description": "Pridėti aprašymą", "label.add-member": "Pridėti narį", @@ -78,6 +78,7 @@ "label.filter-combined": "Kombinuoti", "label.filter-raw": "Neapdoroti", "label.filters": "Filtrai", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "{x, plural, =0 {Paskutinės # dienų} zero {Paskutinės # dienų} one {Paskutinė diena} other {Paskutinės # dienos}}", "label.last-hours": "{x, plural, =0 {Paskutinės # valandų} zero {Paskutinės # valandų} one {Paskutinė # valanda} other {Paskutinės # valandos}}", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Išeiti", "label.leave-team": "Išeiti iš komandos", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Puslapio pavadinimas", "label.pages": "Puslapiai", "label.password": "Slaptažodis", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Powered by {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Profilis", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Užklausos", "label.query": "Užklausa", @@ -156,6 +161,9 @@ "label.reset-website": "Atstatyti statistikos duomenis", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Rolė", "label.run-query": "Run query", "label.save": "Išsaugoti", @@ -165,6 +173,7 @@ "label.select-date": "Pasirinkti laikotarpį", "label.select-role": "Pasirinkti rolę", "label.select-website": "Pasirinkti svetainę", + "label.session": "Session", "label.sessions": "Sesijos", "label.settings": "Nustatymai", "label.share-url": "Pasidalinti nuoroda", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "Sekimo kodas", + "label.transactions": "Transactions", "label.transfer": "Perleisti", "label.transfer-website": "Perleisti svetainę", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Unikalūs lankytojai", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Nežinoma", "label.untitled": "Be pavadinimo", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "Vartotojas", + "label.user-property": "User Property", "label.username": "Vartotojo vardas", "label.users": "Vartotojai", "label.utm": "UTM", diff --git a/src/lang/mn-MN.json b/src/lang/mn-MN.json index 0e99817063..769c58a5be 100644 --- a/src/lang/mn-MN.json +++ b/src/lang/mn-MN.json @@ -1,7 +1,7 @@ { "label.access-code": "Хандалтын код", "label.actions": "Үйлдлүүд", - "label.activity-log": "Үйл ажиллагааны бүртгэл", + "label.activity": "Үйл ажиллагааны бүртгэл", "label.add": "Нэмэх", "label.add-description": "Тайлбар нэмэх", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Нэгтгэсэн", "label.filter-raw": "Түүхий", "label.filters": "Шүүлтүүр", + "label.first-seen": "First seen", "label.funnel": "Цутгал", "label.funnel-description": "Хэрэглэгчдийн шилжилт, уналтын хэмжээг шинжлэх.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Сүүлийн {x} хоног", "label.last-hours": "Сүүлийн {x} цаг", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Гарах", "label.leave-team": "Багаас гарах", "label.less-than": "Бага", @@ -132,11 +134,14 @@ "label.pageTitle": "Хуудасны гарчиг", "label.pages": "Хуудас", "label.password": "Нууц үг", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "{name} дээр суурилсан", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Бүртгэл", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Query-нүүд", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Тоон үзүүлэлтийг дахин эхлүүлэх", "label.retention": "Барилт", "label.retention-description": "Хэрэглэгчид таны веб рүү дахин хандах буюу хэрэглэгчдээ хэр тогтоож буйг хэмжих.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Эрх", "label.run-query": "Query ажиллуулах", "label.save": "Хадгалах", @@ -165,6 +173,7 @@ "label.select-date": "Огноо сонгох", "label.select-role": "Select role", "label.select-website": "Веб сонгох", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Тохиргоо", "label.share-url": "Хуваалцах холбоос", @@ -193,18 +202,21 @@ "label.total": "Нийт", "label.total-records": "Нийт мөрийн тоо", "label.tracking-code": "Мөрдөх код", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "Үнэн", "label.type": "Төрөл", "label.unique": "Давхардаагүй", "label.unique-visitors": "Зочин", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Тодорхойгүй", "label.untitled": "Гарчиггүй", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "Хэрэглэгч", + "label.user-property": "User Property", "label.username": "Хэрэглэгчийн нэр", "label.users": "Хэрэглэгчид", "label.utm": "UTM", diff --git a/src/lang/ms-MY.json b/src/lang/ms-MY.json index 82cfbb6b42..a86865731d 100644 --- a/src/lang/ms-MY.json +++ b/src/lang/ms-MY.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "Aksi", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Digabungkan", "label.filter-raw": "Mentah", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "{x} hari lepas", "label.last-hours": "{x} jam lepas", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "Halaman", "label.password": "Kata laluan", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Disediakan oleh {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Profil", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Reset statistics", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "Simpan", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Tetapan", "label.share-url": "Kongsikan URL", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "Kod penjejakan", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Pelawat unik", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Tidak diketahui", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "Nama pengguna", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/my-MM.json b/src/lang/my-MM.json index 8a71cd4974..3ab75f885e 100644 --- a/src/lang/my-MM.json +++ b/src/lang/my-MM.json @@ -1,7 +1,7 @@ { "label.access-code": "ဝင်ခွင့်ကုဒ်", "label.actions": "လုပ်ဆောင်ချက်များ", - "label.activity-log": "လုပ်ဆောင်ချက်စာရင်း", + "label.activity": "လုပ်ဆောင်ချက်စာရင်း", "label.add": "ထပ်ထည့်မည်", "label.add-description": "အကြောင်းအရာဖော်ပြချက် ထည့်မည်", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "ပေါင်းစပ်ပြီး", "label.filter-raw": "အရှိအတိုင်း", "label.filters": "Filter များ", + "label.first-seen": "First seen", "label.funnel": "ဖန်နယ်", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "လွန်ခဲ့သော {x} ရက်က", "label.last-hours": "လွန်ခဲ့သော {x} နာရီက", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "ထွက်မည်", "label.leave-team": "အသင်းမှထွက်မည်", "label.less-than": "ထက်ပို၍ငယ်သည်", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "စာမျက်နှာများ", "label.password": "စကားဝှက်", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "{name} ထောက်ပံ့သည်", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "ပရိုဖိုင်း", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries (ကွာရီများ)", "label.query": "Query (ကွာရီ)", @@ -156,6 +161,9 @@ "label.reset-website": "ဝက်ဘ်ဆိုဒ်ဒေတာကိုဖျက်မည်", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "အခန်းကဏ္ဍ", "label.run-query": "Query ကိုလုပ်ဆောင်မည်", "label.save": "သိမ်းဆည်းမည်", @@ -165,6 +173,7 @@ "label.select-date": "ရက်ရွေးပါ", "label.select-role": "Select role", "label.select-website": "ဝဘက်ဘ်ဆိုဒ်ရွေးပါ", + "label.session": "Session", "label.sessions": "ဆက်ရှင်များ", "label.settings": "ဆက်တင်များ", "label.share-url": "URL ကိုရှဲမည်", @@ -193,18 +202,21 @@ "label.total": "စုစုပေါင်း", "label.total-records": "မှတ်တမ်းစုစုပေါင်း", "label.tracking-code": "ထရက်လုပ်သည့် ကုဒ်", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "မှန်သည်", "label.type": "အမျိုးအစား", "label.unique": "Unique", "label.unique-visitors": "ဝင်ရောက်သူ (ထပ်ခြင်းမရှိ)", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "မသိသော", "label.untitled": "ခေါင်းစဉ်မရှိ", "label.update": "Update", "label.url": "URL", "label.urls": "URL များ", "label.user": "အသုံးပြုသူ", + "label.user-property": "User Property", "label.username": "အသုံးပြုသူအမည်", "label.users": "အသုံးပြုသူများ", "label.utm": "UTM", diff --git a/src/lang/nb-NO.json b/src/lang/nb-NO.json index 4a6e0409c1..55d211874d 100644 --- a/src/lang/nb-NO.json +++ b/src/lang/nb-NO.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "Handlinger", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Kombinert", "label.filter-raw": "Rå", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Siste {x} dager", "label.last-hours": "Siste {x} timer", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "Sider", "label.password": "Passord", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Drevet av {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Profil", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Nullstill statistikk", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "Lagre", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Innstillinger", "label.share-url": "Del URL", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "Sporingskode", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Unike besøkende", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Ukjent", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "Brukernavn", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/nl-NL.json b/src/lang/nl-NL.json index 9a1c852bc1..78b41df7d5 100644 --- a/src/lang/nl-NL.json +++ b/src/lang/nl-NL.json @@ -1,7 +1,7 @@ { "label.access-code": "Toegangscode", "label.actions": "Acties", - "label.activity-log": "Activiteiten logboek", + "label.activity": "Activiteiten logboek", "label.add": "Toevoegen", "label.add-description": "Omschrijving toevoegen", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Gecombineerd", "label.filter-raw": "Ruw", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Ontdek de conversie- en uitvalpercentages van gebruikers.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Laatste {x} dagen", "label.last-hours": "Laatste {x} uur", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Verlaten", "label.leave-team": "Verlaat team", "label.less-than": "Minder dan", @@ -132,11 +134,14 @@ "label.pageTitle": "Pagina titel", "label.pages": "Pagina's", "label.password": "Wachtwoord", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "mogelijk gemaakt door {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Profiel", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Parameters", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Statistieken opnieuw instellen", "label.retention": "Retentie", "label.retention-description": "Meet de retentie van je website door door bij te houden hoe vaak gebruikers terugkeren.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Gebruikersrol", "label.run-query": "Query uitvoeren", "label.save": "Opslaan", @@ -165,6 +173,7 @@ "label.select-date": "Datum selecteren", "label.select-role": "Rol selecteren", "label.select-website": "Website selecteren", + "label.session": "Session", "label.sessions": "Sessies", "label.settings": "Instellingen", "label.share-url": "URL delen", @@ -193,18 +202,21 @@ "label.total": "Totaal", "label.total-records": "Totaal records", "label.tracking-code": "Volgcode", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "Waar", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Unieke bezoekers", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Onbekend", "label.untitled": "Ongetiteld", "label.update": "Update", "label.url": "URL", "label.urls": "URL's", "label.user": "Gebruiker", + "label.user-property": "User Property", "label.username": "Gebruikersnaam", "label.users": "Gebruikers", "label.utm": "UTM", diff --git a/src/lang/pl-PL.json b/src/lang/pl-PL.json index 953438be23..697beeb35e 100644 --- a/src/lang/pl-PL.json +++ b/src/lang/pl-PL.json @@ -1,7 +1,7 @@ { "label.access-code": "Kod dostępu", "label.actions": "Działania", - "label.activity-log": "Dziennik aktywności", + "label.activity": "Dziennik aktywności", "label.add": "Dodaj", "label.add-description": "Dodaj opis", "label.add-member": "Dodaj członka", @@ -78,6 +78,7 @@ "label.filter-combined": "Połączone", "label.filter-raw": "Surowe dane", "label.filters": "Filtry", + "label.first-seen": "First seen", "label.funnel": "Lejek", "label.funnel-description": "Zrozum wskaźniki konwersji i odpływu użytkowników.", "label.goal": "Cel", @@ -103,6 +104,7 @@ "label.last-days": "Ostatnie {x} dni", "label.last-hours": "Ostatnie {x} godzin", "label.last-months": "Osatnie {x} miesięcy", + "label.last-seen": "Last seen", "label.leave": "Opuść", "label.leave-team": "Opuść zespół", "label.less-than": "Mniejsze niż", @@ -132,11 +134,14 @@ "label.pageTitle": "Tytuł strony", "label.pages": "Strony", "label.password": "Hasło", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Obsługiwane przez {name}", "label.previous": "Poprzedni", "label.previous-period": "Poprzedni okres", "label.previous-year": "Poprzedni rok", "label.profile": "Profil", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Zapytania", "label.query": "Zapytanie", @@ -156,6 +161,9 @@ "label.reset-website": "Zresetuj statystyki", "label.retention": "Retencja", "label.retention-description": "Mierz przyciągającą siłę swojej strony internetowej, śledząc, jak często użytkownicy powracają.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Rola", "label.run-query": "Uruchom zapytanie", "label.save": "Zapisz", @@ -165,6 +173,7 @@ "label.select-date": "Wybierz datę", "label.select-role": "Wybierz rolę", "label.select-website": "Wybierz witrynę", + "label.session": "Session", "label.sessions": "Sesje", "label.settings": "Ustawienia", "label.share-url": "Udostępnij adres URL", @@ -193,18 +202,21 @@ "label.total": "W sumie", "label.total-records": "Suma rekordów", "label.tracking-code": "Kod śledzenia", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "Prawda", "label.type": "Typ", "label.unique": "Unikalne", "label.unique-visitors": "Unikalni odwiedzający", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Nieznany", "label.untitled": "Bez tytułu", "label.update": "Aktualizuj", "label.url": "Link", "label.urls": "Linki", "label.user": "Użytkownik", + "label.user-property": "User Property", "label.username": "Nazwa użytkownika", "label.users": "Użytkownicy", "label.utm": "UTM", diff --git a/src/lang/pt-BR.json b/src/lang/pt-BR.json index be96d09772..6e5b095ed6 100644 --- a/src/lang/pt-BR.json +++ b/src/lang/pt-BR.json @@ -1,7 +1,7 @@ { "label.access-code": "Código de acesso", "label.actions": "Ações do usuário", - "label.activity-log": "Registro de atividades", + "label.activity": "Registro de atividades", "label.add": "Adicionar", "label.add-description": "Adicionar descrição", "label.add-member": "Adicionar membro", @@ -78,6 +78,7 @@ "label.filter-combined": "Combinado", "label.filter-raw": "Bruto", "label.filters": "Filtros", + "label.first-seen": "First seen", "label.funnel": "Funil", "label.funnel-description": "Entenda a taxa de conversão e abandono dos seus usuários.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Últimos {x} dias", "label.last-hours": "Últimas {x} horas", "label.last-months": "Últimos {x} meses", + "label.last-seen": "Last seen", "label.leave": "Sair", "label.leave-team": "Sair da equipe", "label.less-than": "Menor que", @@ -132,11 +134,14 @@ "label.pageTitle": "Título", "label.pages": "Páginas", "label.password": "Senha", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Desenvolvido por {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Perfil", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Consultas", "label.query": "Consulta", @@ -156,6 +161,9 @@ "label.reset-website": "Redefinir dados", "label.retention": "Retenção", "label.retention-description": "Avalie a fidelidade dos seus usuários medindo a frequência com que eles retornam.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Função", "label.run-query": "Executar consulta", "label.save": "Salvar", @@ -165,6 +173,7 @@ "label.select-date": "Selecionar data", "label.select-role": "Selecionar função", "label.select-website": "Selecionar site", + "label.session": "Session", "label.sessions": "Sessões", "label.settings": "Configurações", "label.share-url": "Link para compartilhar", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total de registros", "label.tracking-code": "Código de rastreamento", + "label.transactions": "Transactions", "label.transfer": "Transferir", "label.transfer-website": "Transferir site", "label.true": "Sim", "label.type": "Tipo", "label.unique": "Únicos", "label.unique-visitors": "Visitantes únicos", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Desconhecido", "label.untitled": "Sem título", "label.update": "Atualizar", "label.url": "URL", "label.urls": "URLs", "label.user": "Usuário", + "label.user-property": "User Property", "label.username": "Nome de usuário", "label.users": "Usuários", "label.utm": "UTM", diff --git a/src/lang/pt-PT.json b/src/lang/pt-PT.json index 252457f833..ddc6b72616 100644 --- a/src/lang/pt-PT.json +++ b/src/lang/pt-PT.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "Ações", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Combinado", "label.filter-raw": "Dados brutos", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Últimos {x} dias", "label.last-hours": "Últimas {x} horas", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "Páginas", "label.password": "Senha", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Distribuído por {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Perfil", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Repor estatísticas", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "Guardar", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Definições", "label.share-url": "Partilhar link", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "Código de rastreamento", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Visitantes únicos", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Desconhecido", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "Nome de utilizador", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/ro-RO.json b/src/lang/ro-RO.json index c06690c23d..f60f8f2baa 100644 --- a/src/lang/ro-RO.json +++ b/src/lang/ro-RO.json @@ -1,7 +1,7 @@ { "label.access-code": "Cod de access", "label.actions": "Acțiuni", - "label.activity-log": "Jurnal de activități", + "label.activity": "Jurnal de activități", "label.add": "Adaugă", "label.add-description": "Adaugă descriere", "label.add-member": "Adaugă membru", @@ -78,6 +78,7 @@ "label.filter-combined": "Combinat", "label.filter-raw": "Brut", "label.filters": "Filtre", + "label.first-seen": "First seen", "label.funnel": "Parcursul utilizatorului", "label.funnel-description": "Înțelege rata de conversie și rata de abandon a utilizatorilor.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Ultimele {x} zile", "label.last-hours": "Ultimele {x} ore", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Părăsește", "label.leave-team": "Părăsește echipa", "label.less-than": "Mai puțin decât", @@ -132,11 +134,14 @@ "label.pageTitle": "Titlul paginii", "label.pages": "Pagini", "label.password": "Parolă", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Cu sprijinul {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Profil", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Interogări", "label.query": "Interogare", @@ -156,6 +161,9 @@ "label.reset-website": "Resetează statisticile pentru site", "label.retention": "Retenție", "label.retention-description": "Măsoară atractivitatea site-ului tău prin urmărirea frecvenței cu care utilizatorii se întorc.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Rol", "label.run-query": "Execută interogarea", "label.save": "Salvează", @@ -165,6 +173,7 @@ "label.select-date": "Selectează data", "label.select-role": "Selectează rolul", "label.select-website": "Selectează website", + "label.session": "Session", "label.sessions": "Sesiuni", "label.settings": "Setări", "label.share-url": "Partajare URL", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total înregistrări", "label.tracking-code": "Cod de urmărire", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "Adevărat", "label.type": "Tip", "label.unique": "Unici", "label.unique-visitors": "Vizitatori unici", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Necunoscut", "label.untitled": "Fără titlu", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "Utilizator", + "label.user-property": "User Property", "label.username": "Nume utilizator", "label.users": "Utilizatori", "label.utm": "UTM", diff --git a/src/lang/ru-RU.json b/src/lang/ru-RU.json index 257c0491aa..f483522a97 100644 --- a/src/lang/ru-RU.json +++ b/src/lang/ru-RU.json @@ -1,7 +1,7 @@ { "label.access-code": "Код доступа", "label.actions": "Действия", - "label.activity-log": "Журнал активности", + "label.activity": "Журнал активности", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Объединенные", "label.filter-raw": "Сырые данные", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Последние {x} дней", "label.last-hours": "Последние {x} часа", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Уйти", "label.leave-team": "Покинуть команду", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "Страницы", "label.password": "Пароль", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "На движке {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Профиль", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Запросы", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Сбросить статистику", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Роль", "label.run-query": "Run query", "label.save": "Сохранить", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Выбрать сайт", + "label.session": "Session", "label.sessions": "Сессии", "label.settings": "Настройки", "label.share-url": "Поделиться ссылкой", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "Код отслеживания", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Уникальные посетители", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Неизвестно", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "Пользователь", + "label.user-property": "User Property", "label.username": "Имя пользователя", "label.users": "Пользователи", "label.utm": "UTM", diff --git a/src/lang/si-LK.json b/src/lang/si-LK.json index 6263f91a96..46dab6b304 100644 --- a/src/lang/si-LK.json +++ b/src/lang/si-LK.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "Actions", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Combined", "label.filter-raw": "Raw", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "අන්තිම {x} දින", "label.last-hours": "අන්තිම {x} පැය", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "Pages", "label.password": "මුරපදය", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Powered by {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "පැතිකඩ", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "සංඛ්යා ලේඛන නැවත සකසන්න", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "සුරකින්න", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "සැකසුම්", "label.share-url": "බෙදාගැනීමේ URL", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "ලුහුබැඳීමේ කේතය", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Unique visitors", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "නොදනී", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "පරිශීලක නාමය", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/sk-SK.json b/src/lang/sk-SK.json index 4447ada298..2a02b5c744 100644 --- a/src/lang/sk-SK.json +++ b/src/lang/sk-SK.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "Akcie", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Kombinácie", "label.filter-raw": "Nezpracované", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Posledných {x} dní", "label.last-hours": "Posledných {x} hodín", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "Stránky", "label.password": "Heslo", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Powered by {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Profil", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Reset statistics", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "Uložiť", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Nastavenia", "label.share-url": "Zdielanie URL", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "Sledovací kód", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Jedinečné návštevy", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Neznámý", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "Užívateľské meno", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/sl-SI.json b/src/lang/sl-SI.json index 3c684dec03..bd180bfb9b 100644 --- a/src/lang/sl-SI.json +++ b/src/lang/sl-SI.json @@ -1,7 +1,7 @@ { "label.access-code": "Koda za dostop", "label.actions": "Dejanja", - "label.activity-log": "Dnevnik dejavnosti", + "label.activity": "Dnevnik dejavnosti", "label.add": "Dodaj", "label.add-description": "Dodaj opis", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Skupaj", "label.filter-raw": "Neobdelano", "label.filters": "Filtri", + "label.first-seen": "First seen", "label.funnel": "Prodajni lijak", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Zadnjih {x} dni", "label.last-hours": "Zadnjih {x} ur", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Zapusti", "label.leave-team": "Zapusti ekipo", "label.less-than": "Manjše kot", @@ -132,11 +134,14 @@ "label.pageTitle": "Naslov strani", "label.pages": "Strani", "label.password": "Geslo", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Poganja {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Profil", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Poizvedbe", "label.query": "Poizvedba", @@ -156,6 +161,9 @@ "label.reset-website": "Ponastavi statistiko", "label.retention": "Ohranjanje uporabnikov", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Vloga", "label.run-query": "Izvedi poizvedbo", "label.save": "Shrani", @@ -165,6 +173,7 @@ "label.select-date": "Izberi datum", "label.select-role": "Select role", "label.select-website": "Izberi spletno mesto", + "label.session": "Session", "label.sessions": "Seje", "label.settings": "Nastavitve", "label.share-url": "Deli povezavo", @@ -193,18 +202,21 @@ "label.total": "Skupaj", "label.total-records": "Skupni zapisi", "label.tracking-code": "Koda za sledenje", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "Pravilno", "label.type": "Vrsta", "label.unique": "Unikatni", "label.unique-visitors": "Unikatni obiskovalci", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Neznano", "label.untitled": "Brez naslova", "label.update": "Update", "label.url": "Povezava", "label.urls": "Povezave", "label.user": "Uporabnik", + "label.user-property": "User Property", "label.username": "Uporabniško ime", "label.users": "Uporabniki", "label.utm": "UTM", diff --git a/src/lang/sv-SE.json b/src/lang/sv-SE.json index c60b6cf904..cdaa676af4 100644 --- a/src/lang/sv-SE.json +++ b/src/lang/sv-SE.json @@ -1,7 +1,7 @@ { "label.access-code": "Åtkomstkod", "label.actions": "Händelser", - "label.activity-log": "Aktivitetslogg", + "label.activity": "Aktivitetslogg", "label.add": "Lägg till", "label.add-description": "Lägg till beskrivning", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Kombinerade", "label.filter-raw": "Rådata", "label.filters": "Filter", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Förstå omvandlingen och bortfallsfrekvensen för användare.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Senaste {x} dagarna", "label.last-hours": "Senaste {x} timmarna", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Lämna", "label.leave-team": "Lämna team", "label.less-than": "Mindre än", @@ -132,11 +134,14 @@ "label.pageTitle": "Sidtitel", "label.pages": "Sidor", "label.password": "Lösenord", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Drivs av {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Profil", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Frågor", "label.query": "Fråga", @@ -156,6 +161,9 @@ "label.reset-website": "Återställ webbplats", "label.retention": "Retention", "label.retention-description": "Mät din webbplats engagemang genom att följa hur ofta användare återvänder.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Roll", "label.run-query": "Kör sökning", "label.save": "Spara", @@ -165,6 +173,7 @@ "label.select-date": "Välj datum", "label.select-role": "Select role", "label.select-website": "Välj webbplats", + "label.session": "Session", "label.sessions": "Sessioner", "label.settings": "Inställningar", "label.share-url": "Delningslänk", @@ -193,18 +202,21 @@ "label.total": "Totalt", "label.total-records": "Totala poster", "label.tracking-code": "Spårningskod", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "Sant", "label.type": "Typ", "label.unique": "Unikt", "label.unique-visitors": "Unika besökare", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Okänt", "label.untitled": "Namnlös", "label.update": "Update", "label.url": "Länk", "label.urls": "Länkar", "label.user": "Användare", + "label.user-property": "User Property", "label.username": "Användarnamn", "label.users": "Användare", "label.utm": "UTM", diff --git a/src/lang/ta-IN.json b/src/lang/ta-IN.json index 0f95bc1861..1e4466b52c 100644 --- a/src/lang/ta-IN.json +++ b/src/lang/ta-IN.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "செயல்கள்", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "ஒருங்கிணைந்த", "label.filter-raw": "மூல", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "முந்தைய {x} நாட்கள்", "label.last-hours": "முந்தைய {x} மணி", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "பக்கங்கள்", "label.password": "கடவுச்சொல்", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "{name} ஆல் இயக்கப்படுகிறது", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "சுயவிவரம்", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Reset statistics", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "சேமி", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "அமைப்புகள்", "label.share-url": "வலைத்தள களத்தைப் பகிரவும்", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "கண்காணிப்பு குறியீடு", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "தனிப்பட்ட பார்வையாளர்கள்", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "தெரியாத", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "பயனர்பெயர்", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/th-TH.json b/src/lang/th-TH.json index a594bb3120..7cc3504506 100644 --- a/src/lang/th-TH.json +++ b/src/lang/th-TH.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "การกระทำ", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "ข้อมูลรวม", "label.filter-raw": "ข้อมูลดิบ", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "{x} วันที่ผ่านมา", "label.last-hours": "{x} ชั่วโมงที่ผ่านมา", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "หน้าเพจ", "label.password": "รหัสผ่าน", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "ขับเคลื่อนโดย {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "โปรไฟล์", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "รีเซตข้อมูลสถิติ", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "บันทึก", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "ตั้งค่า", "label.share-url": "แชร์ลิงก์", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "โค้ดสำหรับใช้ติดตาม", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "ผู้เข้าชม", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "ไม่รู้จัก", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "ชื่อผู้ใช้", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/tr-TR.json b/src/lang/tr-TR.json index b8271ed219..34a81f75cf 100644 --- a/src/lang/tr-TR.json +++ b/src/lang/tr-TR.json @@ -1,7 +1,7 @@ { "label.access-code": "Erişim Kodu", "label.actions": "Hareketler", - "label.activity-log": "Aktivite Kaydı", + "label.activity": "Aktivite Kaydı", "label.add": "Ekle", "label.add-description": "Açıklama ekle", "label.add-member": "Üye ekle", @@ -78,6 +78,7 @@ "label.filter-combined": "Birleşik filtre", "label.filter-raw": "Ham filtre", "label.filters": "Filtreler", + "label.first-seen": "First seen", "label.funnel": "Huni", "label.funnel-description": "Kullanıcıların dönüşüm ve ayrılma oranlarını anlayın.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "Son {x} gün", "label.last-hours": "Son {x} saat", "label.last-months": "Son {x} ay", + "label.last-seen": "Last seen", "label.leave": "Ayrıl", "label.leave-team": "Takımdan Ayrıl", "label.less-than": "Küçüktür", @@ -132,11 +134,14 @@ "label.pageTitle": "Sayfa başlığı", "label.pages": "Sayfalar", "label.password": "Parola", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Sağlayıcı: {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Profil", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Sorgular", "label.query": "Sorgu", @@ -156,6 +161,9 @@ "label.reset-website": "İstatistikleri sıfırla", "label.retention": "Geri dönüş", "label.retention-description": "Kullanıcıların ne sıklıkla geri döndüğünü takip ederek web sitenizin kalıcılığını ölçün.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Rol", "label.run-query": "Sorgu çalıştır", "label.save": "Kaydet", @@ -165,6 +173,7 @@ "label.select-date": "Tarih seç", "label.select-role": "Rol seç", "label.select-website": "Web sitesi seç", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Ayarlar", "label.share-url": "Paylaşım adresi", @@ -193,18 +202,21 @@ "label.total": "Toplam", "label.total-records": "Toplam kayıt", "label.tracking-code": "İzleme kodu", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer web sitesi", "label.true": "Doğru", "label.type": "Tip", "label.unique": "Benzersiz", "label.unique-visitors": "Tekil kullanıcı", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Bilinmeyen", "label.untitled": "İsimsiz", "label.update": "Güncelle", "label.url": "URL", "label.urls": "URLs", "label.user": "Kullanıcı", + "label.user-property": "User Property", "label.username": "Kullanıcı adı", "label.users": "Kullanıcılar", "label.utm": "UTM", diff --git a/src/lang/uk-UA.json b/src/lang/uk-UA.json index ecfd27f50a..c6843055f2 100644 --- a/src/lang/uk-UA.json +++ b/src/lang/uk-UA.json @@ -1,7 +1,7 @@ { "label.access-code": "Код доступу", "label.actions": "Дії", - "label.activity-log": "Журнал", + "label.activity": "Журнал", "label.add": "Додати", "label.add-description": "Додати опис", "label.add-member": "Додати учасника", @@ -62,6 +62,9 @@ "label.domain": "Домен", "label.dropoff": "Відсів", "label.edit": "Редагувати", + "label.edit-dashboard": "Edit dashboard", + "label.edit-member": "Edit member", + "label.enable-share-url": "Enable share URL", "label.end-step": "End Step", "label.entry": "Entry URL", "label.event": "Подія", @@ -75,6 +78,7 @@ "label.filter-combined": "Об'єднані", "label.filter-raw": "Сирі дані", "label.filters": "Фільтри", + "label.first-seen": "First seen", "label.funnel": "Воронка", "label.funnel-description": "Зрозуміти рівень конверсії та відсіву користувачів.", "label.goal": "Goal", @@ -100,6 +104,7 @@ "label.last-days": "Останні {x} днів", "label.last-hours": "Останні {x} годин", "label.last-months": "Останні {x} місяців", + "label.last-seen": "Last seen", "label.leave": "Покинути", "label.leave-team": "Покинути команду", "label.less-than": "Менше ніж", @@ -107,6 +112,7 @@ "label.login": "Увійти", "label.logout": "Вийти", "label.manage": "Керувати", + "label.manager": "Manager", "label.max": "Макс.", "label.member": "Учасник", "label.members": "Учасники", @@ -128,11 +134,14 @@ "label.pageTitle": "Заголовок сторінки", "label.pages": "Сторінки", "label.password": "Пароль", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "На базі {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Профіль", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Запити", "label.query": "Запит", @@ -152,6 +161,9 @@ "label.reset-website": "Скинути статистику сайту", "label.retention": "Липкість", "label.retention-description": "Виміряйте липкість вашого сайту, відстежуючи, як часто користувачі повертаються на нього.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Роль", "label.run-query": "Виконати запит", "label.save": "Зберегти", @@ -161,6 +173,7 @@ "label.select-date": "Вибрати дату", "label.select-role": "Вибрати роль", "label.select-website": "Вибрати сайт", + "label.session": "Session", "label.sessions": "Сесії", "label.settings": "Налаштування", "label.share-url": "Поділитися посилання", @@ -171,6 +184,7 @@ "label.tablet": "Планшет", "label.team": "Команда", "label.team-id": "Ідентифікатор команди", + "label.team-manager": "Team manager", "label.team-member": "Учасник команди", "label.team-name": "Назва команди", "label.team-owner": "Власник команди", @@ -188,18 +202,21 @@ "label.total": "Всього", "label.total-records": "Всього записів", "label.tracking-code": "Код для відслідковування", + "label.transactions": "Transactions", "label.transfer": "Передати", "label.transfer-website": "Передати сайт", "label.true": "True", "label.type": "Тип", "label.unique": "Унікальний", "label.unique-visitors": "Унікальні відвідувачі", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Невідомо", "label.untitled": "Без заголовку", "label.update": "Оновлення", "label.url": "URL", "label.urls": "URLs", "label.user": "Користувач", + "label.user-property": "User Property", "label.username": "Ім'я користувача", "label.users": "Користувачі", "label.utm": "UTM", @@ -210,6 +227,7 @@ "label.view-only": "Тільки для перегляду", "label.views": "Перегляди", "label.views-per-visit": "Перегляди за одне відвідування", + "label.visit-duration": "Visit duration", "label.visitors": "Відвідувачі", "label.visits": "Відвідування", "label.website": "Сайт", diff --git a/src/lang/ur-PK.json b/src/lang/ur-PK.json index 97de5d6940..138ec1b3eb 100644 --- a/src/lang/ur-PK.json +++ b/src/lang/ur-PK.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "اعمال", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "مشترکہ", "label.filter-raw": "خام", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "پچھلے {x} دن", "label.last-hours": "پچھلے {x} گھنٹے", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "صفحات", "label.password": "پاس ورڈ", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "تقویت یافتہ بذریعہ {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "پروفائل", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "اعدادوشمار کو دوبارہ ترتیب دیں", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "محفوظ کریں", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "ترتیبات", "label.share-url": "URL کا اشتراک کریں", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "ٹریکنگ کوڈ", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "منفرد زائرین", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "نامعلوم", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "صارف نام", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/vi-VN.json b/src/lang/vi-VN.json index 054f449a8a..22a4eaf72a 100644 --- a/src/lang/vi-VN.json +++ b/src/lang/vi-VN.json @@ -1,7 +1,7 @@ { "label.access-code": "Access code", "label.actions": "Hành động", - "label.activity-log": "Activity log", + "label.activity": "Activity log", "label.add": "Add", "label.add-description": "Add description", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "Kết hợp", "label.filter-raw": "Gốc", "label.filters": "Filters", + "label.first-seen": "First seen", "label.funnel": "Funnel", "label.funnel-description": "Understand the conversion and drop-off rate of users.", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "{x} ngày gần nhất", "label.last-hours": "{x} giờ gần nhất", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "Leave", "label.leave-team": "Leave team", "label.less-than": "Less than", @@ -132,11 +134,14 @@ "label.pageTitle": "Page title", "label.pages": "Trang", "label.password": "Mật khẩu", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "Bản quyền thuộc về {name}", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "Hồ sơ", + "label.properties": "Properties", "label.property": "Property", "label.queries": "Queries", "label.query": "Query", @@ -156,6 +161,9 @@ "label.reset-website": "Tái thiết lập thống kê", "label.retention": "Retention", "label.retention-description": "Measure your website stickiness by tracking how often users return.", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "Role", "label.run-query": "Run query", "label.save": "Lưu", @@ -165,6 +173,7 @@ "label.select-date": "Select date", "label.select-role": "Select role", "label.select-website": "Select website", + "label.session": "Session", "label.sessions": "Sessions", "label.settings": "Cài đặt", "label.share-url": "Chia sẻ URL", @@ -193,18 +202,21 @@ "label.total": "Total", "label.total-records": "Total records", "label.tracking-code": "Mã theo dõi", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "True", "label.type": "Type", "label.unique": "Unique", "label.unique-visitors": "Khách truy cập một lần", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "Không rõ", "label.untitled": "Untitled", "label.update": "Update", "label.url": "URL", "label.urls": "URLs", "label.user": "User", + "label.user-property": "User Property", "label.username": "Tên đăng nhập", "label.users": "Users", "label.utm": "UTM", diff --git a/src/lang/zh-CN.json b/src/lang/zh-CN.json index 64dfa12fd3..741d2de0ec 100644 --- a/src/lang/zh-CN.json +++ b/src/lang/zh-CN.json @@ -1,7 +1,7 @@ { "label.access-code": "访问代码", "label.actions": "用户行为", - "label.activity-log": "活动日志", + "label.activity": "活动日志", "label.add": "添加", "label.add-description": "添加描述", "label.add-member": "添加成员", @@ -78,6 +78,7 @@ "label.filter-combined": "合并", "label.filter-raw": "原始", "label.filters": "筛选", + "label.first-seen": "First seen", "label.funnel": "分析", "label.funnel-description": "了解用户的转换率和退出率。", "label.goal": "目标", @@ -103,6 +104,7 @@ "label.last-days": "最近 {x} 天", "label.last-hours": "最近 {x} 小时", "label.last-months": "最近 {x} 个月", + "label.last-seen": "Last seen", "label.leave": "离开", "label.leave-team": "离开团队", "label.less-than": "少于", @@ -132,11 +134,14 @@ "label.pageTitle": "标题", "label.pages": "网页", "label.password": "密码", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "由 {name} 提供支持", "label.previous": "先前", "label.previous-period": "上一时期", "label.previous-year": "上一年", "label.profile": "个人资料", + "label.properties": "Properties", "label.property": "属性", "label.queries": "查询", "label.query": "查询", @@ -156,6 +161,9 @@ "label.reset-website": "重置统计数据", "label.retention": "保留", "label.retention-description": "通过跟踪用户返回的频率来衡量网站的用户粘性。", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "角色", "label.run-query": "查询", "label.save": "保存", @@ -165,6 +173,7 @@ "label.select-date": "选择数据", "label.select-role": "选择角色", "label.select-website": "选择网站", + "label.session": "Session", "label.sessions": "会话", "label.settings": "设置", "label.share-url": "共享链接", @@ -193,18 +202,21 @@ "label.total": "总数", "label.total-records": "总记录数", "label.tracking-code": "跟踪代码", + "label.transactions": "Transactions", "label.transfer": "转移", "label.transfer-website": "转移网站", "label.true": "是", "label.type": "类型", "label.unique": "独立", "label.unique-visitors": "独立访客", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "未知", "label.untitled": "未命名", "label.update": "更新", "label.url": "网址", "label.urls": "网址", "label.user": "用户", + "label.user-property": "User Property", "label.username": "用户名", "label.users": "用户", "label.utm": "UTM", diff --git a/src/lang/zh-TW.json b/src/lang/zh-TW.json index 10069181e3..a2c5d2c31b 100644 --- a/src/lang/zh-TW.json +++ b/src/lang/zh-TW.json @@ -1,7 +1,7 @@ { "label.access-code": "存取碼", "label.actions": "行動", - "label.activity-log": "活動日誌", + "label.activity": "活動日誌", "label.add": "新增", "label.add-description": "新增描述", "label.add-member": "Add member", @@ -78,6 +78,7 @@ "label.filter-combined": "組合", "label.filter-raw": "原始", "label.filters": "篩選器", + "label.first-seen": "First seen", "label.funnel": "漏斗", "label.funnel-description": "瞭解使用者的轉換率和退出率", "label.goal": "Goal", @@ -103,6 +104,7 @@ "label.last-days": "最近 {x} 天", "label.last-hours": "最近 {x} 小時", "label.last-months": "Last {x} months", + "label.last-seen": "Last seen", "label.leave": "離開", "label.leave-team": "離開團隊", "label.less-than": "小於", @@ -132,11 +134,14 @@ "label.pageTitle": "頁面標題", "label.pages": "頁面", "label.password": "密碼", + "label.path": "Path", + "label.paths": "Paths", "label.powered-by": "由 {name} 提供", "label.previous": "Previous", "label.previous-period": "Previous period", "label.previous-year": "Previous year", "label.profile": "個人資料", + "label.properties": "Properties", "label.property": "Property", "label.queries": "查詢", "label.query": "查詢", @@ -156,6 +161,9 @@ "label.reset-website": "重設網站", "label.retention": "保留", "label.retention-description": "透過追蹤使用者回訪的頻率來衡量您的網站黏著度。", + "label.revenue": "Revenue", + "label.revenue-description": "Look into your revenue across time.", + "label.revenue-property": "Revenue Property", "label.role": "角色", "label.run-query": "執行查詢", "label.save": "儲存", @@ -165,6 +173,7 @@ "label.select-date": "選擇日期", "label.select-role": "Select role", "label.select-website": "選擇網站", + "label.session": "Session", "label.sessions": "工作階段", "label.settings": "設定", "label.share-url": "分享網址", @@ -193,18 +202,21 @@ "label.total": "總計", "label.total-records": "總記錄", "label.tracking-code": "追蹤代碼", + "label.transactions": "Transactions", "label.transfer": "Transfer", "label.transfer-website": "Transfer website", "label.true": "是", "label.type": "類型", "label.unique": "獨立", "label.unique-visitors": "獨立訪客", + "label.uniqueCustomers": "Unique Customers", "label.unknown": "未知", "label.untitled": "無標題", "label.update": "Update", "label.url": "網址", "label.urls": "網址", "label.user": "使用者", + "label.user-property": "User Property", "label.username": "使用者名稱", "label.users": "使用者", "label.utm": "UTM", diff --git a/src/lib/types.ts b/src/lib/types.ts index 3bce527151..743761d783 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -125,9 +125,9 @@ export interface WebsiteEventMetric { export interface WebsiteEventData { eventName?: string; - fieldName: string; + propertyName: string; dataType: number; - fieldValue?: string; + propertyValue?: string; total: number; } diff --git a/src/pages/api/events/[eventId]/data.ts b/src/pages/api/websites/[websiteId]/event-data/events.ts similarity index 100% rename from src/pages/api/events/[eventId]/data.ts rename to src/pages/api/websites/[websiteId]/event-data/events.ts diff --git a/src/pages/api/websites/[websiteId]/event-data/properties.ts b/src/pages/api/websites/[websiteId]/event-data/properties.ts new file mode 100644 index 0000000000..19e9bbb892 --- /dev/null +++ b/src/pages/api/websites/[websiteId]/event-data/properties.ts @@ -0,0 +1,49 @@ +import { canViewWebsite } from 'lib/auth'; +import { useAuth, useCors, useValidate } from 'lib/middleware'; +import { NextApiRequestQueryBody } from 'lib/types'; +import { NextApiResponse } from 'next'; +import { methodNotAllowed, ok, unauthorized } from 'next-basics'; +import { getEventDataProperties } from 'queries'; +import * as yup from 'yup'; + +export interface EventDataFieldsRequestQuery { + websiteId: string; + startAt: string; + endAt: string; + propertyName?: string; +} + +const schema = { + GET: yup.object().shape({ + websiteId: yup.string().uuid().required(), + startAt: yup.number().integer().required(), + endAt: yup.number().integer().min(yup.ref('startAt')).required(), + propertyName: yup.string(), + }), +}; + +export default async ( + req: NextApiRequestQueryBody, + res: NextApiResponse, +) => { + await useCors(req, res); + await useAuth(req, res); + await useValidate(schema, req, res); + + if (req.method === 'GET') { + const { websiteId, startAt, endAt, propertyName } = req.query; + + if (!(await canViewWebsite(req.auth, websiteId))) { + return unauthorized(res); + } + + const startDate = new Date(+startAt); + const endDate = new Date(+endAt); + + const data = await getEventDataProperties(websiteId, { startDate, endDate, propertyName }); + + return ok(res, data); + } + + return methodNotAllowed(res); +}; diff --git a/src/pages/api/event-data/fields.ts b/src/pages/api/websites/[websiteId]/event-data/values.ts similarity index 80% rename from src/pages/api/event-data/fields.ts rename to src/pages/api/websites/[websiteId]/event-data/values.ts index 4353ee7303..b9ada96df5 100644 --- a/src/pages/api/event-data/fields.ts +++ b/src/pages/api/websites/[websiteId]/event-data/values.ts @@ -3,14 +3,15 @@ import { useAuth, useCors, useValidate } from 'lib/middleware'; import { NextApiRequestQueryBody } from 'lib/types'; import { NextApiResponse } from 'next'; import { methodNotAllowed, ok, unauthorized } from 'next-basics'; -import { getEventDataFields } from 'queries'; +import { getEventDataValues } from 'queries'; + import * as yup from 'yup'; export interface EventDataFieldsRequestQuery { websiteId: string; startAt: string; endAt: string; - field?: string; + propertyName?: string; } const schema = { @@ -18,7 +19,7 @@ const schema = { websiteId: yup.string().uuid().required(), startAt: yup.number().integer().required(), endAt: yup.number().integer().min(yup.ref('startAt')).required(), - field: yup.string(), + propertyName: yup.string(), }), }; @@ -31,7 +32,7 @@ export default async ( await useValidate(schema, req, res); if (req.method === 'GET') { - const { websiteId, startAt, endAt, field } = req.query; + const { websiteId, startAt, endAt, propertyName } = req.query; if (!(await canViewWebsite(req.auth, websiteId))) { return unauthorized(res); @@ -40,7 +41,7 @@ export default async ( const startDate = new Date(+startAt); const endDate = new Date(+endAt); - const data = await getEventDataFields(websiteId, { startDate, endDate, field }); + const data = await getEventDataValues(websiteId, { startDate, endDate, propertyName }); return ok(res, data); } diff --git a/src/pages/api/event-data/stats.ts b/src/pages/api/websites/[websiteId]/events/stats.ts similarity index 100% rename from src/pages/api/event-data/stats.ts rename to src/pages/api/websites/[websiteId]/events/stats.ts diff --git a/src/pages/api/sessions/[sessionId]/data.ts b/src/pages/api/websites/[websiteId]/sessions/[sessionId]/properties.ts similarity index 100% rename from src/pages/api/sessions/[sessionId]/data.ts rename to src/pages/api/websites/[websiteId]/sessions/[sessionId]/properties.ts diff --git a/src/queries/analytics/events/getEventDataEvents.ts b/src/queries/analytics/events/getEventDataEvents.ts index e5647debd0..6b57330c59 100644 --- a/src/queries/analytics/events/getEventDataEvents.ts +++ b/src/queries/analytics/events/getEventDataEvents.ts @@ -22,9 +22,9 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { ` select website_event.event_name as "eventName", - event_data.data_key as "fieldName", + event_data.data_key as "propertyName", event_data.data_type as "dataType", - event_data.string_value as "fieldValue", + event_data.string_value as "propertyValue", count(*) as "total" from event_data inner join website_event @@ -43,7 +43,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { ` select website_event.event_name as "eventName", - event_data.data_key as "fieldName", + event_data.data_key as "propertyName", event_data.data_type as "dataType", count(*) as "total" from event_data @@ -62,7 +62,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { async function clickhouseQuery( websiteId: string, filters: QueryFilters, -): Promise<{ eventName: string; fieldName: string; dataType: number; total: number }[]> { +): Promise<{ eventName: string; propertyName: string; dataType: number; total: number }[]> { const { rawQuery, parseFilters } = clickhouse; const { event } = filters; const { params } = await parseFilters(websiteId, filters); @@ -72,9 +72,9 @@ async function clickhouseQuery( ` select event_name as eventName, - data_key as fieldName, + data_key as propertyName, data_type as dataType, - string_value as fieldValue, + string_value as propertyValue, count(*) as total from event_data where website_id = {websiteId:UUID} @@ -89,9 +89,9 @@ async function clickhouseQuery( return Object.values(result).map((a: any) => { return { eventName: a.eventName, - fieldName: a.fieldName, + propertyName: a.propertyName, dataType: Number(a.dataType), - fieldValue: a.fieldValue, + propertyValue: a.propertyValue, total: Number(a.total), }; }); @@ -102,7 +102,7 @@ async function clickhouseQuery( ` select event_name as eventName, - data_key as fieldName, + data_key as propertyName, data_type as dataType, count(*) as total from event_data @@ -117,7 +117,7 @@ async function clickhouseQuery( return Object.values(result).map((a: any) => { return { eventName: a.eventName, - fieldName: a.fieldName, + propertyName: a.propertyName, dataType: Number(a.dataType), total: Number(a.total), }; diff --git a/src/queries/analytics/events/getEventDataFields.ts b/src/queries/analytics/events/getEventDataProperties.ts similarity index 61% rename from src/queries/analytics/events/getEventDataFields.ts rename to src/queries/analytics/events/getEventDataProperties.ts index f669ad39f6..78fcb2cda3 100644 --- a/src/queries/analytics/events/getEventDataFields.ts +++ b/src/queries/analytics/events/getEventDataProperties.ts @@ -3,8 +3,8 @@ import clickhouse from 'lib/clickhouse'; import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; import { QueryFilters, WebsiteEventData } from 'lib/types'; -export async function getEventDataFields( - ...args: [websiteId: string, filters: QueryFilters & { field?: string }] +export async function getEventDataProperties( + ...args: [websiteId: string, filters: QueryFilters & { propertyName?: string }] ): Promise { return runQuery({ [PRISMA]: () => relationalQuery(...args), @@ -12,25 +12,26 @@ export async function getEventDataFields( }); } -async function relationalQuery(websiteId: string, filters: QueryFilters & { field?: string }) { +async function relationalQuery( + websiteId: string, + filters: QueryFilters & { propertyName?: string }, +) { const { rawQuery, parseFilters } = prisma; const { filterQuery, params } = await parseFilters(websiteId, filters, { - columns: { field: 'data_key' }, + columns: { propertyName: 'data_key' }, }); return rawQuery( ` select - data_key as "fieldName", - data_type as "dataType", - string_value as "fieldValue", + data_key as "propertyName", count(*) as "total" from event_data where website_id = {{websiteId::uuid}} and created_at between {{startDate}} and {{endDate}} ${filterQuery} - group by data_key, data_type, string_value - order by 3 desc, 2 desc, 1 asc + group by data_key + order by 2 desc limit 500 `, params, @@ -39,35 +40,33 @@ async function relationalQuery(websiteId: string, filters: QueryFilters & { fiel async function clickhouseQuery( websiteId: string, - filters: QueryFilters & { field?: string }, -): Promise<{ fieldName: string; dataType: number; fieldValue: string; total: number }[]> { + filters: QueryFilters & { propertyName?: string }, +): Promise<{ propertyName: string; dataType: number; propertyValue: string; total: number }[]> { const { rawQuery, parseFilters } = clickhouse; const { filterQuery, params } = await parseFilters(websiteId, filters, { - columns: { field: 'data_key' }, + columns: { propertyName: 'data_key' }, }); return rawQuery( ` select - data_key as fieldName, - data_type as dataType, - string_value as fieldValue, + data_key as propertyName, count(*) as total from event_data where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} ${filterQuery} - group by data_key, data_type, string_value - order by 3 desc, 2 desc, 1 asc + group by data_key + order by 2 desc limit 500 `, params, ).then(result => { return Object.values(result).map((a: any) => { return { - fieldName: a.fieldName, + propertyName: a.propertyName, dataType: Number(a.dataType), - fieldValue: a.fieldValue, + propertyValue: a.propertyValue, total: Number(a.total), }; }); diff --git a/src/queries/analytics/events/getEventDataStats.ts b/src/queries/analytics/events/getEventDataStats.ts index 09bef107c6..51a0da746e 100644 --- a/src/queries/analytics/events/getEventDataStats.ts +++ b/src/queries/analytics/events/getEventDataStats.ts @@ -7,7 +7,7 @@ export async function getEventDataStats( ...args: [websiteId: string, filters: QueryFilters] ): Promise<{ events: number; - fields: number; + properties: number; records: number; }> { return runQuery({ @@ -24,7 +24,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { ` select count(distinct t.website_event_id) as "events", - count(distinct t.data_key) as "fields", + count(distinct t.data_key) as "properties", sum(t.total) as "records" from ( select @@ -45,7 +45,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { async function clickhouseQuery( websiteId: string, filters: QueryFilters, -): Promise<{ events: number; fields: number; records: number }[]> { +): Promise<{ events: number; properties: number; records: number }[]> { const { rawQuery, parseFilters } = clickhouse; const { filterQuery, params } = await parseFilters(websiteId, filters); @@ -53,7 +53,7 @@ async function clickhouseQuery( ` select count(distinct t.event_id) as "events", - count(distinct t.data_key) as "fields", + count(distinct t.data_key) as "properties", sum(t.total) as "records" from ( select @@ -72,7 +72,7 @@ async function clickhouseQuery( return Object.values(result).map((a: any) => { return { events: Number(a.events), - fields: Number(a.fields), + properties: Number(a.properties), records: Number(a.records), }; }); diff --git a/src/queries/analytics/events/getEventDataValues.ts b/src/queries/analytics/events/getEventDataValues.ts new file mode 100644 index 0000000000..abf2efc0c9 --- /dev/null +++ b/src/queries/analytics/events/getEventDataValues.ts @@ -0,0 +1,70 @@ +import prisma from 'lib/prisma'; +import clickhouse from 'lib/clickhouse'; +import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; +import { QueryFilters, WebsiteEventData } from 'lib/types'; + +export async function getEventDataValues( + ...args: [websiteId: string, filters: QueryFilters & { propertyName?: string }] +): Promise { + return runQuery({ + [PRISMA]: () => relationalQuery(...args), + [CLICKHOUSE]: () => clickhouseQuery(...args), + }); +} + +async function relationalQuery( + websiteId: string, + filters: QueryFilters & { propertyName?: string }, +) { + const { rawQuery, parseFilters } = prisma; + const { filterQuery, params } = await parseFilters(websiteId, filters); + + return rawQuery( + ` + select + string_value as "value", + count(*) as "total" + from event_data + where website_id = {{websiteId::uuid}} + and created_at between {{startDate}} and {{endDate}} + and data_key = {{propertyName}} + ${filterQuery} + group by string_value + order by 2 desc + limit 500 + `, + params, + ); +} + +async function clickhouseQuery( + websiteId: string, + filters: QueryFilters & { propertyName?: string }, +): Promise<{ propertyName: string; dataType: number; propertyValue: string; total: number }[]> { + const { rawQuery, parseFilters } = clickhouse; + const { filterQuery, params } = await parseFilters(websiteId, filters); + + return rawQuery( + ` + select + string_value as "value", + count(*) as "total" + from event_data + where website_id = {websiteId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} + and data_key = {propertyName:String} + ${filterQuery} + group by string_value + order by 2 desc + limit 500 + `, + params, + ).then(result => { + return Object.values(result).map((a: any) => { + return { + ...a, + total: Number(a.total), + }; + }); + }); +} diff --git a/src/queries/index.ts b/src/queries/index.ts index 51c1e14b9f..7dd7178340 100644 --- a/src/queries/index.ts +++ b/src/queries/index.ts @@ -4,7 +4,8 @@ export * from 'queries/prisma/teamUser'; export * from 'queries/prisma/user'; export * from 'queries/prisma/website'; export * from './analytics/events/getEventDataEvents'; -export * from './analytics/events/getEventDataFields'; +export * from './analytics/events/getEventDataProperties'; +export * from './analytics/events/getEventDataValues'; export * from './analytics/events/getEventDataStats'; export * from './analytics/events/getEventDataUsage'; export * from './analytics/events/getEventMetrics'; diff --git a/yarn.lock b/yarn.lock index 24df3ea113..9cfe709f93 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9033,10 +9033,10 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" -react-basics@^0.124.0: - version "0.124.0" - resolved "https://registry.yarnpkg.com/react-basics/-/react-basics-0.124.0.tgz#c2c783f634aadccc13aae8a770960d298a10ebe4" - integrity sha512-5E2chyDE0PKKQ9x7kKM18/6UjFsjkPx7mfbTJb4PsWCC5gPi6m5zX7uPWF7omu4/I5Oms/nj16D3pa350WXIyw== +react-basics@^0.125.0: + version "0.125.0" + resolved "https://registry.yarnpkg.com/react-basics/-/react-basics-0.125.0.tgz#6baf3fea503fb4475f51877efa05d1a734b232c6" + integrity sha512-8swjTaKfenwb+NunwzQo16V+dCA/38Kd+PSYWpBFyNmlFzs3Ax2ZgnysxDhW9IgfFr4wR6/0gzD3S31WzXq6Kw== dependencies: "@react-spring/web" "^9.7.3" classnames "^2.3.1" @@ -9865,7 +9865,16 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -9954,7 +9963,14 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -10726,7 +10742,7 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -10744,6 +10760,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.0.1, wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From 67874a6282e9f5e0b52d1b6a0e3d5284cce468d4 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 9 Aug 2024 01:41:21 -0700 Subject: [PATCH 076/132] Update event property styles. --- .../[websiteId]/events/EventProperties.module.css | 15 +++++++++++++-- .../[websiteId]/events/EventProperties.tsx | 8 ++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/events/EventProperties.module.css b/src/app/(main)/websites/[websiteId]/events/EventProperties.module.css index b476ebd87a..4f6a65ec9a 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventProperties.module.css +++ b/src/app/(main)/websites/[websiteId]/events/EventProperties.module.css @@ -1,7 +1,8 @@ .container { display: grid; - grid-template-columns: repeat(auto-fill, minmax(600px, 1fr)); - gap: 30px; + grid-template-columns: repeat(auto-fill, minmax(420px, 1fr)); + gap: 60px; + margin-bottom: 40px; } .table { @@ -12,3 +13,13 @@ cursor: pointer; color: var(--primary400); } + +.title { + text-align: center; + font-weight: bold; + margin: 20px 0; +} + +.chart { + min-height: 620px; +} diff --git a/src/app/(main)/websites/[websiteId]/events/EventProperties.tsx b/src/app/(main)/websites/[websiteId]/events/EventProperties.tsx index d6e52c59ea..b2fa491a1b 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventProperties.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventProperties.tsx @@ -1,10 +1,10 @@ import { GridColumn, GridTable } from 'react-basics'; import { useEventDataProperties, useEventDataValues, useMessages } from 'components/hooks'; import { LoadingPanel } from 'components/common/LoadingPanel'; -import styles from './EventProperties.module.css'; import PieChart from 'components/charts/PieChart'; import { useState } from 'react'; import { CHART_COLORS } from 'lib/constants'; +import styles from './EventProperties.module.css'; export function EventProperties({ websiteId }: { websiteId: string }) { const [propertyName, setPropertyName] = useState(''); @@ -36,11 +36,11 @@ export function EventProperties({ websiteId }: { websiteId: string }) {
)}
- +
{propertyName && ( -
- {propertyName} +
+
{propertyName}
)} From 840c1c02bf897df61551d6f49aacf59f79306fe4 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 9 Aug 2024 11:49:08 -0700 Subject: [PATCH 077/132] Fixed type icons. --- .../[websiteId]/sessions/SessionsTable.tsx | 20 ++++++++++++++++--- src/components/common/TypeIcon.tsx | 4 +++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx index 6e2e53ed00..ad4a8ac924 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx @@ -31,11 +31,25 @@ export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean - {row => formatValue(row.browser, 'browser')} + {row => ( + + {formatValue(row.browser, 'browser')} + + )} + + + {row => ( + + {formatValue(row.os, 'os')} + + )} - - {row => formatValue(row.device, 'device')} + {row => ( + + {formatValue(row.device, 'device')} + + )} {row => formatDate(new Date(row.lastAt), 'PPPpp', locale)} diff --git a/src/components/common/TypeIcon.tsx b/src/components/common/TypeIcon.tsx index 475fe6436a..bb99c2f7f0 100644 --- a/src/components/common/TypeIcon.tsx +++ b/src/components/common/TypeIcon.tsx @@ -12,7 +12,9 @@ export function TypeIcon({ return ( <> {value} Date: Fri, 9 Aug 2024 13:37:39 -0700 Subject: [PATCH 078/132] Fixed share page. --- src/app/share/[...shareId]/SharePage.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/share/[...shareId]/SharePage.tsx b/src/app/share/[...shareId]/SharePage.tsx index 4ac6af378a..c4d9af62da 100644 --- a/src/app/share/[...shareId]/SharePage.tsx +++ b/src/app/share/[...shareId]/SharePage.tsx @@ -5,6 +5,7 @@ import Page from 'components/layout/Page'; import Header from './Header'; import Footer from './Footer'; import styles from './SharePage.module.css'; +import { WebsiteProvider } from 'app/(main)/websites/[websiteId]/WebsiteProvider'; export default function SharePage({ shareId }) { const { shareToken, isLoading } = useShareToken(shareId); @@ -17,7 +18,9 @@ export default function SharePage({ shareId }) {
- + + +
From c7d39a3e940c47337860d351b4e9d83028da0d66 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 9 Aug 2024 14:13:34 -0700 Subject: [PATCH 079/132] Fixed session properties. --- .../sessions/[sessionId]/SessionData.tsx | 13 +++++-------- .../sessions/[sessionId]/SessionDetailsPage.tsx | 12 ++++-------- src/components/common/LoadingPanel.module.css | 1 + src/components/common/LoadingPanel.tsx | 2 +- src/components/hooks/queries/useSessionData.ts | 2 +- 5 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.tsx index 7e583f185b..6b18cb572b 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.tsx @@ -1,19 +1,16 @@ -import { Loading, TextOverflow } from 'react-basics'; +import { TextOverflow } from 'react-basics'; import { useMessages, useSessionData } from 'components/hooks'; import Empty from 'components/common/Empty'; import { DATA_TYPES } from 'lib/constants'; import styles from './SessionData.module.css'; +import { LoadingPanel } from 'components/common/LoadingPanel'; export function SessionData({ websiteId, sessionId }: { websiteId: string; sessionId: string }) { const { formatMessage, labels } = useMessages(); - const { data, isLoading } = useSessionData(websiteId, sessionId); - - if (isLoading) { - return ; - } + const { data, ...query } = useSessionData(websiteId, sessionId); return ( -
+
{formatMessage(labels.properties)}
{!data?.length && } {data?.map(({ dataKey, dataType, stringValue }) => { @@ -29,6 +26,6 @@ export function SessionData({ websiteId, sessionId }: { websiteId: string; sessi
); })} -
+ ); } diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx index 059e041f5a..fa96c7762e 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx @@ -2,12 +2,12 @@ import WebsiteHeader from '../../WebsiteHeader'; import SessionInfo from './SessionInfo'; import { useWebsiteSession } from 'components/hooks'; -import { Loading } from 'react-basics'; import Avatar from 'components/common/Avatar'; import { SessionActivity } from './SessionActivity'; import { SessionStats } from './SessionStats'; import { SessionData } from './SessionData'; import styles from './SessionDetailsPage.module.css'; +import { LoadingPanel } from 'components/common/LoadingPanel'; export default function SessionDetailsPage({ websiteId, @@ -16,14 +16,10 @@ export default function SessionDetailsPage({ websiteId: string; sessionId: string; }) { - const { data, isLoading } = useWebsiteSession(websiteId, sessionId); - - if (isLoading) { - return ; - } + const { data, ...query } = useWebsiteSession(websiteId, sessionId); return ( - <> +
@@ -38,6 +34,6 @@ export default function SessionDetailsPage({
- +
); } diff --git a/src/components/common/LoadingPanel.module.css b/src/components/common/LoadingPanel.module.css index d82e27d69b..2dc8b75ee3 100644 --- a/src/components/common/LoadingPanel.module.css +++ b/src/components/common/LoadingPanel.module.css @@ -2,4 +2,5 @@ display: flex; flex-direction: column; position: relative; + flex: 1; } diff --git a/src/components/common/LoadingPanel.tsx b/src/components/common/LoadingPanel.tsx index 890e8e3918..dc18ca508a 100644 --- a/src/components/common/LoadingPanel.tsx +++ b/src/components/common/LoadingPanel.tsx @@ -23,7 +23,7 @@ export function LoadingPanel({ className?: string; children: ReactNode; }) { - const isEmpty = !data?.length && !isLoading && isFetched; + const isEmpty = !isLoading && isFetched && data && Array.isArray(data) && data.length > 0; return (
diff --git a/src/components/hooks/queries/useSessionData.ts b/src/components/hooks/queries/useSessionData.ts index f2e8f5241c..14e046d1d2 100644 --- a/src/components/hooks/queries/useSessionData.ts +++ b/src/components/hooks/queries/useSessionData.ts @@ -6,7 +6,7 @@ export function useSessionData(websiteId: string, sessionId: string) { return useQuery({ queryKey: ['session:data', { websiteId, sessionId }], queryFn: () => { - return get(`/sessions/${sessionId}/data`, { websiteId }); + return get(`/websites/${websiteId}/sessions/${sessionId}/properties`, { websiteId }); }, }); } From 4c0d7d960d4e4e85c7ed0d1b8e0855ce4df6719c Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 9 Aug 2024 15:02:48 -0700 Subject: [PATCH 080/132] Fixed empty logic. --- src/components/common/LoadingPanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/common/LoadingPanel.tsx b/src/components/common/LoadingPanel.tsx index dc18ca508a..487252be6a 100644 --- a/src/components/common/LoadingPanel.tsx +++ b/src/components/common/LoadingPanel.tsx @@ -23,7 +23,7 @@ export function LoadingPanel({ className?: string; children: ReactNode; }) { - const isEmpty = !isLoading && isFetched && data && Array.isArray(data) && data.length > 0; + const isEmpty = !isLoading && isFetched && data && Array.isArray(data) && data.length === 0; return (
From a1c0ec9c81af9a1017d05ccbe117a58973740f6e Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Fri, 9 Aug 2024 15:49:30 -0700 Subject: [PATCH 081/132] update getSessionActivity / getWebsiteSession --- .../sessions/[sessionId]/SessionActivity.tsx | 6 +++- .../[sessionId]/SessionDetailsPage.tsx | 17 +++++++---- .../hooks/queries/useSessionActivity.ts | 9 ++++-- .../sessions/[sessionId]/activity.ts | 13 +++++++-- .../[websiteId]/sessions/[sessionId]/index.ts | 2 +- .../analytics/sessions/getSessionActivity.ts | 28 ++++++++++++++----- .../analytics/sessions/getWebsiteSession.ts | 24 ++++++++-------- 7 files changed, 69 insertions(+), 30 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx index 0a15430c0e..69407584e7 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx @@ -8,12 +8,16 @@ import styles from './SessionActivity.module.css'; export function SessionActivity({ websiteId, sessionId, + startDate, + endDate, }: { websiteId: string; sessionId: string; + startDate: string; + endDate: string; }) { const { locale } = useLocale(); - const { data, isLoading } = useSessionActivity(websiteId, sessionId); + const { data, isLoading } = useSessionActivity(websiteId, sessionId, startDate, endDate); if (isLoading) { return ; diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx index fa96c7762e..d6a07edcf4 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx @@ -1,13 +1,13 @@ 'use client'; -import WebsiteHeader from '../../WebsiteHeader'; -import SessionInfo from './SessionInfo'; -import { useWebsiteSession } from 'components/hooks'; import Avatar from 'components/common/Avatar'; +import { LoadingPanel } from 'components/common/LoadingPanel'; +import { useWebsiteSession } from 'components/hooks'; +import WebsiteHeader from '../../WebsiteHeader'; import { SessionActivity } from './SessionActivity'; -import { SessionStats } from './SessionStats'; import { SessionData } from './SessionData'; import styles from './SessionDetailsPage.module.css'; -import { LoadingPanel } from 'components/common/LoadingPanel'; +import SessionInfo from './SessionInfo'; +import { SessionStats } from './SessionStats'; export default function SessionDetailsPage({ websiteId, @@ -28,7 +28,12 @@ export default function SessionDetailsPage({
- +
diff --git a/src/components/hooks/queries/useSessionActivity.ts b/src/components/hooks/queries/useSessionActivity.ts index e6d7ffa0e4..94676a9916 100644 --- a/src/components/hooks/queries/useSessionActivity.ts +++ b/src/components/hooks/queries/useSessionActivity.ts @@ -1,12 +1,17 @@ import { useApi } from './useApi'; -export function useSessionActivity(websiteId: string, sessionId: string) { +export function useSessionActivity( + websiteId: string, + sessionId: string, + startDate: string, + endDate: string, +) { const { get, useQuery } = useApi(); return useQuery({ queryKey: ['session:activity', { websiteId, sessionId }], queryFn: () => { - return get(`/websites/${websiteId}/sessions/${sessionId}/activity`); + return get(`/websites/${websiteId}/sessions/${sessionId}/activity`, { startDate, endDate }); }, }); } diff --git a/src/pages/api/websites/[websiteId]/sessions/[sessionId]/activity.ts b/src/pages/api/websites/[websiteId]/sessions/[sessionId]/activity.ts index 8d1b234617..2bf236545f 100644 --- a/src/pages/api/websites/[websiteId]/sessions/[sessionId]/activity.ts +++ b/src/pages/api/websites/[websiteId]/sessions/[sessionId]/activity.ts @@ -9,12 +9,16 @@ import { getSessionActivity } from 'queries'; export interface SessionActivityRequestQuery extends PageParams { websiteId: string; sessionId: string; + startDate: string; + endDate: string; } const schema = { GET: yup.object().shape({ websiteId: yup.string().uuid().required(), sessionId: yup.string().uuid().required(), + startDate: yup.string().required(), + endDate: yup.string().required(), }), }; @@ -26,14 +30,19 @@ export default async ( await useAuth(req, res); await useValidate(schema, req, res); - const { websiteId, sessionId } = req.query; + const { websiteId, sessionId, startDate, endDate } = req.query; if (req.method === 'GET') { if (!(await canViewWebsite(req.auth, websiteId))) { return unauthorized(res); } - const data = await getSessionActivity(websiteId, sessionId); + const data = await getSessionActivity( + websiteId, + sessionId, + new Date(startDate), + new Date(endDate), + ); return ok(res, data); } diff --git a/src/pages/api/websites/[websiteId]/sessions/[sessionId]/index.ts b/src/pages/api/websites/[websiteId]/sessions/[sessionId]/index.ts index b2665c7cf6..f627a20858 100644 --- a/src/pages/api/websites/[websiteId]/sessions/[sessionId]/index.ts +++ b/src/pages/api/websites/[websiteId]/sessions/[sessionId]/index.ts @@ -33,7 +33,7 @@ export default async ( return unauthorized(res); } - const data = await getWebsiteSession(sessionId); + const data = await getWebsiteSession(websiteId, sessionId); return ok(res, data); } diff --git a/src/queries/analytics/sessions/getSessionActivity.ts b/src/queries/analytics/sessions/getSessionActivity.ts index bc1f9acaa4..bb7c141cb4 100644 --- a/src/queries/analytics/sessions/getSessionActivity.ts +++ b/src/queries/analytics/sessions/getSessionActivity.ts @@ -1,24 +1,37 @@ -import prisma from 'lib/prisma'; import clickhouse from 'lib/clickhouse'; -import { runQuery, PRISMA, CLICKHOUSE } from 'lib/db'; +import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; +import prisma from 'lib/prisma'; -export async function getSessionActivity(...args: [websiteId: string, sessionId: string]) { +export async function getSessionActivity( + ...args: [websiteId: string, sessionId: string, startDate: Date, endDate: Date] +) { return runQuery({ [PRISMA]: () => relationalQuery(...args), [CLICKHOUSE]: () => clickhouseQuery(...args), }); } -async function relationalQuery(websiteId: string, sessionId: string) { +async function relationalQuery( + websiteId: string, + sessionId: string, + startDate: Date, + endDate: Date, +) { return prisma.client.websiteEvent.findMany({ where: { id: sessionId, websiteId, + createdAt: { gte: startDate, lte: endDate }, }, }); } -async function clickhouseQuery(websiteId: string, sessionId: string) { +async function clickhouseQuery( + websiteId: string, + sessionId: string, + startDate: Date, + endDate: Date, +) { const { rawQuery } = clickhouse; return rawQuery( @@ -36,9 +49,10 @@ async function clickhouseQuery(websiteId: string, sessionId: string) { visit_id as visitId from website_event where website_id = {websiteId:UUID} - and session_id = {sessionId:UUID} + and session_id = {sessionId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} order by created_at desc `, - { websiteId, sessionId }, + { websiteId, sessionId, startDate, endDate }, ); } diff --git a/src/queries/analytics/sessions/getWebsiteSession.ts b/src/queries/analytics/sessions/getWebsiteSession.ts index 38bc230c8c..ca527dba86 100644 --- a/src/queries/analytics/sessions/getWebsiteSession.ts +++ b/src/queries/analytics/sessions/getWebsiteSession.ts @@ -2,22 +2,23 @@ import prisma from 'lib/prisma'; import clickhouse from 'lib/clickhouse'; import { runQuery, PRISMA, CLICKHOUSE } from 'lib/db'; -export async function getWebsiteSession(...args: [sessionId: string]) { +export async function getWebsiteSession(...args: [websiteId: string, sessionId: string]) { return runQuery({ [PRISMA]: () => relationalQuery(...args), [CLICKHOUSE]: () => clickhouseQuery(...args), }); } -async function relationalQuery(sessionId: string) { +async function relationalQuery(websiteId: string, sessionId: string) { return prisma.client.session.findUnique({ where: { id: sessionId, + websiteId, }, }); } -async function clickhouseQuery(sessionId: string) { +async function clickhouseQuery(websiteId: string, sessionId: string) { const { rawQuery } = clickhouse; return rawQuery( @@ -34,15 +35,16 @@ async function clickhouseQuery(sessionId: string) { country, subdivision1, city, - min(created_at) as firstAt, - max(created_at) as lastAt, + min(min_time) as firstAt, + max(max_time) as lastAt, uniq(visit_id) as visits, - sumIf(1, event_type = 1) as views, - sumIf(1, event_type = 2) as events - from website_event - where session_id = {sessionId:UUID} - group by session_id, website_id, hostname, browser, os, device, screen, language, country, subdivision1, city + sumIf(views, event_type = 1) as views, + length(groupArrayArray(event_name)) as events + from website_event_stats_hourly + where website_id = {websiteId:UUID} + and session_id = {sessionId:UUID} + group by session_id, website_id, hostname, browser, os, device, screen, language, country, subdivision1, city; `, - { sessionId }, + { websiteId, sessionId }, ).then(result => result?.[0]); } From 136dd6794e171751938a53885b16187cf7e60ada Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Fri, 9 Aug 2024 17:27:06 -0700 Subject: [PATCH 082/132] update websitesession, add session duration --- .../sessions/[sessionId]/SessionStats.tsx | 18 +++++++++++++++++- src/components/messages.ts | 1 + src/lib/load.ts | 6 +++--- src/lib/session.ts | 2 +- .../sessions/[sessionId]/activity.ts | 4 ++-- .../analytics/sessions/getWebsiteSessions.ts | 2 +- 6 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx index a25f7af3b4..6953405e3d 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx @@ -1,15 +1,31 @@ -import MetricCard from 'components/metrics/MetricCard'; import { useMessages } from 'components/hooks'; +import MetricCard from 'components/metrics/MetricCard'; import MetricsBar from 'components/metrics/MetricsBar'; +import { formatShortTime } from 'lib/format'; export function SessionStats({ data }) { const { formatMessage, labels } = useMessages(); + const duration = (new Date(data?.lastAt).getTime() - new Date(data?.firstAt).getTime()) / 1000; + let dateFormat; + + if (duration > 86400) { + dateFormat = ['d', 'm']; + } else if (duration > 3600) { + dateFormat = ['h', 'm']; + } else { + dateFormat = ['m', 's']; + } return ( + `${+n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), dateFormat, ' ')}`} + /> ); } diff --git a/src/components/messages.ts b/src/components/messages.ts index f711a4f6cf..0b32d0d868 100644 --- a/src/components/messages.ts +++ b/src/components/messages.ts @@ -141,6 +141,7 @@ export const labels = defineMessages({ bounceRate: { id: 'label.bounce-rate', defaultMessage: 'Bounce rate' }, viewsPerVisit: { id: 'label.views-per-visit', defaultMessage: 'Views per visit' }, visitDuration: { id: 'label.visit-duration', defaultMessage: 'Visit duration' }, + sessionDuration: { id: 'label.session-duration', defaultMessage: 'Session duration' }, desktop: { id: 'label.desktop', defaultMessage: 'Desktop' }, laptop: { id: 'label.laptop', defaultMessage: 'Laptop' }, tablet: { id: 'label.tablet', defaultMessage: 'Tablet' }, diff --git a/src/lib/load.ts b/src/lib/load.ts index 970247ddd5..8cddeaa9e5 100644 --- a/src/lib/load.ts +++ b/src/lib/load.ts @@ -18,17 +18,17 @@ export async function fetchWebsite(websiteId: string): Promise { return website; } -export async function fetchSession(sessionId: string): Promise { +export async function fetchSession(websiteId: string, sessionId: string): Promise { let session = null; if (redis.enabled) { session = await redis.client.fetch( `session:${sessionId}`, - () => getWebsiteSession(sessionId), + () => getWebsiteSession(websiteId, sessionId), 86400, ); } else { - session = await getWebsiteSession(sessionId); + session = await getWebsiteSession(websiteId, sessionId); } if (!session) { diff --git a/src/lib/session.ts b/src/lib/session.ts index cf30aa7124..ca145c04bb 100644 --- a/src/lib/session.ts +++ b/src/lib/session.ts @@ -62,7 +62,7 @@ export async function getSession(req: NextApiRequestCollect): Promise Date: Fri, 9 Aug 2024 18:20:26 -0700 Subject: [PATCH 083/132] remove event from website stats. doesnt work with view --- .../[websiteId]/events/EventsMetricsBar.tsx | 5 ----- src/queries/analytics/getWebsiteStats.ts | 13 +++---------- .../analytics/sessions/getWebsiteSessions.ts | 8 ++++---- 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx b/src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx index f77c6c04de..0a054e8d17 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx @@ -28,11 +28,6 @@ export function EventsMetricsBar({ websiteId }: { websiteId: string }) { label={formatMessage(labels.views)} formatValue={formatLongNumber} /> - diff --git a/src/queries/analytics/getWebsiteStats.ts b/src/queries/analytics/getWebsiteStats.ts index a33011ff5c..c35aea0693 100644 --- a/src/queries/analytics/getWebsiteStats.ts +++ b/src/queries/analytics/getWebsiteStats.ts @@ -33,15 +33,12 @@ async function relationalQuery( count(distinct t.session_id) as "visitors", count(distinct t.visit_id) as "visits", count(distinct t.country) as "countries", - count(t.event_name) as "countries", - sum(case when t.event_type = 2 then 1 else 0 end) as "events", sum(case when t.c = 1 then 1 else 0 end) as "bounces", sum(${getTimestampDiffSQL('t.min_time', 't.max_time')}) as "totaltime", from ( select website_event.session_id, website_event.visit_id, - website_event.event_type, session.country, count(*) as "c", min(website_event.created_at) as "min_time", @@ -51,7 +48,7 @@ async function relationalQuery( where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} ${filterQuery} - group by 1, 2, 3, 4 + group by 1, 2, 3 ) as t `, params, @@ -78,14 +75,12 @@ async function clickhouseQuery( uniq(t.session_id) as "visitors", uniq(t.visit_id) as "visits", uniq(t.country) as "countries", - sumIf(1, t.event_type = 2) as "events", sumIf(1, t.c = 1) as "bounces", sum(max_time-min_time) as "totaltime" from ( select session_id, visit_id, - event_type, country, count(*) c, min(created_at) min_time, @@ -94,7 +89,7 @@ async function clickhouseQuery( where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} ${filterQuery} - group by session_id, visit_id, event_type, country + group by session_id, visit_id, country ) as t; `; } else { @@ -104,14 +99,12 @@ async function clickhouseQuery( uniq(session_id) as "visitors", uniq(visit_id) as "visits", uniq(country) as "countries", - sumIf(1, t.event_type = 2) as "events", sumIf(1, t.c = 1) as "bounces", sum(max_time-min_time) as "totaltime" from ( select session_id, visit_id, - event_type, country, sum(views) c, min(min_time) min_time, @@ -120,7 +113,7 @@ async function clickhouseQuery( where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} ${filterQuery} - group by session_id, visit_id, event_type, country + group by session_id, visit_id, country ) as t; `; } diff --git a/src/queries/analytics/sessions/getWebsiteSessions.ts b/src/queries/analytics/sessions/getWebsiteSessions.ts index b7c4f92848..2b22a0c810 100644 --- a/src/queries/analytics/sessions/getWebsiteSessions.ts +++ b/src/queries/analytics/sessions/getWebsiteSessions.ts @@ -44,11 +44,11 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar country, subdivision1, city, - min(created_at) as firstAt, - max(created_at) as lastAt, + min(min_time) as firstAt, + max(max_time) as lastAt, uniq(visit_id) as visits, - sumIf(1, event_type = 1) as views - from website_event + sumIf(views, event_type = 1) as views + from website_event_stats_hourly where website_id = {websiteId:UUID} ${dateQuery} ${filterQuery} From 1e52eeb0f0be14d2fb9c789a2b70b263dd137df6 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 9 Aug 2024 20:02:46 -0700 Subject: [PATCH 084/132] Updated header nav. --- .../websites/[websiteId]/WebsiteHeader.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx index 3b8cddaeaa..edd10b99b0 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx @@ -32,25 +32,25 @@ export function WebsiteHeader({ path: '', }, { - label: formatMessage(labels.compare), - icon: , - path: '/compare', + label: formatMessage(labels.events), + icon: , + path: '/events', }, { label: formatMessage(labels.sessions), icon: , path: '/sessions', }, - { - label: formatMessage(labels.events), - icon: , - path: '/events', - }, { label: formatMessage(labels.realtime), icon: , path: '/realtime', }, + { + label: formatMessage(labels.compare), + icon: , + path: '/compare', + }, { label: formatMessage(labels.reports), icon: , From 78728b8706c1121c0146975616e6b80fb870c813 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Sun, 11 Aug 2024 23:14:02 -0700 Subject: [PATCH 085/132] Fixed event search. Removed session time from profile. --- .../sessions/[sessionId]/SessionData.tsx | 30 ++++++++++--------- .../sessions/[sessionId]/SessionStats.tsx | 16 ---------- src/components/messages.ts | 1 - .../analytics/events/getWebsiteEvents.ts | 9 +++++- .../analytics/sessions/getWebsiteSessions.ts | 21 ++++++++----- 5 files changed, 37 insertions(+), 40 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.tsx index 6b18cb572b..39b6afd1f5 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.tsx @@ -10,22 +10,24 @@ export function SessionData({ websiteId, sessionId }: { websiteId: string; sessi const { data, ...query } = useSessionData(websiteId, sessionId); return ( - + <>
{formatMessage(labels.properties)}
- {!data?.length && } - {data?.map(({ dataKey, dataType, stringValue }) => { - return ( -
-
-
- {dataKey} + + {!data?.length && } + {data?.map(({ dataKey, dataType, stringValue }) => { + return ( +
+
+
+ {dataKey} +
+
{DATA_TYPES[dataType]}
-
{DATA_TYPES[dataType]}
+
{stringValue}
-
{stringValue}
-
- ); - })} - + ); + })} + + ); } diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx index 6953405e3d..3265e59241 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx @@ -1,31 +1,15 @@ import { useMessages } from 'components/hooks'; import MetricCard from 'components/metrics/MetricCard'; import MetricsBar from 'components/metrics/MetricsBar'; -import { formatShortTime } from 'lib/format'; export function SessionStats({ data }) { const { formatMessage, labels } = useMessages(); - const duration = (new Date(data?.lastAt).getTime() - new Date(data?.firstAt).getTime()) / 1000; - let dateFormat; - - if (duration > 86400) { - dateFormat = ['d', 'm']; - } else if (duration > 3600) { - dateFormat = ['h', 'm']; - } else { - dateFormat = ['m', 's']; - } return ( - `${+n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), dateFormat, ' ')}`} - /> ); } diff --git a/src/components/messages.ts b/src/components/messages.ts index 0b32d0d868..f711a4f6cf 100644 --- a/src/components/messages.ts +++ b/src/components/messages.ts @@ -141,7 +141,6 @@ export const labels = defineMessages({ bounceRate: { id: 'label.bounce-rate', defaultMessage: 'Bounce rate' }, viewsPerVisit: { id: 'label.views-per-visit', defaultMessage: 'Views per visit' }, visitDuration: { id: 'label.visit-duration', defaultMessage: 'Visit duration' }, - sessionDuration: { id: 'label.session-duration', defaultMessage: 'Session duration' }, desktop: { id: 'label.desktop', defaultMessage: 'Desktop' }, laptop: { id: 'label.laptop', defaultMessage: 'Laptop' }, tablet: { id: 'label.tablet', defaultMessage: 'Tablet' }, diff --git a/src/queries/analytics/events/getWebsiteEvents.ts b/src/queries/analytics/events/getWebsiteEvents.ts index 8666816102..2fe2bda21f 100644 --- a/src/queries/analytics/events/getWebsiteEvents.ts +++ b/src/queries/analytics/events/getWebsiteEvents.ts @@ -14,10 +14,12 @@ export function getWebsiteEvents( async function relationalQuery(websiteId: string, filters: QueryFilters, pageParams?: PageParams) { const { pagedQuery } = prisma; + const { query } = pageParams; const where = { ...filters, id: websiteId, + ...prisma.getSearchParameters(query, [{ eventName: 'contains' }, { urlPath: 'contains' }]), }; return pagedQuery('website_event', { where }, pageParams); @@ -47,7 +49,12 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar where website_id = {websiteId:UUID} ${dateQuery} ${filterQuery} - ${query ? `and (positionCaseInsensitive(event_name, {query:String}) > 0)` : ''} + ${ + query + ? `and (positionCaseInsensitive(event_name, {query:String}) > 0 + or positionCaseInsensitive(url_path, {query:String}) > 0)` + : '' + } order by created_at desc `, { ...params, query }, diff --git a/src/queries/analytics/sessions/getWebsiteSessions.ts b/src/queries/analytics/sessions/getWebsiteSessions.ts index 2b22a0c810..518769b03f 100644 --- a/src/queries/analytics/sessions/getWebsiteSessions.ts +++ b/src/queries/analytics/sessions/getWebsiteSessions.ts @@ -14,12 +14,10 @@ export async function getWebsiteSessions( async function relationalQuery(websiteId: string, filters: QueryFilters, pageParams: PageParams) { const { pagedQuery } = prisma; - const { query } = pageParams; const where = { ...filters, id: websiteId, - ...prisma.getSearchParameters(query, [{ eventName: 'contains' }, { urlPath: 'contains' }]), }; return pagedQuery('session', { where }, pageParams); @@ -28,7 +26,6 @@ async function relationalQuery(websiteId: string, filters: QueryFilters, pagePar async function clickhouseQuery(websiteId: string, filters: QueryFilters, pageParams?: PageParams) { const { pagedQuery, parseFilters } = clickhouse; const { params, dateQuery, filterQuery } = await parseFilters(websiteId, filters); - const { query } = pageParams; return pagedQuery( ` @@ -52,14 +49,22 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar where website_id = {websiteId:UUID} ${dateQuery} ${filterQuery} - ${query ? `and (positionCaseInsensitive(event_name, {query:String}) > 0)` : ''} group by session_id, website_id, hostname, browser, os, device, screen, language, country, subdivision1, city order by lastAt desc `, params, pageParams, - ).then((result: any) => ({ - ...result, - visits: Number(result.visits), - })); + ).then((result: any) => { + return { + ...result, + data: result.data.map((row: any) => { + return { + ...row, + createdAt: row.firstAt, + visits: Number(row.visits), + views: Number(row.views), + }; + }), + }; + }); } From b11b2e352432f1e6562bb6787f3dae0c58f73f7b Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Mon, 12 Aug 2024 23:54:21 -0700 Subject: [PATCH 086/132] Display correct date for events/sessions. --- package.json | 1 + .../[websiteId]/events/EventsTable.tsx | 13 ++++++++---- .../[websiteId]/sessions/SessionsTable.tsx | 13 ++++++++---- src/lib/clickhouse.ts | 20 ++++++++++++------- .../analytics/events/getWebsiteEvents.ts | 4 ++-- yarn.lock | 5 +++++ 6 files changed, 39 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index af873918c3..dd43d88909 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ ], "dependencies": { "@clickhouse/client": "^1.3.0", + "@date-fns/utc": "^1.2.0", "@dicebear/collection": "^9.2.1", "@dicebear/core": "^9.2.1", "@fontsource/inter": "^4.5.15", diff --git a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx index 261f8e4799..c09a797c17 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx @@ -1,13 +1,14 @@ import { GridTable, GridColumn, Icon } from 'react-basics'; -import { useLocale, useMessages, useTeamUrl } from 'components/hooks'; +import { useLocale, useMessages, useTeamUrl, useTimezone } from 'components/hooks'; import Empty from 'components/common/Empty'; import Avatar from 'components/common/Avatar'; import Link from 'next/link'; import Icons from 'components/icons'; -import { formatDate } from 'lib/date'; +import { formatInTimeZone } from 'date-fns-tz'; export function EventsTable({ data = [] }) { - const { locale } = useLocale(); + const { dateLocale } = useLocale(); + const { timezone } = useTimezone(); const { formatMessage, labels } = useMessages(); const { renderTeamUrl } = useTeamUrl(); @@ -36,7 +37,11 @@ export function EventsTable({ data = [] }) { }} - {row => formatDate(new Date(row.createdAt), 'PPPpp', locale)} + {row => + formatInTimeZone(new Date(row.createdAt.split(' ').join('T') + 'Z'), timezone, 'PPPpp', { + locale: dateLocale, + }) + } ); diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx index ad4a8ac924..0f346f8b0c 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx @@ -1,13 +1,14 @@ import Link from 'next/link'; import { GridColumn, GridTable } from 'react-basics'; -import { useFormat, useLocale, useMessages } from 'components/hooks'; +import { useFormat, useLocale, useMessages, useTimezone } from 'components/hooks'; import Avatar from 'components/common/Avatar'; import styles from './SessionsTable.module.css'; -import { formatDate } from 'lib/date'; import TypeIcon from 'components/common/TypeIcon'; +import { formatInTimeZone } from 'date-fns-tz'; export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean }) { - const { locale } = useLocale(); + const { dateLocale } = useLocale(); + const { timezone } = useTimezone(); const { formatMessage, labels } = useMessages(); const { formatValue } = useFormat(); @@ -52,7 +53,11 @@ export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean )} - {row => formatDate(new Date(row.lastAt), 'PPPpp', locale)} + {row => + formatInTimeZone(new Date(row.createdAt.split(' ').join('T') + 'Z'), timezone, 'PPPpp', { + locale: dateLocale, + }) + } ); diff --git a/src/lib/clickhouse.ts b/src/lib/clickhouse.ts index c25eff4e53..bf3da950fb 100644 --- a/src/lib/clickhouse.ts +++ b/src/lib/clickhouse.ts @@ -9,12 +9,12 @@ import { filtersToArray } from './params'; import { PageParams, QueryFilters, QueryOptions } from './types'; export const CLICKHOUSE_DATE_FORMATS = { - second: '%Y-%m-%dT%H:%i:%S', - minute: '%Y-%m-%dT%H:%i:00', - hour: '%Y-%m-%dT%H:00:00', - day: '%Y-%m-%dT00:00:00', - month: '%Y-%m-01T00:00:00', - year: '%Y-01-01T00:00:00', + second: '%Y-%m-%d %H:%i:%S', + minute: '%Y-%m-%d %H:%i:00', + hour: '%Y-%m-%d %H:00:00', + day: '%Y-%m-%d', + month: '%Y-%m-01', + year: '%Y-01-01', }; const log = debug('umami:clickhouse'); @@ -101,12 +101,18 @@ function getFilterQuery(filters: QueryFilters = {}, options: QueryOptions = {}) } function getDateQuery(filters: QueryFilters = {}) { - const { startDate, endDate } = filters; + const { startDate, endDate, timezone } = filters; if (startDate) { if (endDate) { + if (timezone) { + return `and created_at between toTimezone({startDate:DateTime64},{timezone:String}) and toTimezone({endDate:DateTime64},{timezone:String})`; + } return `and created_at between {startDate:DateTime64} and {endDate:DateTime64}`; } else { + if (timezone) { + return `and created_at >= toTimezone({startDate:DateTime64},{timezone:String})`; + } return `and created_at >= {startDate:DateTime64}`; } } diff --git a/src/queries/analytics/events/getWebsiteEvents.ts b/src/queries/analytics/events/getWebsiteEvents.ts index 2fe2bda21f..296602ea49 100644 --- a/src/queries/analytics/events/getWebsiteEvents.ts +++ b/src/queries/analytics/events/getWebsiteEvents.ts @@ -26,7 +26,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters, pagePar } async function clickhouseQuery(websiteId: string, filters: QueryFilters, pageParams?: PageParams) { - const { pagedQuery, parseFilters, getDateStringSQL } = clickhouse; + const { pagedQuery, parseFilters } = clickhouse; const { params, dateQuery, filterQuery } = await parseFilters(websiteId, filters); const { query } = pageParams; @@ -36,7 +36,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar event_id as id, website_id as websiteId, session_id as sessionId, - ${getDateStringSQL('created_at', 'second', filters.timezone)} as createdAt, + created_at as createdAt, url_path as urlPath, url_query as urlQuery, referrer_path as referrerPath, diff --git a/yarn.lock b/yarn.lock index 9cfe709f93..e7456af739 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1381,6 +1381,11 @@ debug "^3.1.0" lodash.once "^4.1.1" +"@date-fns/utc@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@date-fns/utc/-/utc-1.2.0.tgz#fb705b025b6769840608782c8fa7f3919d1b3337" + integrity sha512-YLq+crMPJiBmIdkRmv9nZuZy1mVtMlDcUKlg4mvI0UsC/dZeIaGoGB5p/C4FrpeOhZ7zBTK03T58S0DFkRNMnw== + "@dicebear/adventurer-neutral@9.2.1": version "9.2.1" resolved "https://registry.yarnpkg.com/@dicebear/adventurer-neutral/-/adventurer-neutral-9.2.1.tgz#0416ff71d0dd3ff391db9c9fda2acb166b074c5f" From 9b005e11c068268030e5bd88b5939bcf4b7bd9cb Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Tue, 13 Aug 2024 00:40:19 -0700 Subject: [PATCH 087/132] Created formatDate function for rendering dates in correct timezone. --- .../websites/[websiteId]/events/EventsTable.tsx | 12 +++--------- .../websites/[websiteId]/sessions/SessionsTable.tsx | 12 +++--------- src/components/hooks/useTimezone.ts | 7 ++++++- 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx index c09a797c17..4100202b36 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx @@ -1,14 +1,12 @@ import { GridTable, GridColumn, Icon } from 'react-basics'; -import { useLocale, useMessages, useTeamUrl, useTimezone } from 'components/hooks'; +import { useMessages, useTeamUrl, useTimezone } from 'components/hooks'; import Empty from 'components/common/Empty'; import Avatar from 'components/common/Avatar'; import Link from 'next/link'; import Icons from 'components/icons'; -import { formatInTimeZone } from 'date-fns-tz'; export function EventsTable({ data = [] }) { - const { dateLocale } = useLocale(); - const { timezone } = useTimezone(); + const { formatDate } = useTimezone(); const { formatMessage, labels } = useMessages(); const { renderTeamUrl } = useTeamUrl(); @@ -37,11 +35,7 @@ export function EventsTable({ data = [] }) { }} - {row => - formatInTimeZone(new Date(row.createdAt.split(' ').join('T') + 'Z'), timezone, 'PPPpp', { - locale: dateLocale, - }) - } + {row => formatDate(row.createdAt, 'PPPpp')} ); diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx index 0f346f8b0c..e266885642 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx @@ -1,14 +1,12 @@ import Link from 'next/link'; import { GridColumn, GridTable } from 'react-basics'; -import { useFormat, useLocale, useMessages, useTimezone } from 'components/hooks'; +import { useFormat, useMessages, useTimezone } from 'components/hooks'; import Avatar from 'components/common/Avatar'; import styles from './SessionsTable.module.css'; import TypeIcon from 'components/common/TypeIcon'; -import { formatInTimeZone } from 'date-fns-tz'; export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean }) { - const { dateLocale } = useLocale(); - const { timezone } = useTimezone(); + const { formatDate } = useTimezone(); const { formatMessage, labels } = useMessages(); const { formatValue } = useFormat(); @@ -53,11 +51,7 @@ export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean )} - {row => - formatInTimeZone(new Date(row.createdAt.split(' ').join('T') + 'Z'), timezone, 'PPPpp', { - locale: dateLocale, - }) - } + {row => formatDate(row.createdAt, 'PPPpp')} ); diff --git a/src/components/hooks/useTimezone.ts b/src/components/hooks/useTimezone.ts index 8bd76504bf..067441f57c 100644 --- a/src/components/hooks/useTimezone.ts +++ b/src/components/hooks/useTimezone.ts @@ -1,5 +1,6 @@ import { setItem } from 'next-basics'; import { TIMEZONE_CONFIG } from 'lib/constants'; +import { formatInTimeZone } from 'date-fns-tz'; import useStore, { setTimezone } from 'store/app'; const selector = (state: { timezone: string }) => state.timezone; @@ -12,7 +13,11 @@ export function useTimezone() { setTimezone(value); }; - return { timezone, saveTimezone }; + const formatDate = (date: string, pattern: string) => { + return formatInTimeZone(date.split(' ').join('T') + 'Z', timezone, pattern); + }; + + return { timezone, saveTimezone, formatDate }; } export default useTimezone; From af646029514572ca4ed63570c55bf6de8fa2a991 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Tue, 13 Aug 2024 00:56:41 -0700 Subject: [PATCH 088/132] Added map to sessions page. --- .../(main)/websites/[websiteId]/sessions/SessionsPage.tsx | 5 +++++ src/components/hooks/useTimezone.ts | 8 +++++++- src/components/metrics/WorldMap.tsx | 6 ++++-- src/queries/analytics/sessions/getWebsiteSessions.ts | 2 +- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx index afb1873507..ed02b2a86d 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx @@ -2,12 +2,17 @@ import WebsiteHeader from '../WebsiteHeader'; import SessionsDataTable from './SessionsDataTable'; import SessionsMetricsBar from './SessionsMetricsBar'; +import WorldMap from 'components/metrics/WorldMap'; +import { GridRow } from 'components/layout/Grid'; export function SessionsPage({ websiteId }) { return ( <> + + + ); diff --git a/src/components/hooks/useTimezone.ts b/src/components/hooks/useTimezone.ts index 067441f57c..b5e58ea923 100644 --- a/src/components/hooks/useTimezone.ts +++ b/src/components/hooks/useTimezone.ts @@ -14,7 +14,13 @@ export function useTimezone() { }; const formatDate = (date: string, pattern: string) => { - return formatInTimeZone(date.split(' ').join('T') + 'Z', timezone, pattern); + return formatInTimeZone( + /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$/.test(date) + ? date + : date.split(' ').join('T') + 'Z', + timezone, + pattern, + ); }; return { timezone, saveTimezone, formatDate }; diff --git a/src/components/metrics/WorldMap.tsx b/src/components/metrics/WorldMap.tsx index 5cebd428d3..4a16f98f77 100644 --- a/src/components/metrics/WorldMap.tsx +++ b/src/components/metrics/WorldMap.tsx @@ -1,4 +1,4 @@ -import { useState, useMemo } from 'react'; +import { useState, useMemo, HTMLAttributes } from 'react'; import { ComposableMap, Geographies, Geography, ZoomableGroup } from 'react-simple-maps'; import classNames from 'classnames'; import { colord } from 'colord'; @@ -16,11 +16,12 @@ export function WorldMap({ websiteId, data, className, + ...props }: { websiteId?: string; data?: any[]; className?: string; -}) { +} & HTMLAttributes) { const [tooltip, setTooltipPopup] = useState(); const { theme, colors } = useTheme(); const { locale } = useLocale(); @@ -67,6 +68,7 @@ export function WorldMap({ return (
{ return { ...row, - createdAt: row.firstAt, + createdAt: row.lastAt, visits: Number(row.visits), views: Number(row.views), }; From fc758745dc30bf87b9bc364e8dda94ac734074ba Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Tue, 13 Aug 2024 01:04:28 -0700 Subject: [PATCH 089/132] Fixed date display for session profile. --- .../sessions/[sessionId]/SessionActivity.tsx | 11 ++++------- .../[websiteId]/sessions/[sessionId]/SessionInfo.tsx | 8 ++++---- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx index 69407584e7..def3f0ba02 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx @@ -1,8 +1,7 @@ -import { formatDate } from 'lib/date'; import { isSameDay } from 'date-fns'; import { Loading, Icon, StatusLight } from 'react-basics'; import Icons from 'components/icons'; -import { useLocale, useSessionActivity } from 'components/hooks'; +import { useSessionActivity, useTimezone } from 'components/hooks'; import styles from './SessionActivity.module.css'; export function SessionActivity({ @@ -16,7 +15,7 @@ export function SessionActivity({ startDate: string; endDate: string; }) { - const { locale } = useLocale(); + const { formatDate } = useTimezone(); const { data, isLoading } = useSessionActivity(websiteId, sessionId, startDate, endDate); if (isLoading) { @@ -34,14 +33,12 @@ export function SessionActivity({ return ( <> {showHeader && ( -
- {formatDate(new Date(createdAt), 'EEEE, PPP', locale)} -
+
{formatDate(createdAt, 'EEEE, PPP')}
)}
- {formatDate(new Date(createdAt), 'h:mm:ss aaa', locale)} + {formatDate(createdAt, 'h:mm:ss aaa')}
{eventName ? : } diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.tsx index 4b3e8a06d6..641d2941b0 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.tsx @@ -1,5 +1,4 @@ -import { formatDate } from 'lib/date'; -import { useFormat, useLocale, useMessages, useRegionNames } from 'components/hooks'; +import { useFormat, useLocale, useMessages, useRegionNames, useTimezone } from 'components/hooks'; import TypeIcon from 'components/common/TypeIcon'; import { Icon, CopyIcon } from 'react-basics'; import Icons from 'components/icons'; @@ -7,6 +6,7 @@ import styles from './SessionInfo.module.css'; export default function SessionInfo({ data }) { const { locale } = useLocale(); + const { formatDate } = useTimezone(); const { formatMessage, labels } = useMessages(); const { formatValue } = useFormat(); const { getRegionName } = useRegionNames(locale); @@ -20,10 +20,10 @@ export default function SessionInfo({ data }) {
{formatMessage(labels.lastSeen)}
-
{formatDate(new Date(data?.lastAt), 'EEEE, PPPpp', locale)}
+
{formatDate(data?.lastAt, 'EEEE, PPPpp')}
{formatMessage(labels.firstSeen)}
-
{formatDate(new Date(data?.firstAt), 'EEEE, PPPpp', locale)}
+
{formatDate(data?.firstAt, 'EEEE, PPPpp')}
{formatMessage(labels.country)}
From f9c7129a3e279ee6167fc6ebe19c7fd3fda4bc9e Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Tue, 13 Aug 2024 08:45:52 -0700 Subject: [PATCH 090/132] implement visit duration --- .../sessions/[sessionId]/SessionStats.tsx | 6 +++ .../analytics/sessions/getWebsiteSession.ts | 38 ++++++++++++++----- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx index 3265e59241..ea606582f0 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx @@ -1,6 +1,7 @@ import { useMessages } from 'components/hooks'; import MetricCard from 'components/metrics/MetricCard'; import MetricsBar from 'components/metrics/MetricsBar'; +import { formatShortTime } from 'lib/format'; export function SessionStats({ data }) { const { formatMessage, labels } = useMessages(); @@ -10,6 +11,11 @@ export function SessionStats({ data }) { + `${+n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`} + /> ); } diff --git a/src/queries/analytics/sessions/getWebsiteSession.ts b/src/queries/analytics/sessions/getWebsiteSession.ts index ca527dba86..22c186420a 100644 --- a/src/queries/analytics/sessions/getWebsiteSession.ts +++ b/src/queries/analytics/sessions/getWebsiteSession.ts @@ -23,9 +23,8 @@ async function clickhouseQuery(websiteId: string, sessionId: string) { return rawQuery( ` - select - session_id as id, - website_id as websiteId, + select id, + websiteId, hostname, browser, os, @@ -37,13 +36,32 @@ async function clickhouseQuery(websiteId: string, sessionId: string) { city, min(min_time) as firstAt, max(max_time) as lastAt, - uniq(visit_id) as visits, - sumIf(views, event_type = 1) as views, - length(groupArrayArray(event_name)) as events - from website_event_stats_hourly - where website_id = {websiteId:UUID} - and session_id = {sessionId:UUID} - group by session_id, website_id, hostname, browser, os, device, screen, language, country, subdivision1, city; + uniq(visit_id) visits, + sum(views) as views, + sum(events) as events, + sum(max_time-min_time) as totaltime + from (select + session_id as id, + visit_id, + website_id as websiteId, + hostname, + browser, + os, + device, + screen, + language, + country, + subdivision1, + city, + min(min_time) as min_time, + max(max_time) as max_time, + sum(views) as views, + length(groupArrayArray(event_name)) as events + from website_event_stats_hourly + where website_id = {websiteId:UUID} + and session_id = {sessionId:UUID} + group by session_id, visit_id, website_id, hostname, browser, os, device, screen, language, country, subdivision1, city) t + group by id, websiteId, hostname, browser, os, device, screen, language, country, subdivision1, city; `, { websiteId, sessionId }, ).then(result => result?.[0]); From ae3888ced83d739d501b68f7412ad7b9bd3f8f52 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Tue, 13 Aug 2024 09:21:20 -0700 Subject: [PATCH 091/132] fix loading spinner / no results bug --- src/components/common/DataTable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/common/DataTable.tsx b/src/components/common/DataTable.tsx index 775e123c8c..c3bab88ff4 100644 --- a/src/components/common/DataTable.tsx +++ b/src/components/common/DataTable.tsx @@ -68,7 +68,7 @@ export function DataTable({ {hasData ? (typeof children === 'function' ? children(result) : children) : null} {isLoading && } {!isLoading && !hasData && !query && } - {noResults && } + {!isLoading && noResults && } {allowPaging && hasData && ( Date: Tue, 13 Aug 2024 10:11:12 -0700 Subject: [PATCH 092/132] fix event property format, fix realtime date format --- .../[websiteId]/realtime/RealtimeLog.tsx | 20 +++++++++---------- .../analytics/events/getEventDataValues.ts | 10 ++++++---- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx index b1836d4f1d..8bae2c17bd 100644 --- a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx +++ b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx @@ -1,17 +1,16 @@ -import { useContext, useMemo, useState } from 'react'; -import { StatusLight, Icon, Text, SearchField } from 'react-basics'; -import { FixedSizeList } from 'react-window'; -import { format } from 'date-fns'; -import thenby from 'thenby'; -import { safeDecodeURI } from 'next-basics'; -import FilterButtons from 'components/common/FilterButtons'; +import useFormat from 'components//hooks/useFormat'; import Empty from 'components/common/Empty'; -import { useLocale, useCountryNames, useMessages } from 'components/hooks'; +import FilterButtons from 'components/common/FilterButtons'; +import { useCountryNames, useLocale, useMessages, useTimezone } from 'components/hooks'; import Icons from 'components/icons'; -import useFormat from 'components//hooks/useFormat'; import { BROWSERS } from 'lib/constants'; import { stringToColor } from 'lib/format'; import { RealtimeData } from 'lib/types'; +import { safeDecodeURI } from 'next-basics'; +import { useContext, useMemo, useState } from 'react'; +import { Icon, SearchField, StatusLight, Text } from 'react-basics'; +import { FixedSizeList } from 'react-window'; +import thenby from 'thenby'; import { WebsiteContext } from '../WebsiteProvider'; import styles from './RealtimeLog.module.css'; @@ -32,6 +31,7 @@ export function RealtimeLog({ data }: { data: RealtimeData }) { const { formatMessage, labels, messages, FormattedMessage } = useMessages(); const { formatValue } = useFormat(); const { locale } = useLocale(); + const { formatDate } = useTimezone(); const { countryNames } = useCountryNames(locale); const [filter, setFilter] = useState(TYPE_ALL); @@ -54,7 +54,7 @@ export function RealtimeLog({ data }: { data: RealtimeData }) { }, ]; - const getTime = ({ createdAt, firstAt }) => format(new Date(firstAt || createdAt), 'h:mm:ss'); + const getTime = ({ createdAt, firstAt }) => formatDate(firstAt || createdAt, 'h:mm:ss'); const getColor = ({ id, sessionId }) => stringToColor(sessionId || id); diff --git a/src/queries/analytics/events/getEventDataValues.ts b/src/queries/analytics/events/getEventDataValues.ts index abf2efc0c9..d7754ad2a5 100644 --- a/src/queries/analytics/events/getEventDataValues.ts +++ b/src/queries/analytics/events/getEventDataValues.ts @@ -47,16 +47,18 @@ async function clickhouseQuery( return rawQuery( ` select - string_value as "value", + multiIf(data_type = 2, replaceAll(string_value, '.0000', ''), + data_type = 4, toString(date_trunc('hour', date_value)), + string_value) as "value", count(*) as "total" - from event_data + from umami.event_data where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and data_key = {propertyName:String} ${filterQuery} - group by string_value + group by value order by 2 desc - limit 500 + limit 500; `, params, ).then(result => { From 3805a0b43149df8b5c133189787c98b709eb9c3d Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Tue, 13 Aug 2024 10:49:04 -0700 Subject: [PATCH 093/132] limit event/session table to 1000 for performance --- src/queries/analytics/events/getWebsiteEvents.ts | 3 +++ src/queries/analytics/sessions/getWebsiteSessions.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/queries/analytics/events/getWebsiteEvents.ts b/src/queries/analytics/events/getWebsiteEvents.ts index 296602ea49..9304d69e88 100644 --- a/src/queries/analytics/events/getWebsiteEvents.ts +++ b/src/queries/analytics/events/getWebsiteEvents.ts @@ -32,6 +32,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar return pagedQuery( ` + with events as ( select event_id as id, website_id as websiteId, @@ -56,6 +57,8 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar : '' } order by created_at desc + limit 1000) + select * from events `, { ...params, query }, pageParams, diff --git a/src/queries/analytics/sessions/getWebsiteSessions.ts b/src/queries/analytics/sessions/getWebsiteSessions.ts index 5513e4380d..60f30b6b9e 100644 --- a/src/queries/analytics/sessions/getWebsiteSessions.ts +++ b/src/queries/analytics/sessions/getWebsiteSessions.ts @@ -29,6 +29,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar return pagedQuery( ` + with sessions as ( select session_id as id, website_id as websiteId, @@ -51,6 +52,8 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar ${filterQuery} group by session_id, website_id, hostname, browser, os, device, screen, language, country, subdivision1, city order by lastAt desc + limit 1000) + select * from sessions `, params, pageParams, From 0bd57bb158d14ca8080133bfc3cec7e2884cd6d2 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Tue, 13 Aug 2024 21:42:20 -0700 Subject: [PATCH 094/132] Added display of session properties. --- .../sessions/SessionProperties.module.css | 25 +++++++ .../sessions/SessionProperties.tsx | 52 +++++++++++++ .../[websiteId]/sessions/SessionsPage.tsx | 14 +++- src/components/hooks/index.ts | 2 + .../hooks/queries/useSessionDataProperties.ts | 20 +++++ .../hooks/queries/useSessionDataValues.ts | 21 ++++++ .../[websiteId]/session-data/properties.ts | 49 ++++++++++++ .../[websiteId]/session-data/values.ts | 50 +++++++++++++ .../sessions/getSessionDataProperties.ts | 74 +++++++++++++++++++ .../sessions/getSessionDataValues.ts | 72 ++++++++++++++++++ 10 files changed, 378 insertions(+), 1 deletion(-) create mode 100644 src/app/(main)/websites/[websiteId]/sessions/SessionProperties.module.css create mode 100644 src/app/(main)/websites/[websiteId]/sessions/SessionProperties.tsx create mode 100644 src/components/hooks/queries/useSessionDataProperties.ts create mode 100644 src/components/hooks/queries/useSessionDataValues.ts create mode 100644 src/pages/api/websites/[websiteId]/session-data/properties.ts create mode 100644 src/pages/api/websites/[websiteId]/session-data/values.ts create mode 100644 src/queries/analytics/sessions/getSessionDataProperties.ts create mode 100644 src/queries/analytics/sessions/getSessionDataValues.ts diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.module.css b/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.module.css new file mode 100644 index 0000000000..4f6a65ec9a --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.module.css @@ -0,0 +1,25 @@ +.container { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(420px, 1fr)); + gap: 60px; + margin-bottom: 40px; +} + +.table { + align-self: start; +} + +.link:hover { + cursor: pointer; + color: var(--primary400); +} + +.title { + text-align: center; + font-weight: bold; + margin: 20px 0; +} + +.chart { + min-height: 620px; +} diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.tsx new file mode 100644 index 0000000000..49b63e7489 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.tsx @@ -0,0 +1,52 @@ +import { GridColumn, GridTable } from 'react-basics'; +import { useSessionDataProperties, useSessionDataValues, useMessages } from 'components/hooks'; +import { LoadingPanel } from 'components/common/LoadingPanel'; +import PieChart from 'components/charts/PieChart'; +import { useState } from 'react'; +import { CHART_COLORS } from 'lib/constants'; +import styles from './SessionProperties.module.css'; + +export function SessionProperties({ websiteId }: { websiteId: string }) { + const [propertyName, setPropertyName] = useState(''); + const { formatMessage, labels } = useMessages(); + const { data, isLoading, isFetched, error } = useSessionDataProperties(websiteId); + const { data: values } = useSessionDataValues(websiteId, propertyName); + const chartData = + propertyName && values + ? { + labels: values.map(({ value }) => value), + datasets: [ + { + data: values.map(({ total }) => total), + backgroundColor: CHART_COLORS, + borderWidth: 0, + }, + ], + } + : null; + + return ( + +
+ + + {row => ( +
setPropertyName(row.propertyName)}> + {row.propertyName} +
+ )} +
+ +
+ {propertyName && ( +
+
{propertyName}
+ +
+ )} +
+
+ ); +} + +export default SessionProperties; diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx index ed02b2a86d..1b3ba6dd4e 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx @@ -2,10 +2,17 @@ import WebsiteHeader from '../WebsiteHeader'; import SessionsDataTable from './SessionsDataTable'; import SessionsMetricsBar from './SessionsMetricsBar'; +import SessionProperties from './SessionProperties'; import WorldMap from 'components/metrics/WorldMap'; import { GridRow } from 'components/layout/Grid'; +import { Item, Tabs } from 'react-basics'; +import { useState } from 'react'; +import { useMessages } from 'components/hooks'; export function SessionsPage({ websiteId }) { + const [tab, setTab] = useState('activity'); + const { formatMessage, labels } = useMessages(); + return ( <> @@ -13,7 +20,12 @@ export function SessionsPage({ websiteId }) { - + setTab(value)} style={{ marginBottom: 30 }}> + {formatMessage(labels.activity)} + {formatMessage(labels.properties)} + + {tab === 'activity' && } + {tab === 'properties' && } ); } diff --git a/src/components/hooks/index.ts b/src/components/hooks/index.ts index 42cac81a65..4e9c49d693 100644 --- a/src/components/hooks/index.ts +++ b/src/components/hooks/index.ts @@ -10,6 +10,8 @@ export * from './queries/useReport'; export * from './queries/useReports'; export * from './queries/useSessionActivity'; export * from './queries/useSessionData'; +export * from './queries/useSessionDataProperties'; +export * from './queries/useSessionDataValues'; export * from './queries/useWebsiteSession'; export * from './queries/useWebsiteSessions'; export * from './queries/useShareToken'; diff --git a/src/components/hooks/queries/useSessionDataProperties.ts b/src/components/hooks/queries/useSessionDataProperties.ts new file mode 100644 index 0000000000..459dccd6a8 --- /dev/null +++ b/src/components/hooks/queries/useSessionDataProperties.ts @@ -0,0 +1,20 @@ +import useApi from './useApi'; +import { UseQueryOptions } from '@tanstack/react-query'; +import { useFilterParams } from '../useFilterParams'; + +export function useSessionDataProperties( + websiteId: string, + options?: Omit, +) { + const { get, useQuery } = useApi(); + const params = useFilterParams(websiteId); + + return useQuery({ + queryKey: ['websites:event-data:properties', { websiteId, ...params }], + queryFn: () => get(`/websites/${websiteId}/session-data/properties`, { ...params }), + enabled: !!websiteId, + ...options, + }); +} + +export default useSessionDataProperties; diff --git a/src/components/hooks/queries/useSessionDataValues.ts b/src/components/hooks/queries/useSessionDataValues.ts new file mode 100644 index 0000000000..5230017736 --- /dev/null +++ b/src/components/hooks/queries/useSessionDataValues.ts @@ -0,0 +1,21 @@ +import useApi from './useApi'; +import { UseQueryOptions } from '@tanstack/react-query'; +import { useFilterParams } from '../useFilterParams'; + +export function useSessionDataValues( + websiteId: string, + propertyName: string, + options?: Omit, +) { + const { get, useQuery } = useApi(); + const params = useFilterParams(websiteId); + + return useQuery({ + queryKey: ['websites:event-data:values', { websiteId, propertyName, ...params }], + queryFn: () => get(`/websites/${websiteId}/session-data/values`, { ...params, propertyName }), + enabled: !!(websiteId && propertyName), + ...options, + }); +} + +export default useSessionDataValues; diff --git a/src/pages/api/websites/[websiteId]/session-data/properties.ts b/src/pages/api/websites/[websiteId]/session-data/properties.ts new file mode 100644 index 0000000000..19e9bbb892 --- /dev/null +++ b/src/pages/api/websites/[websiteId]/session-data/properties.ts @@ -0,0 +1,49 @@ +import { canViewWebsite } from 'lib/auth'; +import { useAuth, useCors, useValidate } from 'lib/middleware'; +import { NextApiRequestQueryBody } from 'lib/types'; +import { NextApiResponse } from 'next'; +import { methodNotAllowed, ok, unauthorized } from 'next-basics'; +import { getEventDataProperties } from 'queries'; +import * as yup from 'yup'; + +export interface EventDataFieldsRequestQuery { + websiteId: string; + startAt: string; + endAt: string; + propertyName?: string; +} + +const schema = { + GET: yup.object().shape({ + websiteId: yup.string().uuid().required(), + startAt: yup.number().integer().required(), + endAt: yup.number().integer().min(yup.ref('startAt')).required(), + propertyName: yup.string(), + }), +}; + +export default async ( + req: NextApiRequestQueryBody, + res: NextApiResponse, +) => { + await useCors(req, res); + await useAuth(req, res); + await useValidate(schema, req, res); + + if (req.method === 'GET') { + const { websiteId, startAt, endAt, propertyName } = req.query; + + if (!(await canViewWebsite(req.auth, websiteId))) { + return unauthorized(res); + } + + const startDate = new Date(+startAt); + const endDate = new Date(+endAt); + + const data = await getEventDataProperties(websiteId, { startDate, endDate, propertyName }); + + return ok(res, data); + } + + return methodNotAllowed(res); +}; diff --git a/src/pages/api/websites/[websiteId]/session-data/values.ts b/src/pages/api/websites/[websiteId]/session-data/values.ts new file mode 100644 index 0000000000..b9ada96df5 --- /dev/null +++ b/src/pages/api/websites/[websiteId]/session-data/values.ts @@ -0,0 +1,50 @@ +import { canViewWebsite } from 'lib/auth'; +import { useAuth, useCors, useValidate } from 'lib/middleware'; +import { NextApiRequestQueryBody } from 'lib/types'; +import { NextApiResponse } from 'next'; +import { methodNotAllowed, ok, unauthorized } from 'next-basics'; +import { getEventDataValues } from 'queries'; + +import * as yup from 'yup'; + +export interface EventDataFieldsRequestQuery { + websiteId: string; + startAt: string; + endAt: string; + propertyName?: string; +} + +const schema = { + GET: yup.object().shape({ + websiteId: yup.string().uuid().required(), + startAt: yup.number().integer().required(), + endAt: yup.number().integer().min(yup.ref('startAt')).required(), + propertyName: yup.string(), + }), +}; + +export default async ( + req: NextApiRequestQueryBody, + res: NextApiResponse, +) => { + await useCors(req, res); + await useAuth(req, res); + await useValidate(schema, req, res); + + if (req.method === 'GET') { + const { websiteId, startAt, endAt, propertyName } = req.query; + + if (!(await canViewWebsite(req.auth, websiteId))) { + return unauthorized(res); + } + + const startDate = new Date(+startAt); + const endDate = new Date(+endAt); + + const data = await getEventDataValues(websiteId, { startDate, endDate, propertyName }); + + return ok(res, data); + } + + return methodNotAllowed(res); +}; diff --git a/src/queries/analytics/sessions/getSessionDataProperties.ts b/src/queries/analytics/sessions/getSessionDataProperties.ts new file mode 100644 index 0000000000..0f45e1404a --- /dev/null +++ b/src/queries/analytics/sessions/getSessionDataProperties.ts @@ -0,0 +1,74 @@ +import prisma from 'lib/prisma'; +import clickhouse from 'lib/clickhouse'; +import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; +import { QueryFilters, WebsiteEventData } from 'lib/types'; + +export async function getSessionDataProperties( + ...args: [websiteId: string, filters: QueryFilters & { propertyName?: string }] +): Promise { + return runQuery({ + [PRISMA]: () => relationalQuery(...args), + [CLICKHOUSE]: () => clickhouseQuery(...args), + }); +} + +async function relationalQuery( + websiteId: string, + filters: QueryFilters & { propertyName?: string }, +) { + const { rawQuery, parseFilters } = prisma; + const { filterQuery, params } = await parseFilters(websiteId, filters, { + columns: { propertyName: 'data_key' }, + }); + + return rawQuery( + ` + select + data_key as "propertyName", + count(*) as "total" + from session_data + where website_id = {{websiteId::uuid}} + and created_at between {{startDate}} and {{endDate}} + ${filterQuery} + group by data_key + order by 2 desc + limit 500 + `, + params, + ); +} + +async function clickhouseQuery( + websiteId: string, + filters: QueryFilters & { propertyName?: string }, +): Promise<{ propertyName: string; dataType: number; propertyValue: string; total: number }[]> { + const { rawQuery, parseFilters } = clickhouse; + const { filterQuery, params } = await parseFilters(websiteId, filters, { + columns: { propertyName: 'data_key' }, + }); + + return rawQuery( + ` + select + data_key as propertyName, + count(*) as total + from session_data + where website_id = {websiteId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} + ${filterQuery} + group by data_key + order by 2 desc + limit 500 + `, + params, + ).then(result => { + return Object.values(result).map((a: any) => { + return { + propertyName: a.propertyName, + dataType: Number(a.dataType), + propertyValue: a.propertyValue, + total: Number(a.total), + }; + }); + }); +} diff --git a/src/queries/analytics/sessions/getSessionDataValues.ts b/src/queries/analytics/sessions/getSessionDataValues.ts new file mode 100644 index 0000000000..a018dfa0e7 --- /dev/null +++ b/src/queries/analytics/sessions/getSessionDataValues.ts @@ -0,0 +1,72 @@ +import prisma from 'lib/prisma'; +import clickhouse from 'lib/clickhouse'; +import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; +import { QueryFilters, WebsiteEventData } from 'lib/types'; + +export async function getSessionDataValues( + ...args: [websiteId: string, filters: QueryFilters & { propertyName?: string }] +): Promise { + return runQuery({ + [PRISMA]: () => relationalQuery(...args), + [CLICKHOUSE]: () => clickhouseQuery(...args), + }); +} + +async function relationalQuery( + websiteId: string, + filters: QueryFilters & { propertyName?: string }, +) { + const { rawQuery, parseFilters } = prisma; + const { filterQuery, params } = await parseFilters(websiteId, filters); + + return rawQuery( + ` + select + string_value as "value", + count(*) as "total" + from session_data + where website_id = {{websiteId::uuid}} + and created_at between {{startDate}} and {{endDate}} + and data_key = {{propertyName}} + ${filterQuery} + group by string_value + order by 2 desc + limit 500 + `, + params, + ); +} + +async function clickhouseQuery( + websiteId: string, + filters: QueryFilters & { propertyName?: string }, +): Promise<{ propertyName: string; dataType: number; propertyValue: string; total: number }[]> { + const { rawQuery, parseFilters } = clickhouse; + const { filterQuery, params } = await parseFilters(websiteId, filters); + + return rawQuery( + ` + select + multiIf(data_type = 2, replaceAll(string_value, '.0000', ''), + data_type = 4, toString(date_trunc('hour', date_value)), + string_value) as "value", + count(*) as "total" + from umami.session_data + where website_id = {websiteId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} + and data_key = {propertyName:String} + ${filterQuery} + group by value + order by 2 desc + limit 500; + `, + params, + ).then(result => { + return Object.values(result).map((a: any) => { + return { + ...a, + total: Number(a.total), + }; + }); + }); +} From 04de691893658d9cb9ad6c5cf673cddb35c0144c Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Tue, 13 Aug 2024 22:16:21 -0700 Subject: [PATCH 095/132] Removed custom date format. Use ISO string everywhere. --- package.json | 1 - src/lib/clickhouse.ts | 6 ------ src/lib/kafka.ts | 6 ------ src/queries/analytics/events/saveEvent.ts | 4 ++-- src/queries/analytics/events/saveEventData.ts | 4 ++-- src/queries/analytics/sessions/saveSessionData.ts | 6 +++--- yarn.lock | 5 ----- 7 files changed, 7 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index dd43d88909..d6217788d3 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,6 @@ "cross-spawn": "^7.0.3", "date-fns": "^2.23.0", "date-fns-tz": "^1.1.4", - "dateformat": "^5.0.3", "debug": "^4.3.4", "del": "^6.0.0", "detect-browser": "^5.2.0", diff --git a/src/lib/clickhouse.ts b/src/lib/clickhouse.ts index bf3da950fb..78f0323ed0 100644 --- a/src/lib/clickhouse.ts +++ b/src/lib/clickhouse.ts @@ -1,5 +1,4 @@ import { ClickHouseClient, createClient } from '@clickhouse/client'; -import dateFormat from 'dateformat'; import debug from 'debug'; import { CLICKHOUSE } from 'lib/db'; import { DEFAULT_PAGE_SIZE, OPERATORS } from './constants'; @@ -63,10 +62,6 @@ function getDateSQL(field: string, unit: string, timezone?: string) { return `date_trunc('${unit}', ${field})`; } -function getDateFormat(date: Date) { - return `'${dateFormat(date, 'UTC:yyyy-mm-dd HH:MM:ss')}'`; -} - function mapFilter(column: string, operator: string, name: string, type: string = 'String') { const value = `{${name}:${type}}`; @@ -224,7 +219,6 @@ export default { connect, getDateStringSQL, getDateSQL, - getDateFormat, getFilterQuery, parseFilters, pagedQuery, diff --git a/src/lib/kafka.ts b/src/lib/kafka.ts index 76692afb4b..38a7073e6a 100644 --- a/src/lib/kafka.ts +++ b/src/lib/kafka.ts @@ -1,4 +1,3 @@ -import dateFormat from 'dateformat'; import debug from 'debug'; import { Kafka, Mechanism, Producer, RecordMetadata, SASLOptions, logLevel } from 'kafkajs'; import { KAFKA, KAFKA_PRODUCER } from 'lib/db'; @@ -56,10 +55,6 @@ async function getProducer(): Promise { return producer; } -function getDateFormat(date: Date, format?: string): string { - return dateFormat(date, format ? format : 'UTC:yyyy-mm-dd HH:MM:ss'); -} - async function sendMessage( topic: string, message: { [key: string]: string | number }, @@ -107,7 +102,6 @@ export default { producer, log, connect, - getDateFormat, sendMessage, sendMessages, }; diff --git a/src/queries/analytics/events/saveEvent.ts b/src/queries/analytics/events/saveEvent.ts index fa4805df36..5e21e303ff 100644 --- a/src/queries/analytics/events/saveEvent.ts +++ b/src/queries/analytics/events/saveEvent.ts @@ -136,9 +136,9 @@ async function clickhouseQuery(data: { ...args } = data; const { insert } = clickhouse; - const { getDateFormat, sendMessage } = kafka; + const { sendMessage } = kafka; const eventId = uuid(); - const createdAt = getDateFormat(new Date()); + const createdAt = new Date().toISOString(); const message = { ...args, diff --git a/src/queries/analytics/events/saveEventData.ts b/src/queries/analytics/events/saveEventData.ts index 9c7218e97b..fec4cae393 100644 --- a/src/queries/analytics/events/saveEventData.ts +++ b/src/queries/analytics/events/saveEventData.ts @@ -61,7 +61,7 @@ async function clickhouseQuery(data: { const { websiteId, sessionId, eventId, urlPath, eventName, eventData, createdAt } = data; const { insert } = clickhouse; - const { getDateFormat, sendMessages } = kafka; + const { sendMessages } = kafka; const jsonKeys = flattenJSON(eventData); @@ -76,7 +76,7 @@ async function clickhouseQuery(data: { data_type: dataType, string_value: getStringValue(value, dataType), number_value: dataType === DATA_TYPE.number ? value : null, - date_value: dataType === DATA_TYPE.date ? getDateFormat(value) : null, + date_value: dataType === DATA_TYPE.date ? value?.toISOString() : null, created_at: createdAt, }; }); diff --git a/src/queries/analytics/sessions/saveSessionData.ts b/src/queries/analytics/sessions/saveSessionData.ts index 51f5c47d0d..d932f7ed27 100644 --- a/src/queries/analytics/sessions/saveSessionData.ts +++ b/src/queries/analytics/sessions/saveSessionData.ts @@ -81,8 +81,8 @@ async function clickhouseQuery(data: { const { websiteId, sessionId, sessionData } = data; const { insert } = clickhouse; - const { getDateFormat, sendMessages } = kafka; - const createdAt = getDateFormat(new Date()); + const { sendMessages } = kafka; + const createdAt = new Date().toISOString(); const jsonKeys = flattenJSON(sessionData); @@ -94,7 +94,7 @@ async function clickhouseQuery(data: { data_type: dataType, string_value: getStringValue(value, dataType), number_value: dataType === DATA_TYPE.number ? value : null, - date_value: dataType === DATA_TYPE.date ? getDateFormat(value) : null, + date_value: dataType === DATA_TYPE.date ? value?.toISOString() : null, created_at: createdAt, }; }); diff --git a/yarn.lock b/yarn.lock index e7456af739..cf78cc0c6a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4625,11 +4625,6 @@ date-fns@^2.23.0, date-fns@^2.29.3: dependencies: "@babel/runtime" "^7.21.0" -dateformat@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-5.0.3.tgz#fe2223eff3cc70ce716931cb3038b59a9280696e" - integrity sha512-Kvr6HmPXUMerlLcLF+Pwq3K7apHpYmGDVqrxcDasBg86UcKeTSNWbEzU8bwdXnxnR44FtMhJAxI4Bov6Y/KUfA== - dayjs@^1.10.4: version "1.11.12" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.12.tgz#5245226cc7f40a15bf52e0b99fd2a04669ccac1d" From aaf9adacc61623d842ac90babac7c5a523963d29 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Wed, 14 Aug 2024 12:29:47 -0700 Subject: [PATCH 096/132] add event name to properties table --- .../[websiteId]/events/EventProperties.tsx | 17 +++++++++++++++-- .../hooks/queries/useEventDataValues.ts | 4 +++- .../websites/[websiteId]/event-data/values.ts | 11 +++++++++-- .../analytics/events/getEventDataProperties.ts | 15 ++++++++------- .../analytics/events/getEventDataValues.ts | 13 +++++++++---- 5 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/events/EventProperties.tsx b/src/app/(main)/websites/[websiteId]/events/EventProperties.tsx index b2fa491a1b..794a5053d5 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventProperties.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventProperties.tsx @@ -8,9 +8,10 @@ import styles from './EventProperties.module.css'; export function EventProperties({ websiteId }: { websiteId: string }) { const [propertyName, setPropertyName] = useState(''); + const [eventName, setEventName] = useState(''); const { formatMessage, labels } = useMessages(); const { data, isLoading, isFetched, error } = useEventDataProperties(websiteId); - const { data: values } = useEventDataValues(websiteId, propertyName); + const { data: values } = useEventDataValues(websiteId, eventName, propertyName); const chartData = propertyName && values ? { @@ -25,13 +26,25 @@ export function EventProperties({ websiteId }: { websiteId: string }) { } : null; + const handleRowClick = row => { + setEventName(row.eventName); + setPropertyName(row.propertyName); + }; + return (
+ + {row => ( +
handleRowClick(row)}> + {row.eventName} +
+ )} +
{row => ( -
setPropertyName(row.propertyName)}> +
handleRowClick(row)}> {row.propertyName}
)} diff --git a/src/components/hooks/queries/useEventDataValues.ts b/src/components/hooks/queries/useEventDataValues.ts index 47b5a513ce..61aea58e05 100644 --- a/src/components/hooks/queries/useEventDataValues.ts +++ b/src/components/hooks/queries/useEventDataValues.ts @@ -4,6 +4,7 @@ import { useFilterParams } from '../useFilterParams'; export function useEventDataValues( websiteId: string, + eventName: string, propertyName: string, options?: Omit, ) { @@ -12,7 +13,8 @@ export function useEventDataValues( return useQuery({ queryKey: ['websites:event-data:values', { websiteId, propertyName, ...params }], - queryFn: () => get(`/websites/${websiteId}/event-data/values`, { ...params, propertyName }), + queryFn: () => + get(`/websites/${websiteId}/event-data/values`, { ...params, eventName, propertyName }), enabled: !!(websiteId && propertyName), ...options, }); diff --git a/src/pages/api/websites/[websiteId]/event-data/values.ts b/src/pages/api/websites/[websiteId]/event-data/values.ts index b9ada96df5..e5bb4ab8de 100644 --- a/src/pages/api/websites/[websiteId]/event-data/values.ts +++ b/src/pages/api/websites/[websiteId]/event-data/values.ts @@ -11,6 +11,7 @@ export interface EventDataFieldsRequestQuery { websiteId: string; startAt: string; endAt: string; + eventName?: string; propertyName?: string; } @@ -19,6 +20,7 @@ const schema = { websiteId: yup.string().uuid().required(), startAt: yup.number().integer().required(), endAt: yup.number().integer().min(yup.ref('startAt')).required(), + eventName: yup.string(), propertyName: yup.string(), }), }; @@ -32,7 +34,7 @@ export default async ( await useValidate(schema, req, res); if (req.method === 'GET') { - const { websiteId, startAt, endAt, propertyName } = req.query; + const { websiteId, startAt, endAt, eventName, propertyName } = req.query; if (!(await canViewWebsite(req.auth, websiteId))) { return unauthorized(res); @@ -41,7 +43,12 @@ export default async ( const startDate = new Date(+startAt); const endDate = new Date(+endAt); - const data = await getEventDataValues(websiteId, { startDate, endDate, propertyName }); + const data = await getEventDataValues(websiteId, { + startDate, + endDate, + eventName, + propertyName, + }); return ok(res, data); } diff --git a/src/queries/analytics/events/getEventDataProperties.ts b/src/queries/analytics/events/getEventDataProperties.ts index 78fcb2cda3..8d1eaf9de0 100644 --- a/src/queries/analytics/events/getEventDataProperties.ts +++ b/src/queries/analytics/events/getEventDataProperties.ts @@ -24,14 +24,15 @@ async function relationalQuery( return rawQuery( ` select + event_name as "eventName", data_key as "propertyName", count(*) as "total" from event_data where website_id = {{websiteId::uuid}} and created_at between {{startDate}} and {{endDate}} ${filterQuery} - group by data_key - order by 2 desc + group by event_name, data_key + order by 3 desc limit 500 `, params, @@ -41,7 +42,7 @@ async function relationalQuery( async function clickhouseQuery( websiteId: string, filters: QueryFilters & { propertyName?: string }, -): Promise<{ propertyName: string; dataType: number; propertyValue: string; total: number }[]> { +): Promise<{ eventName: string; propertyName: string; total: number }[]> { const { rawQuery, parseFilters } = clickhouse; const { filterQuery, params } = await parseFilters(websiteId, filters, { columns: { propertyName: 'data_key' }, @@ -50,23 +51,23 @@ async function clickhouseQuery( return rawQuery( ` select + event_name as eventName, data_key as propertyName, count(*) as total from event_data where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} ${filterQuery} - group by data_key - order by 2 desc + group by event_name, data_key + order by 1, 3 desc limit 500 `, params, ).then(result => { return Object.values(result).map((a: any) => { return { + eventName: a.eventName, propertyName: a.propertyName, - dataType: Number(a.dataType), - propertyValue: a.propertyValue, total: Number(a.total), }; }); diff --git a/src/queries/analytics/events/getEventDataValues.ts b/src/queries/analytics/events/getEventDataValues.ts index d7754ad2a5..6d50a96d6c 100644 --- a/src/queries/analytics/events/getEventDataValues.ts +++ b/src/queries/analytics/events/getEventDataValues.ts @@ -4,7 +4,10 @@ import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; import { QueryFilters, WebsiteEventData } from 'lib/types'; export async function getEventDataValues( - ...args: [websiteId: string, filters: QueryFilters & { propertyName?: string }] + ...args: [ + websiteId: string, + filters: QueryFilters & { eventName?: string; propertyName?: string }, + ] ): Promise { return runQuery({ [PRISMA]: () => relationalQuery(...args), @@ -14,7 +17,7 @@ export async function getEventDataValues( async function relationalQuery( websiteId: string, - filters: QueryFilters & { propertyName?: string }, + filters: QueryFilters & { eventName?: string; propertyName?: string }, ) { const { rawQuery, parseFilters } = prisma; const { filterQuery, params } = await parseFilters(websiteId, filters); @@ -28,6 +31,7 @@ async function relationalQuery( where website_id = {{websiteId::uuid}} and created_at between {{startDate}} and {{endDate}} and data_key = {{propertyName}} + and event_key = {{eventName}} ${filterQuery} group by string_value order by 2 desc @@ -39,7 +43,7 @@ async function relationalQuery( async function clickhouseQuery( websiteId: string, - filters: QueryFilters & { propertyName?: string }, + filters: QueryFilters & { eventName?: string; propertyName?: string }, ): Promise<{ propertyName: string; dataType: number; propertyValue: string; total: number }[]> { const { rawQuery, parseFilters } = clickhouse; const { filterQuery, params } = await parseFilters(websiteId, filters); @@ -55,8 +59,9 @@ async function clickhouseQuery( where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and data_key = {propertyName:String} + and event_name = {eventName:String} ${filterQuery} - group by value + group by event_name, value order by 2 desc limit 500; `, From db25f241c0367885c2b244001827ed0e60597c82 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Wed, 14 Aug 2024 12:57:43 -0700 Subject: [PATCH 097/132] clean up session properties / values --- src/components/hooks/queries/useSessionDataValues.ts | 2 +- src/pages/api/websites/[websiteId]/session-data/properties.ts | 4 ++-- src/pages/api/websites/[websiteId]/session-data/values.ts | 4 ++-- src/queries/analytics/events/getEventDataValues.ts | 2 +- src/queries/analytics/sessions/getSessionDataProperties.ts | 4 +--- src/queries/analytics/sessions/getSessionDataValues.ts | 2 +- src/queries/index.ts | 2 ++ 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/hooks/queries/useSessionDataValues.ts b/src/components/hooks/queries/useSessionDataValues.ts index 5230017736..ce9a67f3ec 100644 --- a/src/components/hooks/queries/useSessionDataValues.ts +++ b/src/components/hooks/queries/useSessionDataValues.ts @@ -11,7 +11,7 @@ export function useSessionDataValues( const params = useFilterParams(websiteId); return useQuery({ - queryKey: ['websites:event-data:values', { websiteId, propertyName, ...params }], + queryKey: ['websites:session-data:values', { websiteId, propertyName, ...params }], queryFn: () => get(`/websites/${websiteId}/session-data/values`, { ...params, propertyName }), enabled: !!(websiteId && propertyName), ...options, diff --git a/src/pages/api/websites/[websiteId]/session-data/properties.ts b/src/pages/api/websites/[websiteId]/session-data/properties.ts index 19e9bbb892..4cd2e1e62e 100644 --- a/src/pages/api/websites/[websiteId]/session-data/properties.ts +++ b/src/pages/api/websites/[websiteId]/session-data/properties.ts @@ -3,7 +3,7 @@ import { useAuth, useCors, useValidate } from 'lib/middleware'; import { NextApiRequestQueryBody } from 'lib/types'; import { NextApiResponse } from 'next'; import { methodNotAllowed, ok, unauthorized } from 'next-basics'; -import { getEventDataProperties } from 'queries'; +import { getSessionDataProperties } from 'queries'; import * as yup from 'yup'; export interface EventDataFieldsRequestQuery { @@ -40,7 +40,7 @@ export default async ( const startDate = new Date(+startAt); const endDate = new Date(+endAt); - const data = await getEventDataProperties(websiteId, { startDate, endDate, propertyName }); + const data = await getSessionDataProperties(websiteId, { startDate, endDate, propertyName }); return ok(res, data); } diff --git a/src/pages/api/websites/[websiteId]/session-data/values.ts b/src/pages/api/websites/[websiteId]/session-data/values.ts index b9ada96df5..98463f1522 100644 --- a/src/pages/api/websites/[websiteId]/session-data/values.ts +++ b/src/pages/api/websites/[websiteId]/session-data/values.ts @@ -3,7 +3,7 @@ import { useAuth, useCors, useValidate } from 'lib/middleware'; import { NextApiRequestQueryBody } from 'lib/types'; import { NextApiResponse } from 'next'; import { methodNotAllowed, ok, unauthorized } from 'next-basics'; -import { getEventDataValues } from 'queries'; +import { getSessionDataValues } from 'queries'; import * as yup from 'yup'; @@ -41,7 +41,7 @@ export default async ( const startDate = new Date(+startAt); const endDate = new Date(+endAt); - const data = await getEventDataValues(websiteId, { startDate, endDate, propertyName }); + const data = await getSessionDataValues(websiteId, { startDate, endDate, propertyName }); return ok(res, data); } diff --git a/src/queries/analytics/events/getEventDataValues.ts b/src/queries/analytics/events/getEventDataValues.ts index 6d50a96d6c..e055bfccc9 100644 --- a/src/queries/analytics/events/getEventDataValues.ts +++ b/src/queries/analytics/events/getEventDataValues.ts @@ -55,7 +55,7 @@ async function clickhouseQuery( data_type = 4, toString(date_trunc('hour', date_value)), string_value) as "value", count(*) as "total" - from umami.event_data + from event_data where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and data_key = {propertyName:String} diff --git a/src/queries/analytics/sessions/getSessionDataProperties.ts b/src/queries/analytics/sessions/getSessionDataProperties.ts index 0f45e1404a..bfbd924618 100644 --- a/src/queries/analytics/sessions/getSessionDataProperties.ts +++ b/src/queries/analytics/sessions/getSessionDataProperties.ts @@ -41,7 +41,7 @@ async function relationalQuery( async function clickhouseQuery( websiteId: string, filters: QueryFilters & { propertyName?: string }, -): Promise<{ propertyName: string; dataType: number; propertyValue: string; total: number }[]> { +): Promise<{ propertyName: string; total: number }[]> { const { rawQuery, parseFilters } = clickhouse; const { filterQuery, params } = await parseFilters(websiteId, filters, { columns: { propertyName: 'data_key' }, @@ -65,8 +65,6 @@ async function clickhouseQuery( return Object.values(result).map((a: any) => { return { propertyName: a.propertyName, - dataType: Number(a.dataType), - propertyValue: a.propertyValue, total: Number(a.total), }; }); diff --git a/src/queries/analytics/sessions/getSessionDataValues.ts b/src/queries/analytics/sessions/getSessionDataValues.ts index a018dfa0e7..fd67e7a99a 100644 --- a/src/queries/analytics/sessions/getSessionDataValues.ts +++ b/src/queries/analytics/sessions/getSessionDataValues.ts @@ -51,7 +51,7 @@ async function clickhouseQuery( data_type = 4, toString(date_trunc('hour', date_value)), string_value) as "value", count(*) as "total" - from umami.session_data + from session_data where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and data_key = {propertyName:String} diff --git a/src/queries/index.ts b/src/queries/index.ts index 7dd7178340..f9c44dba94 100644 --- a/src/queries/index.ts +++ b/src/queries/index.ts @@ -22,6 +22,8 @@ export * from './analytics/pageviews/getPageviewStats'; export * from './analytics/sessions/createSession'; export * from './analytics/sessions/getWebsiteSession'; export * from './analytics/sessions/getSessionData'; +export * from './analytics/sessions/getSessionDataProperties'; +export * from './analytics/sessions/getSessionDataValues'; export * from './analytics/sessions/getSessionMetrics'; export * from './analytics/sessions/getWebsiteSessions'; export * from './analytics/sessions/getSessionActivity'; From c79720ae1d083bea6e7eb2c56066cfdb2498bb5c Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Thu, 15 Aug 2024 09:28:39 -0700 Subject: [PATCH 098/132] update session data schema --- db/clickhouse/schema.sql | 4 ++-- .../[websiteId]/sessions/[sessionId]/SessionData.module.css | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/db/clickhouse/schema.sql b/db/clickhouse/schema.sql index 02be2e38f3..3dcfda9f69 100644 --- a/db/clickhouse/schema.sql +++ b/db/clickhouse/schema.sql @@ -66,8 +66,8 @@ CREATE TABLE umami.session_data created_at DateTime('UTC'), job_id Nullable(UUID) ) -ENGINE = MergeTree - ORDER BY (website_id, session_id, data_key, created_at) +ENGINE = ReplacingMergeTree + ORDER BY (website_id, session_id, data_key) SETTINGS index_granularity = 8192; -- stats hourly diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.module.css b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.module.css index 072172f6dd..4794803d22 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.module.css +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.module.css @@ -7,6 +7,7 @@ .header { font-weight: bold; + margin-bottom: 20px; } .empty { From 439377f5c526f84916740bc6b2b25a1151c9fe38 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Thu, 15 Aug 2024 12:04:48 -0700 Subject: [PATCH 099/132] add final keyword to session data queries --- src/queries/analytics/sessions/getSessionData.ts | 2 +- src/queries/analytics/sessions/getSessionDataProperties.ts | 2 +- src/queries/analytics/sessions/getSessionDataValues.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/queries/analytics/sessions/getSessionData.ts b/src/queries/analytics/sessions/getSessionData.ts index 8b78fc8623..509fcdbf6b 100644 --- a/src/queries/analytics/sessions/getSessionData.ts +++ b/src/queries/analytics/sessions/getSessionData.ts @@ -32,7 +32,7 @@ async function clickhouseQuery(websiteId: string, sessionId: string) { number_value as numberValue, date_value as dateValue, created_at as createdAt - from session_data + from session_data final where website_id = {websiteId:UUID} and session_id = {sessionId:UUID} order by data_key asc diff --git a/src/queries/analytics/sessions/getSessionDataProperties.ts b/src/queries/analytics/sessions/getSessionDataProperties.ts index bfbd924618..b33fecfbd2 100644 --- a/src/queries/analytics/sessions/getSessionDataProperties.ts +++ b/src/queries/analytics/sessions/getSessionDataProperties.ts @@ -52,7 +52,7 @@ async function clickhouseQuery( select data_key as propertyName, count(*) as total - from session_data + from session_data final where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} ${filterQuery} diff --git a/src/queries/analytics/sessions/getSessionDataValues.ts b/src/queries/analytics/sessions/getSessionDataValues.ts index fd67e7a99a..c2fa5f6651 100644 --- a/src/queries/analytics/sessions/getSessionDataValues.ts +++ b/src/queries/analytics/sessions/getSessionDataValues.ts @@ -51,7 +51,7 @@ async function clickhouseQuery( data_type = 4, toString(date_trunc('hour', date_value)), string_value) as "value", count(*) as "total" - from session_data + from session_data final where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and data_key = {propertyName:String} From deb9dd60df39eb6979eb54e5a6def576251146ff Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 16 Aug 2024 00:35:08 -0700 Subject: [PATCH 100/132] Render UTC dates in sessions. --- .../sessions/[sessionId]/SessionActivity.tsx | 6 +--- .../[sessionId]/SessionDetailsPage.tsx | 7 +---- .../hooks/queries/useSessionActivity.ts | 9 ++---- src/components/hooks/useFilterParams.ts | 7 ++--- src/components/hooks/useTimezone.ts | 12 ++++++-- src/lib/clickhouse.ts | 3 +- .../sessions/[sessionId]/activity.ts | 13 ++------- .../analytics/sessions/getSessionActivity.ts | 28 ++++++------------- .../analytics/sessions/getWebsiteSession.ts | 6 ++-- .../analytics/sessions/getWebsiteSessions.ts | 6 ++-- 10 files changed, 35 insertions(+), 62 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx index def3f0ba02..3b0ad0080e 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx @@ -7,16 +7,12 @@ import styles from './SessionActivity.module.css'; export function SessionActivity({ websiteId, sessionId, - startDate, - endDate, }: { websiteId: string; sessionId: string; - startDate: string; - endDate: string; }) { const { formatDate } = useTimezone(); - const { data, isLoading } = useSessionActivity(websiteId, sessionId, startDate, endDate); + const { data, isLoading } = useSessionActivity(websiteId, sessionId); if (isLoading) { return ; diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx index d6a07edcf4..212f853304 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx @@ -28,12 +28,7 @@ export default function SessionDetailsPage({
- +
diff --git a/src/components/hooks/queries/useSessionActivity.ts b/src/components/hooks/queries/useSessionActivity.ts index 94676a9916..e6d7ffa0e4 100644 --- a/src/components/hooks/queries/useSessionActivity.ts +++ b/src/components/hooks/queries/useSessionActivity.ts @@ -1,17 +1,12 @@ import { useApi } from './useApi'; -export function useSessionActivity( - websiteId: string, - sessionId: string, - startDate: string, - endDate: string, -) { +export function useSessionActivity(websiteId: string, sessionId: string) { const { get, useQuery } = useApi(); return useQuery({ queryKey: ['session:activity', { websiteId, sessionId }], queryFn: () => { - return get(`/websites/${websiteId}/sessions/${sessionId}/activity`, { startDate, endDate }); + return get(`/websites/${websiteId}/sessions/${sessionId}/activity`); }, }); } diff --git a/src/components/hooks/useFilterParams.ts b/src/components/hooks/useFilterParams.ts index 343aea9fae..525f349298 100644 --- a/src/components/hooks/useFilterParams.ts +++ b/src/components/hooks/useFilterParams.ts @@ -1,19 +1,18 @@ import { useNavigation } from './useNavigation'; import { useDateRange } from './useDateRange'; import { useTimezone } from './useTimezone'; -import { zonedTimeToUtc } from 'date-fns-tz'; export function useFilterParams(websiteId: string) { const { dateRange } = useDateRange(websiteId); const { startDate, endDate, unit } = dateRange; - const { timezone } = useTimezone(); + const { timezone, toUtc } = useTimezone(); const { query: { url, referrer, title, query, host, os, browser, device, country, region, city, event }, } = useNavigation(); return { - startAt: +zonedTimeToUtc(startDate, timezone), - endAt: +zonedTimeToUtc(endDate, timezone), + startAt: +toUtc(startDate), + endAt: +toUtc(endDate), unit, timezone, url, diff --git a/src/components/hooks/useTimezone.ts b/src/components/hooks/useTimezone.ts index b5e58ea923..24cef02cce 100644 --- a/src/components/hooks/useTimezone.ts +++ b/src/components/hooks/useTimezone.ts @@ -1,6 +1,6 @@ import { setItem } from 'next-basics'; import { TIMEZONE_CONFIG } from 'lib/constants'; -import { formatInTimeZone } from 'date-fns-tz'; +import { formatInTimeZone, zonedTimeToUtc, utcToZonedTime } from 'date-fns-tz'; import useStore, { setTimezone } from 'store/app'; const selector = (state: { timezone: string }) => state.timezone; @@ -23,7 +23,15 @@ export function useTimezone() { ); }; - return { timezone, saveTimezone, formatDate }; + const toUtc = (date: Date | string | number) => { + return zonedTimeToUtc(date, timezone); + }; + + const fromUtc = (date: Date | string | number) => { + return utcToZonedTime(date, timezone); + }; + + return { timezone, saveTimezone, formatDate, toUtc, fromUtc }; } export default useTimezone; diff --git a/src/lib/clickhouse.ts b/src/lib/clickhouse.ts index 78f0323ed0..63027d7525 100644 --- a/src/lib/clickhouse.ts +++ b/src/lib/clickhouse.ts @@ -8,6 +8,7 @@ import { filtersToArray } from './params'; import { PageParams, QueryFilters, QueryOptions } from './types'; export const CLICKHOUSE_DATE_FORMATS = { + utc: '%Y-%m-%dT%H:%i:%SZ', second: '%Y-%m-%d %H:%i:%S', minute: '%Y-%m-%d %H:%i:00', hour: '%Y-%m-%d %H:00:00', @@ -47,7 +48,7 @@ function getClient() { return client; } -function getDateStringSQL(data: any, unit: string | number, timezone?: string) { +function getDateStringSQL(data: any, unit: string = 'utc', timezone?: string) { if (timezone) { return `formatDateTime(${data}, '${CLICKHOUSE_DATE_FORMATS[unit]}', '${timezone}')`; } diff --git a/src/pages/api/websites/[websiteId]/sessions/[sessionId]/activity.ts b/src/pages/api/websites/[websiteId]/sessions/[sessionId]/activity.ts index d1a763fb3b..8d1b234617 100644 --- a/src/pages/api/websites/[websiteId]/sessions/[sessionId]/activity.ts +++ b/src/pages/api/websites/[websiteId]/sessions/[sessionId]/activity.ts @@ -9,16 +9,12 @@ import { getSessionActivity } from 'queries'; export interface SessionActivityRequestQuery extends PageParams { websiteId: string; sessionId: string; - startDate: string; - endDate: string; } const schema = { GET: yup.object().shape({ websiteId: yup.string().uuid().required(), sessionId: yup.string().uuid().required(), - startDate: yup.string().required(), - endDate: yup.string().required(), }), }; @@ -30,19 +26,14 @@ export default async ( await useAuth(req, res); await useValidate(schema, req, res); - const { websiteId, sessionId, startDate, endDate } = req.query; + const { websiteId, sessionId } = req.query; if (req.method === 'GET') { if (!(await canViewWebsite(req.auth, websiteId))) { return unauthorized(res); } - const data = await getSessionActivity( - websiteId, - sessionId, - new Date(startDate + 'Z'), - new Date(endDate + 'Z'), - ); + const data = await getSessionActivity(websiteId, sessionId); return ok(res, data); } diff --git a/src/queries/analytics/sessions/getSessionActivity.ts b/src/queries/analytics/sessions/getSessionActivity.ts index bb7c141cb4..47c5f590d8 100644 --- a/src/queries/analytics/sessions/getSessionActivity.ts +++ b/src/queries/analytics/sessions/getSessionActivity.ts @@ -2,44 +2,32 @@ import clickhouse from 'lib/clickhouse'; import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; import prisma from 'lib/prisma'; -export async function getSessionActivity( - ...args: [websiteId: string, sessionId: string, startDate: Date, endDate: Date] -) { +export async function getSessionActivity(...args: [websiteId: string, sessionId: string]) { return runQuery({ [PRISMA]: () => relationalQuery(...args), [CLICKHOUSE]: () => clickhouseQuery(...args), }); } -async function relationalQuery( - websiteId: string, - sessionId: string, - startDate: Date, - endDate: Date, -) { +async function relationalQuery(websiteId: string, sessionId: string) { return prisma.client.websiteEvent.findMany({ where: { id: sessionId, websiteId, - createdAt: { gte: startDate, lte: endDate }, }, + take: 500, }); } -async function clickhouseQuery( - websiteId: string, - sessionId: string, - startDate: Date, - endDate: Date, -) { - const { rawQuery } = clickhouse; +async function clickhouseQuery(websiteId: string, sessionId: string) { + const { rawQuery, getDateStringSQL } = clickhouse; return rawQuery( ` select session_id as id, website_id as websiteId, - created_at as createdAt, + ${getDateStringSQL('created_at')} as createdAt, url_path as urlPath, url_query as urlQuery, referrer_domain as referrerDomain, @@ -50,9 +38,9 @@ async function clickhouseQuery( from website_event where website_id = {websiteId:UUID} and session_id = {sessionId:UUID} - and created_at between {startDate:DateTime64} and {endDate:DateTime64} order by created_at desc + limit 500 `, - { websiteId, sessionId, startDate, endDate }, + { websiteId, sessionId }, ); } diff --git a/src/queries/analytics/sessions/getWebsiteSession.ts b/src/queries/analytics/sessions/getWebsiteSession.ts index 22c186420a..6f672e7d8d 100644 --- a/src/queries/analytics/sessions/getWebsiteSession.ts +++ b/src/queries/analytics/sessions/getWebsiteSession.ts @@ -19,7 +19,7 @@ async function relationalQuery(websiteId: string, sessionId: string) { } async function clickhouseQuery(websiteId: string, sessionId: string) { - const { rawQuery } = clickhouse; + const { rawQuery, getDateStringSQL } = clickhouse; return rawQuery( ` @@ -34,8 +34,8 @@ async function clickhouseQuery(websiteId: string, sessionId: string) { country, subdivision1, city, - min(min_time) as firstAt, - max(max_time) as lastAt, + ${getDateStringSQL('min(min_time)')} as firstAt, + ${getDateStringSQL('max(max_time)')} as lastAt, uniq(visit_id) visits, sum(views) as views, sum(events) as events, diff --git a/src/queries/analytics/sessions/getWebsiteSessions.ts b/src/queries/analytics/sessions/getWebsiteSessions.ts index 60f30b6b9e..1ea3ef49e6 100644 --- a/src/queries/analytics/sessions/getWebsiteSessions.ts +++ b/src/queries/analytics/sessions/getWebsiteSessions.ts @@ -24,7 +24,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters, pagePar } async function clickhouseQuery(websiteId: string, filters: QueryFilters, pageParams?: PageParams) { - const { pagedQuery, parseFilters } = clickhouse; + const { pagedQuery, parseFilters, getDateStringSQL } = clickhouse; const { params, dateQuery, filterQuery } = await parseFilters(websiteId, filters); return pagedQuery( @@ -42,8 +42,8 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar country, subdivision1, city, - min(min_time) as firstAt, - max(max_time) as lastAt, + ${getDateStringSQL('min(min_time)')} as firstAt, + ${getDateStringSQL('max(max_time)')} as lastAt, uniq(visit_id) as visits, sumIf(views, event_type = 1) as views from website_event_stats_hourly From fc1fc5807ec0682ad6996362c842e0fe74fe2c63 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 16 Aug 2024 19:44:16 -0700 Subject: [PATCH 101/132] Session properties. --- .../sessions/[sessionId]/SessionActivity.tsx | 6 +++- .../[sessionId]/SessionDetailsPage.tsx | 7 ++++- .../hooks/queries/useSessionActivity.ts | 15 ++++++++-- src/declaration.d.ts | 1 - src/lib/clickhouse.ts | 6 ++++ .../[websiteId]/session-data/properties.ts | 4 +-- .../sessions/[sessionId]/activity.ts | 11 ++++++-- src/queries/analytics/events/saveEvent.ts | 4 +-- .../analytics/sessions/getSessionActivity.ts | 28 +++++++++++++------ .../analytics/sessions/saveSessionData.ts | 4 +-- 10 files changed, 64 insertions(+), 22 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx index 3b0ad0080e..663934936e 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx @@ -7,12 +7,16 @@ import styles from './SessionActivity.module.css'; export function SessionActivity({ websiteId, sessionId, + startDate, + endDate, }: { websiteId: string; sessionId: string; + startDate: Date; + endDate: Date; }) { const { formatDate } = useTimezone(); - const { data, isLoading } = useSessionActivity(websiteId, sessionId); + const { data, isLoading } = useSessionActivity(websiteId, sessionId, startDate, endDate); if (isLoading) { return ; diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx index 212f853304..d6a07edcf4 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx @@ -28,7 +28,12 @@ export default function SessionDetailsPage({
- +
diff --git a/src/components/hooks/queries/useSessionActivity.ts b/src/components/hooks/queries/useSessionActivity.ts index e6d7ffa0e4..16c139ab38 100644 --- a/src/components/hooks/queries/useSessionActivity.ts +++ b/src/components/hooks/queries/useSessionActivity.ts @@ -1,12 +1,21 @@ import { useApi } from './useApi'; -export function useSessionActivity(websiteId: string, sessionId: string) { +export function useSessionActivity( + websiteId: string, + sessionId: string, + startDate: Date, + endDate: Date, +) { const { get, useQuery } = useApi(); return useQuery({ - queryKey: ['session:activity', { websiteId, sessionId }], + queryKey: ['session:activity', { websiteId, sessionId, startDate, endDate }], queryFn: () => { - return get(`/websites/${websiteId}/sessions/${sessionId}/activity`); + return get(`/websites/${websiteId}/sessions/${sessionId}/activity`, { + startAt: +new Date(startDate), + endAt: +new Date(endDate), + }); }, + enabled: Boolean(websiteId && sessionId && startDate && endDate), }); } diff --git a/src/declaration.d.ts b/src/declaration.d.ts index d968c14d5e..986adf2723 100644 --- a/src/declaration.d.ts +++ b/src/declaration.d.ts @@ -1,5 +1,4 @@ declare module 'cors'; -declare module 'dateformat'; declare module 'debug'; declare module 'chartjs-adapter-date-fns'; declare module 'md5'; diff --git a/src/lib/clickhouse.ts b/src/lib/clickhouse.ts index 63027d7525..474417b93c 100644 --- a/src/lib/clickhouse.ts +++ b/src/lib/clickhouse.ts @@ -1,4 +1,5 @@ import { ClickHouseClient, createClient } from '@clickhouse/client'; +import { formatInTimeZone } from 'date-fns-tz'; import debug from 'debug'; import { CLICKHOUSE } from 'lib/db'; import { DEFAULT_PAGE_SIZE, OPERATORS } from './constants'; @@ -48,6 +49,10 @@ function getClient() { return client; } +function getUTCString(date?: Date) { + return formatInTimeZone(date || new Date(), 'UTC', 'yyyy-MM-dd HH:mm:ss'); +} + function getDateStringSQL(data: any, unit: string = 'utc', timezone?: string) { if (timezone) { return `formatDateTime(${data}, '${CLICKHOUSE_DATE_FORMATS[unit]}', '${timezone}')`; @@ -221,6 +226,7 @@ export default { getDateStringSQL, getDateSQL, getFilterQuery, + getUTCString, parseFilters, pagedQuery, findUnique, diff --git a/src/pages/api/websites/[websiteId]/session-data/properties.ts b/src/pages/api/websites/[websiteId]/session-data/properties.ts index 4cd2e1e62e..92e182d298 100644 --- a/src/pages/api/websites/[websiteId]/session-data/properties.ts +++ b/src/pages/api/websites/[websiteId]/session-data/properties.ts @@ -6,7 +6,7 @@ import { methodNotAllowed, ok, unauthorized } from 'next-basics'; import { getSessionDataProperties } from 'queries'; import * as yup from 'yup'; -export interface EventDataFieldsRequestQuery { +export interface SessionDataFieldsRequestQuery { websiteId: string; startAt: string; endAt: string; @@ -23,7 +23,7 @@ const schema = { }; export default async ( - req: NextApiRequestQueryBody, + req: NextApiRequestQueryBody, res: NextApiResponse, ) => { await useCors(req, res); diff --git a/src/pages/api/websites/[websiteId]/sessions/[sessionId]/activity.ts b/src/pages/api/websites/[websiteId]/sessions/[sessionId]/activity.ts index 8d1b234617..2b0fc0847e 100644 --- a/src/pages/api/websites/[websiteId]/sessions/[sessionId]/activity.ts +++ b/src/pages/api/websites/[websiteId]/sessions/[sessionId]/activity.ts @@ -9,12 +9,16 @@ import { getSessionActivity } from 'queries'; export interface SessionActivityRequestQuery extends PageParams { websiteId: string; sessionId: string; + startAt: number; + endAt: number; } const schema = { GET: yup.object().shape({ websiteId: yup.string().uuid().required(), sessionId: yup.string().uuid().required(), + startAt: yup.number().integer(), + endAt: yup.number().integer(), }), }; @@ -26,14 +30,17 @@ export default async ( await useAuth(req, res); await useValidate(schema, req, res); - const { websiteId, sessionId } = req.query; + const { websiteId, sessionId, startAt, endAt } = req.query; if (req.method === 'GET') { if (!(await canViewWebsite(req.auth, websiteId))) { return unauthorized(res); } - const data = await getSessionActivity(websiteId, sessionId); + const startDate = new Date(+startAt); + const endDate = new Date(+endAt); + + const data = await getSessionActivity(websiteId, sessionId, startDate, endDate); return ok(res, data); } diff --git a/src/queries/analytics/events/saveEvent.ts b/src/queries/analytics/events/saveEvent.ts index 5e21e303ff..6c0f917baa 100644 --- a/src/queries/analytics/events/saveEvent.ts +++ b/src/queries/analytics/events/saveEvent.ts @@ -135,10 +135,10 @@ async function clickhouseQuery(data: { city, ...args } = data; - const { insert } = clickhouse; + const { insert, getUTCString } = clickhouse; const { sendMessage } = kafka; const eventId = uuid(); - const createdAt = new Date().toISOString(); + const createdAt = getUTCString(); const message = { ...args, diff --git a/src/queries/analytics/sessions/getSessionActivity.ts b/src/queries/analytics/sessions/getSessionActivity.ts index 47c5f590d8..c50a82d97e 100644 --- a/src/queries/analytics/sessions/getSessionActivity.ts +++ b/src/queries/analytics/sessions/getSessionActivity.ts @@ -2,32 +2,43 @@ import clickhouse from 'lib/clickhouse'; import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; import prisma from 'lib/prisma'; -export async function getSessionActivity(...args: [websiteId: string, sessionId: string]) { +export async function getSessionActivity( + ...args: [websiteId: string, sessionId: string, startDate: Date, endDate: Date] +) { return runQuery({ [PRISMA]: () => relationalQuery(...args), [CLICKHOUSE]: () => clickhouseQuery(...args), }); } -async function relationalQuery(websiteId: string, sessionId: string) { +async function relationalQuery( + websiteId: string, + sessionId: string, + startDate: Date, + endDate: Date, +) { return prisma.client.websiteEvent.findMany({ where: { id: sessionId, websiteId, + createdAt: { gte: startDate, lte: endDate }, }, take: 500, }); } -async function clickhouseQuery(websiteId: string, sessionId: string) { - const { rawQuery, getDateStringSQL } = clickhouse; +async function clickhouseQuery( + websiteId: string, + sessionId: string, + startDate: Date, + endDate: Date, +) { + const { rawQuery } = clickhouse; return rawQuery( ` select - session_id as id, - website_id as websiteId, - ${getDateStringSQL('created_at')} as createdAt, + created_at as createdAt, url_path as urlPath, url_query as urlQuery, referrer_domain as referrerDomain, @@ -38,9 +49,10 @@ async function clickhouseQuery(websiteId: string, sessionId: string) { from website_event where website_id = {websiteId:UUID} and session_id = {sessionId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} order by created_at desc limit 500 `, - { websiteId, sessionId }, + { websiteId, sessionId, startDate, endDate }, ); } diff --git a/src/queries/analytics/sessions/saveSessionData.ts b/src/queries/analytics/sessions/saveSessionData.ts index d932f7ed27..5259239a76 100644 --- a/src/queries/analytics/sessions/saveSessionData.ts +++ b/src/queries/analytics/sessions/saveSessionData.ts @@ -80,9 +80,9 @@ async function clickhouseQuery(data: { }) { const { websiteId, sessionId, sessionData } = data; - const { insert } = clickhouse; + const { insert, getUTCString } = clickhouse; const { sendMessages } = kafka; - const createdAt = new Date().toISOString(); + const createdAt = getUTCString(); const jsonKeys = flattenJSON(sessionData); From 53d8548909ea68cdb8ab7c6ef22c798444d0c4b4 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 16 Aug 2024 23:42:26 -0700 Subject: [PATCH 102/132] Weekly session data. --- .../[websiteId]/sessions/SessionsPage.tsx | 6 +- .../sessions/SessionsWeekly.module.css | 40 ++++++++++ .../[websiteId]/sessions/SessionsWeekly.tsx | 78 +++++++++++++++++++ src/components/charts/BubbleChart.tsx | 27 +++++++ src/components/common/LoadingPanel.module.css | 10 +++ src/components/common/LoadingPanel.tsx | 6 +- src/components/hooks/index.ts | 1 + .../hooks/queries/useWebsiteSessionsWeekly.ts | 24 ++++++ src/lib/date.ts | 14 ++++ src/lib/prisma.ts | 13 ++++ .../websites/[websiteId]/sessions/weekly.ts | 47 +++++++++++ .../sessions/getWebsiteSessionsWeekly.ts | 69 ++++++++++++++++ src/queries/index.ts | 1 + 13 files changed, 331 insertions(+), 5 deletions(-) create mode 100644 src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css create mode 100644 src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx create mode 100644 src/components/charts/BubbleChart.tsx create mode 100644 src/components/hooks/queries/useWebsiteSessionsWeekly.ts create mode 100644 src/pages/api/websites/[websiteId]/sessions/weekly.ts create mode 100644 src/queries/analytics/sessions/getWebsiteSessionsWeekly.ts diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx index 1b3ba6dd4e..30fd193dbf 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx @@ -8,6 +8,7 @@ import { GridRow } from 'components/layout/Grid'; import { Item, Tabs } from 'react-basics'; import { useState } from 'react'; import { useMessages } from 'components/hooks'; +import SessionsWeekly from './SessionsWeekly'; export function SessionsPage({ websiteId }) { const [tab, setTab] = useState('activity'); @@ -17,8 +18,9 @@ export function SessionsPage({ websiteId }) { <> - - + + + setTab(value)} style={{ marginBottom: 30 }}> {formatMessage(labels.activity)} diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css new file mode 100644 index 0000000000..4b41d87ceb --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css @@ -0,0 +1,40 @@ +.week { + display: flex; + justify-content: space-between; + position: relative; +} + +.header { + text-align: center; + font-weight: 700; + margin-bottom: 10px; +} + +.day { + display: flex; + flex-direction: column; + gap: 5px; + position: relative; +} + +.cell { + display: flex; + align-items: center; + justify-content: center; + background-color: var(--base100); + width: 20px; + height: 20px; +} + +.hour { + font-weight: 700; + color: var(--font-color300); + background-color: transparent; +} + +.block { + background-color: var(--primary400); + width: 20px; + height: 20px; + border-radius: 3px; +} diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx new file mode 100644 index 0000000000..6c0984ea53 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx @@ -0,0 +1,78 @@ +import { format } from 'date-fns'; +import { useLocale, useMessages, useWebsiteSessionsWeekly } from 'components/hooks'; +import { LoadingPanel } from 'components/common/LoadingPanel'; +import { getDayOfWeekAsDate } from 'lib/date'; +import styles from './SessionsWeekly.module.css'; +import classNames from 'classnames'; +import { TooltipPopup } from 'react-basics'; + +export function SessionsWeekly({ websiteId }: { websiteId: string }) { + const { data, ...props } = useWebsiteSessionsWeekly(websiteId); + const { dateLocale } = useLocale(); + const { labels, formatMessage } = useMessages(); + + const [, max] = data + ? data.reduce((arr: number[], hours: number[], index: number) => { + const min = Math.min(...hours); + const max = Math.max(...hours); + + if (index === 0) { + return [min, max]; + } + + if (min < arr[0]) { + arr[0] = min; + } + + if (max > arr[1]) { + arr[1] = max; + } + + return arr; + }, []) + : []; + + return ( + +
+
+
 
+ {Array(24) + .fill(null) + .map((_, i) => { + return ( +
+ {i.toString().padStart(2, '0')} +
+ ); + })} +
+ {data?.map((day: number[], index: number) => { + return ( +
+
+ {format(getDayOfWeekAsDate(index), 'EEE', { locale: dateLocale })} +
+ {day?.map((hour: number) => { + return ( +
+ {hour > 0 && ( + +
+ + )} +
+ ); + })} +
+ ); + })} +
+ + ); +} + +export default SessionsWeekly; diff --git a/src/components/charts/BubbleChart.tsx b/src/components/charts/BubbleChart.tsx new file mode 100644 index 0000000000..956e260cbd --- /dev/null +++ b/src/components/charts/BubbleChart.tsx @@ -0,0 +1,27 @@ +import { Chart, ChartProps } from 'components/charts/Chart'; +import { useState } from 'react'; +import { StatusLight } from 'react-basics'; +import { formatLongNumber } from 'lib/format'; + +export interface BubbleChartProps extends ChartProps { + type?: 'bubble'; +} + +export default function BubbleChart(props: BubbleChartProps) { + const [tooltip, setTooltip] = useState(null); + const { type = 'bubble' } = props; + + const handleTooltip = ({ tooltip }) => { + const { labelColors, dataPoints } = tooltip; + + setTooltip( + tooltip.opacity ? ( + + {formatLongNumber(dataPoints?.[0]?.raw)} {dataPoints?.[0]?.label} + + ) : null, + ); + }; + + return ; +} diff --git a/src/components/common/LoadingPanel.module.css b/src/components/common/LoadingPanel.module.css index 2dc8b75ee3..00d6cbb4f4 100644 --- a/src/components/common/LoadingPanel.module.css +++ b/src/components/common/LoadingPanel.module.css @@ -3,4 +3,14 @@ flex-direction: column; position: relative; flex: 1; + height: 100%; +} + +.loading { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + margin: auto; } diff --git a/src/components/common/LoadingPanel.tsx b/src/components/common/LoadingPanel.tsx index 487252be6a..36de93651a 100644 --- a/src/components/common/LoadingPanel.tsx +++ b/src/components/common/LoadingPanel.tsx @@ -1,9 +1,9 @@ import { ReactNode } from 'react'; -import styles from './LoadingPanel.module.css'; import classNames from 'classnames'; -import ErrorMessage from 'components/common/ErrorMessage'; import { Loading } from 'react-basics'; +import ErrorMessage from 'components/common/ErrorMessage'; import Empty from 'components/common/Empty'; +import styles from './LoadingPanel.module.css'; export function LoadingPanel({ data, @@ -27,7 +27,7 @@ export function LoadingPanel({ return (
- {isLoading && !isFetched && } + {isLoading && !isFetched && } {error && } {!error && isEmpty && } {!error && !isEmpty && data && children} diff --git a/src/components/hooks/index.ts b/src/components/hooks/index.ts index 4e9c49d693..1be99732e0 100644 --- a/src/components/hooks/index.ts +++ b/src/components/hooks/index.ts @@ -14,6 +14,7 @@ export * from './queries/useSessionDataProperties'; export * from './queries/useSessionDataValues'; export * from './queries/useWebsiteSession'; export * from './queries/useWebsiteSessions'; +export * from './queries/useWebsiteSessionsWeekly'; export * from './queries/useShareToken'; export * from './queries/useTeam'; export * from './queries/useTeams'; diff --git a/src/components/hooks/queries/useWebsiteSessionsWeekly.ts b/src/components/hooks/queries/useWebsiteSessionsWeekly.ts new file mode 100644 index 0000000000..5df543f507 --- /dev/null +++ b/src/components/hooks/queries/useWebsiteSessionsWeekly.ts @@ -0,0 +1,24 @@ +import { useApi } from './useApi'; +import useModified from '../useModified'; +import { useFilterParams } from 'components/hooks/useFilterParams'; + +export function useWebsiteSessionsWeekly( + websiteId: string, + params?: { [key: string]: string | number }, +) { + const { get, useQuery } = useApi(); + const { modified } = useModified(`sessions`); + const filters = useFilterParams(websiteId); + + return useQuery({ + queryKey: ['sessions', { websiteId, modified, ...params, ...filters }], + queryFn: () => { + return get(`/websites/${websiteId}/sessions/weekly`, { + ...params, + ...filters, + }); + }, + }); +} + +export default useWebsiteSessionsWeekly; diff --git a/src/lib/date.ts b/src/lib/date.ts index 2fb24073a6..b8bfa6c765 100644 --- a/src/lib/date.ts +++ b/src/lib/date.ts @@ -34,6 +34,7 @@ import { addWeeks, subWeeks, endOfMinute, + isSameDay, } from 'date-fns'; import { getDateLocale } from 'lib/lang'; import { DateRange } from 'lib/types'; @@ -336,3 +337,16 @@ export function getCompareDate(compare: string, startDate: Date, endDate: Date) return { startDate: subMinutes(startDate, diff), endDate: subMinutes(endDate, diff) }; } + +export function getDayOfWeekAsDate(dayOfWeek: number) { + const startOfWeekDay = startOfWeek(new Date()); + const daysToAdd = [0, 1, 2, 3, 4, 5, 6].indexOf(dayOfWeek); + let currentDate = addDays(startOfWeekDay, daysToAdd); + + // Ensure we're not returning a past date + if (isSameDay(currentDate, startOfWeekDay)) { + currentDate = addDays(currentDate, 7); + } + + return currentDate; +} diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts index 28835414fc..1fbb3cd4f7 100644 --- a/src/lib/prisma.ts +++ b/src/lib/prisma.ts @@ -81,6 +81,18 @@ function getDateSQL(field: string, unit: string, timezone?: string): string { } } +function getDateWeeklySQL(field: string) { + const db = getDatabaseType(); + + if (db === POSTGRESQL) { + return `EXTRACT(DOW FROM ${field})`; + } + + if (db === MYSQL) { + return `DAYOFWEEK(${field})-1`; + } +} + export function getTimestampSQL(field: string) { const db = getDatabaseType(); @@ -284,6 +296,7 @@ export default { getCastColumnQuery, getDayDiffQuery, getDateSQL, + getDateWeeklySQL, getFilterQuery, getSearchParameters, getTimestampDiffSQL, diff --git a/src/pages/api/websites/[websiteId]/sessions/weekly.ts b/src/pages/api/websites/[websiteId]/sessions/weekly.ts new file mode 100644 index 0000000000..f33970d0b7 --- /dev/null +++ b/src/pages/api/websites/[websiteId]/sessions/weekly.ts @@ -0,0 +1,47 @@ +import * as yup from 'yup'; +import { canViewWebsite } from 'lib/auth'; +import { useAuth, useCors, useValidate } from 'lib/middleware'; +import { NextApiRequestQueryBody, PageParams } from 'lib/types'; +import { NextApiResponse } from 'next'; +import { methodNotAllowed, ok, unauthorized } from 'next-basics'; +import { pageInfo } from 'lib/schema'; +import { getWebsiteSessionsWeekly } from 'queries'; + +export interface ReportsRequestQuery extends PageParams { + websiteId: string; +} + +const schema = { + GET: yup.object().shape({ + websiteId: yup.string().uuid().required(), + startAt: yup.number().integer().required(), + endAt: yup.number().integer().min(yup.ref('startAt')).required(), + ...pageInfo, + }), +}; + +export default async ( + req: NextApiRequestQueryBody, + res: NextApiResponse, +) => { + await useCors(req, res); + await useAuth(req, res); + await useValidate(schema, req, res); + + const { websiteId, startAt, endAt } = req.query; + + if (req.method === 'GET') { + if (!(await canViewWebsite(req.auth, websiteId))) { + return unauthorized(res); + } + + const startDate = new Date(+startAt); + const endDate = new Date(+endAt); + + const data = await getWebsiteSessionsWeekly(websiteId, { startDate, endDate }); + + return ok(res, data); + } + + return methodNotAllowed(res); +}; diff --git a/src/queries/analytics/sessions/getWebsiteSessionsWeekly.ts b/src/queries/analytics/sessions/getWebsiteSessionsWeekly.ts new file mode 100644 index 0000000000..9031edf510 --- /dev/null +++ b/src/queries/analytics/sessions/getWebsiteSessionsWeekly.ts @@ -0,0 +1,69 @@ +import prisma from 'lib/prisma'; +import clickhouse from 'lib/clickhouse'; +import { runQuery, PRISMA, CLICKHOUSE } from 'lib/db'; +import { QueryFilters } from 'lib/types'; + +export async function getWebsiteSessionsWeekly( + ...args: [websiteId: string, filters?: QueryFilters] +) { + return runQuery({ + [PRISMA]: () => relationalQuery(...args), + [CLICKHOUSE]: () => clickhouseQuery(...args), + }); +} + +async function relationalQuery(websiteId: string, filters: QueryFilters) { + const { rawQuery, getDateWeeklySQL, parseFilters } = prisma; + const { params } = await parseFilters(websiteId, filters); + + return rawQuery( + ` + select + ${getDateWeeklySQL('created_at')} as time, + count(distinct session_id) as value + from website_event_stats_hourly + where website_id = {{websiteId::uuid}} + and created_at between {{startDate}} and {{endDate}} + group by time + order by 2 + `, + params, + ).then(formatResults); +} + +async function clickhouseQuery(websiteId: string, filters: QueryFilters) { + const { rawQuery } = clickhouse; + const { startDate, endDate } = filters; + + return rawQuery( + ` + select + formatDateTime(created_at, '%w:%H') as time, + count(distinct session_id) as value + from website_event_stats_hourly + where website_id = {websiteId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} + group by time + order by time + `, + { websiteId, startDate, endDate }, + ).then(formatResults); +} + +function formatResults(data: any) { + const days = []; + + for (let i = 0; i < 7; i++) { + days.push([]); + + for (let j = 0; j < 24; j++) { + days[i].push( + Number( + data.find(({ time }) => time === `${i}:${j.toString().padStart(2, '0')}`)?.value || 0, + ), + ); + } + } + + return days; +} diff --git a/src/queries/index.ts b/src/queries/index.ts index f9c44dba94..26c1df09a6 100644 --- a/src/queries/index.ts +++ b/src/queries/index.ts @@ -26,6 +26,7 @@ export * from './analytics/sessions/getSessionDataProperties'; export * from './analytics/sessions/getSessionDataValues'; export * from './analytics/sessions/getSessionMetrics'; export * from './analytics/sessions/getWebsiteSessions'; +export * from './analytics/sessions/getWebsiteSessionsWeekly'; export * from './analytics/sessions/getSessionActivity'; export * from './analytics/sessions/getSessionStats'; export * from './analytics/sessions/saveSessionData'; From b6b1488ddf1856bad9d225ce50aa2a1f31559f32 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Sat, 17 Aug 2024 20:00:58 -0700 Subject: [PATCH 103/132] Updated packages. --- package.json | 10 +-- yarn.lock | 168 +++++++++++++++++++++++++-------------------------- 2 files changed, 89 insertions(+), 89 deletions(-) diff --git a/package.json b/package.json index d6217788d3..351596fb1a 100644 --- a/package.json +++ b/package.json @@ -64,12 +64,12 @@ ".next/cache" ], "dependencies": { - "@clickhouse/client": "^1.3.0", + "@clickhouse/client": "^1.4.1", "@date-fns/utc": "^1.2.0", "@dicebear/collection": "^9.2.1", "@dicebear/core": "^9.2.1", "@fontsource/inter": "^4.5.15", - "@prisma/client": "5.17.0", + "@prisma/client": "5.18.0", "@prisma/extension-read-replicas": "^0.3.0", "@react-spring/web": "^9.7.3", "@tanstack/react-query": "^5.28.6", @@ -95,7 +95,7 @@ "is-ci": "^3.0.1", "is-docker": "^3.0.0", "is-localhost-ip": "^1.4.0", - "isbot": "^5.1.1", + "isbot": "^5.1.16", "kafkajs": "^2.1.0", "maxmind": "^4.3.6", "md5": "^2.3.0", @@ -104,7 +104,7 @@ "next-basics": "^0.39.0", "node-fetch": "^3.2.8", "npm-run-all": "^4.1.5", - "prisma": "5.17.0", + "prisma": "5.18.0", "react": "^18.2.0", "react-basics": "^0.125.0", "react-beautiful-dnd": "^13.1.0", @@ -119,7 +119,7 @@ "thenby": "^1.3.4", "uuid": "^9.0.0", "yup": "^0.32.11", - "zustand": "^4.3.8" + "zustand": "^4.5.5" }, "devDependencies": { "@formatjs/cli": "^4.2.29", diff --git a/yarn.lock b/yarn.lock index cf78cc0c6a..0abea883e2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1199,17 +1199,17 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@clickhouse/client-common@1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@clickhouse/client-common/-/client-common-1.4.0.tgz#6936f667ba901724bf9174cc2b783507e921868f" - integrity sha512-kglG8YyWnR1K24RckUf5ZdNTN0U0s+a1j/bpCO4ZjzjO87ICgWlXFVD22pZqSACW7/2IIi1IkzbwtxKI2s/MOw== +"@clickhouse/client-common@1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@clickhouse/client-common/-/client-common-1.4.1.tgz#23c6ffbff5717729e533301c9f595cd79af221ef" + integrity sha512-f5eoTrUSDplrMoi3ddeZ0MzGTn0iGMByEQ8j63eVMoBSOI2+F6jEIPcW2tWofT79Rvnn3RRlveYcShiaIiCJyw== -"@clickhouse/client@^1.3.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@clickhouse/client/-/client-1.4.0.tgz#84e17f8c0f2ba109d8e76c3d4f5830aeff5e9459" - integrity sha512-O4mbFPM/wQtFck01ghYI2mnNHv9jSFEiQBsTCH4t6MKeGHNAPkJGaFGv+KycLTv6zjnQNjiUGdXDMVRema5SyA== +"@clickhouse/client@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@clickhouse/client/-/client-1.4.1.tgz#be4303d81f42835c5f168de61babd1a67d821f5a" + integrity sha512-12iV+MeykxdQySRFHwaVU+hKUv3JP6kdwOI+z3zzyfPVYHynTlV8emJjjGZR0+VfRaj3PCMuQfryfsJ82nh9WQ== dependencies: - "@clickhouse/client-common" "1.4.0" + "@clickhouse/client-common" "1.4.1" "@colors/colors@1.5.0": version "1.5.0" @@ -2280,51 +2280,51 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@prisma/client@5.17.0": - version "5.17.0" - resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.17.0.tgz#9079947bd749689c2dabfb9ecc70a24ebefb1f43" - integrity sha512-N2tnyKayT0Zf7mHjwEyE8iG7FwTmXDHFZ1GnNhQp0pJUObsuel4ZZ1XwfuAYkq5mRIiC/Kot0kt0tGCfLJ70Jw== +"@prisma/client@5.18.0": + version "5.18.0" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.18.0.tgz#526e4281a448f214c0ff81d65c39243608c98294" + integrity sha512-BWivkLh+af1kqC89zCJYkHsRcyWsM8/JHpsDMM76DjP3ZdEquJhXa4IeX+HkWPnwJ5FanxEJFZZDTWiDs/Kvyw== -"@prisma/debug@5.17.0": - version "5.17.0" - resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-5.17.0.tgz#a765105848993984535b6066f8ebc6e6ead26533" - integrity sha512-l7+AteR3P8FXiYyo496zkuoiJ5r9jLQEdUuxIxNCN1ud8rdbH3GTxm+f+dCyaSv9l9WY+29L9czaVRXz9mULfg== +"@prisma/debug@5.18.0": + version "5.18.0" + resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-5.18.0.tgz#527799e044d2903a35945e61ac2d8916e4b61ead" + integrity sha512-f+ZvpTLidSo3LMJxQPVgAxdAjzv5OpzAo/eF8qZqbwvgi2F5cTOI9XCpdRzJYA0iGfajjwjOKKrVq64vkxEfUw== -"@prisma/engines-version@5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053": - version "5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053" - resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053.tgz#3c7cc1ef3ebc34cbd069e5873b9982f2aabf5acd" - integrity sha512-tUuxZZysZDcrk5oaNOdrBnnkoTtmNQPkzINFDjz7eG6vcs9AVDmA/F6K5Plsb2aQc/l5M2EnFqn3htng9FA4hg== +"@prisma/engines-version@5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169": + version "5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169.tgz#203426ebf4ec4e1acce7da4a59ec8f0df92b29e7" + integrity sha512-a/+LpJj8vYU3nmtkg+N3X51ddbt35yYrRe8wqHTJtYQt7l1f8kjIBcCs6sHJvodW/EK5XGvboOiwm47fmNrbgg== -"@prisma/engines@5.17.0": - version "5.17.0" - resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.17.0.tgz#74dd1aabb22675892760b3cf69a448e3aef4616b" - integrity sha512-+r+Nf+JP210Jur+/X8SIPLtz+uW9YA4QO5IXA+KcSOBe/shT47bCcRMTYCbOESw3FFYFTwe7vU6KTWHKPiwvtg== +"@prisma/engines@5.18.0": + version "5.18.0" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.18.0.tgz#26ea46e26498be622407cf95663d7fb4c39c895b" + integrity sha512-ofmpGLeJ2q2P0wa/XaEgTnX/IsLnvSp/gZts0zjgLNdBhfuj2lowOOPmDcfKljLQUXMvAek3lw5T01kHmCG8rg== dependencies: - "@prisma/debug" "5.17.0" - "@prisma/engines-version" "5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053" - "@prisma/fetch-engine" "5.17.0" - "@prisma/get-platform" "5.17.0" + "@prisma/debug" "5.18.0" + "@prisma/engines-version" "5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169" + "@prisma/fetch-engine" "5.18.0" + "@prisma/get-platform" "5.18.0" "@prisma/extension-read-replicas@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@prisma/extension-read-replicas/-/extension-read-replicas-0.3.0.tgz#2842a7c928f957c1dd58a6256104797596d43426" integrity sha512-F9+rSmYday6GT2qjhJtkZcBOpLO5LtpvFcMGqrBDHf+78LEdSuxfFjOxYlNuqk4B+th62yxpbhfpmB9/Mca14Q== -"@prisma/fetch-engine@5.17.0": - version "5.17.0" - resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-5.17.0.tgz#f718dc7426411d1ebeeee53e2d0d38652387f87c" - integrity sha512-ESxiOaHuC488ilLPnrv/tM2KrPhQB5TRris/IeIV4ZvUuKeaicCl4Xj/JCQeG9IlxqOgf1cCg5h5vAzlewN91Q== +"@prisma/fetch-engine@5.18.0": + version "5.18.0" + resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-5.18.0.tgz#5b343e2b36b27e2713901ddd032ddd6932b3d55f" + integrity sha512-I/3u0x2n31rGaAuBRx2YK4eB7R/1zCuayo2DGwSpGyrJWsZesrV7QVw7ND0/Suxeo/vLkJ5OwuBqHoCxvTHpOg== dependencies: - "@prisma/debug" "5.17.0" - "@prisma/engines-version" "5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053" - "@prisma/get-platform" "5.17.0" + "@prisma/debug" "5.18.0" + "@prisma/engines-version" "5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169" + "@prisma/get-platform" "5.18.0" -"@prisma/get-platform@5.17.0": - version "5.17.0" - resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-5.17.0.tgz#89fdcae2adddebbbf0e7bd0474a6c49d6023519b" - integrity sha512-UlDgbRozCP1rfJ5Tlkf3Cnftb6srGrEQ4Nm3og+1Se2gWmCZ0hmPIi+tQikGDUVLlvOWx3Gyi9LzgRP+HTXV9w== +"@prisma/get-platform@5.18.0": + version "5.18.0" + resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-5.18.0.tgz#0dc4c82fe9a4971f4519a57cb2dd69d8e0df4b71" + integrity sha512-Tk+m7+uhqcKDgnMnFN0lRiH7Ewea0OEsZZs9pqXa7i3+7svS3FSCqDBCaM9x5fmhhkufiG0BtunJVDka+46DlA== dependencies: - "@prisma/debug" "5.17.0" + "@prisma/debug" "5.18.0" "@react-spring/animated@~9.7.4": version "9.7.4" @@ -2633,9 +2633,9 @@ integrity sha512-POQxm42IUp6n89kKWF4IZi18v3fxQWFRolvBA6phNVmA8psdfB1MvDnGacCJdS+EOX12w/CyHM62z//rHmYmvw== "@tanstack/react-query@^5.28.6": - version "5.51.21" - resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.51.21.tgz#cdd14677bcc809a83e01b6c38842c841ce7420af" - integrity sha512-Q/V81x3sAYgCsxjwOkfLXfrmoG+FmDhLeHH5okC/Bp8Aaw2c33lbEo/mMcMnkxUPVtB2FLpzHT0tq3c+OlZEbw== + version "5.51.23" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.51.23.tgz#83c223f4cb6054b206de8856b73ca7e41a63ba1f" + integrity sha512-CfJCfX45nnVIZjQBRYYtvVMIsGgWLKLYC4xcUiYEey671n1alvTZoCBaU9B85O8mF/tx9LPyrI04A6Bs2THv4A== dependencies: "@tanstack/query-core" "5.51.21" @@ -2845,11 +2845,11 @@ integrity sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag== "@types/node@*": - version "22.1.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.1.0.tgz#6d6adc648b5e03f0e83c78dc788c2b037d0ad94b" - integrity sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw== + version "22.4.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.4.0.tgz#c295fe1d6f5f58916cc61dbef8cf65b5b9b71de9" + integrity sha512-49AbMDwYUz7EXxKU/r7mXOsxwFr4BYbvB7tWYxVuLdb2ibd30ijjXINSMAHiEEZk5PCRBmW1gUeisn2VMKt3cQ== dependencies: - undici-types "~6.13.0" + undici-types "~6.19.2" "@types/node@14": version "14.18.63" @@ -2862,11 +2862,11 @@ integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== "@types/node@^20.9.0": - version "20.14.14" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.14.tgz#6b655d4a88623b0edb98300bb9dd2107225f885e" - integrity sha512-d64f00982fS9YoOgJkAMolK7MN8Iq3TDdVjchbYHdEmjth/DHowx82GnoA+tVUAN+7vxfYUgAzi+JXbKNd2SDQ== + version "20.15.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.15.0.tgz#7305f7fe7c62cd31047ed8d65c5092f0b0e1c55d" + integrity sha512-eQf4OkH6gA9v1W0iEpht/neozCsZKMTK+C4cU6/fv7wtJCCL8LEQ4hie2Ln8ZP/0YYM2xGj7//f8xyqItkJ6QA== dependencies: - undici-types "~5.26.4" + undici-types "~6.13.0" "@types/normalize-package-data@^2.4.0": version "2.4.3" @@ -3581,9 +3581,9 @@ aws-sign2@~0.7.0: integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== aws4@^1.8.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.0.tgz#d9b802e9bb9c248d7be5f7f5ef178dc3684e9dcc" - integrity sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g== + version "1.13.1" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.1.tgz#bb5f8b8a20739f6ae1caeaf7eea2c7913df8048e" + integrity sha512-u5w79Rd7SU4JaIlA/zFqG+gOiuq25q5VLyZ8E+ijJeILuTxVzZgp2CaGw/UTw6pXYN9XMO9yiqj/nEHmhTG5CA== axe-core@^4.9.1: version "4.10.0" @@ -4446,9 +4446,9 @@ cypress@*: yauzl "^2.10.0" cypress@^13.6.6: - version "13.13.2" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.13.2.tgz#c71f8d92056c430b1b879e5313f6de25ccce0eda" - integrity sha512-PvJQU33933NvS1StfzEb8/mu2kMy4dABwCF+yd5Bi7Qly1HOVf+Bufrygee/tlmty/6j5lX+KIi8j9Q3JUMbhA== + version "13.13.3" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.13.3.tgz#21ee054bb4e00b3858f2e33b4f8f4e69128470a9" + integrity sha512-hUxPrdbJXhUOTzuML+y9Av7CKoYznbD83pt8g3klgpioEha0emfx4WNIuVRx0C76r0xV2MIwAW9WYiXfVJYFQw== dependencies: "@cypress/request" "^3.0.1" "@cypress/xvfb" "^1.2.4" @@ -6616,10 +6616,10 @@ isarray@^2.0.5: resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== -isbot@^5.1.1: - version "5.1.13" - resolved "https://registry.yarnpkg.com/isbot/-/isbot-5.1.13.tgz#c291ae9f33d346c070648cb44d6ab2bb163650d1" - integrity sha512-RXtBib4m9zChSb+187EpNQML7Z3u2i34zDdqcRFZnqSJs0xdh91xzJytc5apYVg+9Y4NGnUQ0AIeJvX9FAnCUw== +isbot@^5.1.16: + version "5.1.16" + resolved "https://registry.yarnpkg.com/isbot/-/isbot-5.1.16.tgz#652fac0ab5ba181cefc53aa156edcb76f28ac07d" + integrity sha512-zvRjcw/4UfKiCVx+/PqXPKthufO5m2PpJSbA0tVZY9ns7hlQRV1xqWpEFcqyfkK/MrChsXPmu1zpxECjcaEuKg== isexe@^2.0.0: version "2.0.0" @@ -8888,9 +8888,9 @@ postcss@^8.4.28: source-map-js "^1.0.2" postcss@^8.4.31: - version "8.4.40" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.40.tgz#eb81f2a4dd7668ed869a6db25999e02e9ad909d8" - integrity sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q== + version "8.4.41" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.41.tgz#d6104d3ba272d882fe18fc07d15dc2da62fa2681" + integrity sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ== dependencies: nanoid "^3.3.7" picocolors "^1.0.1" @@ -8927,12 +8927,12 @@ pretty-format@^29.0.0, pretty-format@^29.7.0: ansi-styles "^5.0.0" react-is "^18.0.0" -prisma@5.17.0: - version "5.17.0" - resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.17.0.tgz#267b43921ab94805b010537cffa5ccaf530fa066" - integrity sha512-m4UWkN5lBE6yevqeOxEvmepnL5cNPEjzMw2IqDB59AcEV6w7D8vGljDLd1gPFH+W6gUxw9x7/RmN5dCS/WTPxA== +prisma@5.18.0: + version "5.18.0" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.18.0.tgz#5ef69c802a075b7596231ea57003496873610b9e" + integrity sha512-+TrSIxZsh64OPOmaSgVPH7ALL9dfU0jceYaMJXsNrTkFHO7/3RANi5K2ZiPB1De9+KDxCWn7jvRq8y8pvk+o9g== dependencies: - "@prisma/engines" "5.17.0" + "@prisma/engines" "5.18.0" process@^0.11.10: version "0.11.10" @@ -10518,16 +10518,16 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" -undici-types@~5.26.4: - version "5.26.5" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" - integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== - undici-types@~6.13.0: version "6.13.0" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.13.0.tgz#e3e79220ab8c81ed1496b5812471afd7cf075ea5" integrity sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg== +undici-types@~6.19.2: + version "6.19.6" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.6.tgz#e218c3df0987f4c0e0008ca00d6b6472d9b89b36" + integrity sha512-e/vggGopEfTKSvj4ihnOLTsqhrKRN3LeO6qSN/GxohhuRv8qH9bNQ4B8W7e/vFL+0XTnmHPB4/kegunZGA4Org== + unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" @@ -10599,10 +10599,10 @@ use-memo-one@^1.1.1: resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.3.tgz#2fd2e43a2169eabc7496960ace8c79efef975e99" integrity sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ== -use-sync-external-store@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" - integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== +use-sync-external-store@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9" + integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw== util-deprecate@^1.0.2: version "1.0.2" @@ -10908,9 +10908,9 @@ yup@^0.32.11: property-expr "^2.0.4" toposort "^2.0.2" -zustand@^4.3.8: - version "4.5.4" - resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.4.tgz#63abdd81edfb190bc61e0bbae045cc4d52158a05" - integrity sha512-/BPMyLKJPtFEvVL0E9E9BTUM63MNyhPGlvxk1XjrfWTUlV+BR8jufjsovHzrtR6YNcBEcL7cMHovL1n9xHawEg== +zustand@^4.5.5: + version "4.5.5" + resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.5.tgz#f8c713041543715ec81a2adda0610e1dc82d4ad1" + integrity sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q== dependencies: - use-sync-external-store "1.2.0" + use-sync-external-store "1.2.2" From ffa954ee02e74b22acbaea7cafa9b72ed1965e28 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Sat, 17 Aug 2024 20:27:40 -0700 Subject: [PATCH 104/132] Updated data query limits. --- src/lib/clickhouse.ts | 2 +- src/lib/prisma.ts | 8 ++++++++ src/queries/analytics/events/getEventDataValues.ts | 4 ++-- src/queries/analytics/sessions/getSessionDataValues.ts | 4 ++-- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/lib/clickhouse.ts b/src/lib/clickhouse.ts index 474417b93c..70f8d31d7b 100644 --- a/src/lib/clickhouse.ts +++ b/src/lib/clickhouse.ts @@ -189,7 +189,7 @@ async function rawQuery( format: 'JSONEachRow', }); - return resultSet.json() as T; + return (await resultSet.json()) as T; } async function insert(table: string, values: any[]) { diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts index 1fbb3cd4f7..85b1bea7e0 100644 --- a/src/lib/prisma.ts +++ b/src/lib/prisma.ts @@ -1,3 +1,4 @@ +import debug from 'debug'; import { Prisma } from '@prisma/client'; import prisma from '@umami/prisma-client'; import moment from 'moment-timezone'; @@ -8,6 +9,8 @@ import { maxDate } from './date'; import { QueryFilters, QueryOptions, PageParams } from './types'; import { filtersToArray } from './params'; +const log = debug('umami:prisma'); + const MYSQL_DATE_FORMATS = { minute: '%Y-%m-%d %H:%i:00', hour: '%Y-%m-%d %H:00:00', @@ -210,6 +213,11 @@ async function parseFilters( } async function rawQuery(sql: string, data: object): Promise { + if (process.env.LOG_QUERY) { + log('QUERY:\n', sql); + log('PARAMETERS:\n', data); + } + const db = getDatabaseType(); const params = []; diff --git a/src/queries/analytics/events/getEventDataValues.ts b/src/queries/analytics/events/getEventDataValues.ts index e055bfccc9..7ccd7a2863 100644 --- a/src/queries/analytics/events/getEventDataValues.ts +++ b/src/queries/analytics/events/getEventDataValues.ts @@ -35,7 +35,7 @@ async function relationalQuery( ${filterQuery} group by string_value order by 2 desc - limit 500 + limit 100 `, params, ); @@ -63,7 +63,7 @@ async function clickhouseQuery( ${filterQuery} group by event_name, value order by 2 desc - limit 500; + limit 100 `, params, ).then(result => { diff --git a/src/queries/analytics/sessions/getSessionDataValues.ts b/src/queries/analytics/sessions/getSessionDataValues.ts index fd67e7a99a..74524204ad 100644 --- a/src/queries/analytics/sessions/getSessionDataValues.ts +++ b/src/queries/analytics/sessions/getSessionDataValues.ts @@ -31,7 +31,7 @@ async function relationalQuery( ${filterQuery} group by string_value order by 2 desc - limit 500 + limit 100 `, params, ); @@ -58,7 +58,7 @@ async function clickhouseQuery( ${filterQuery} group by value order by 2 desc - limit 500; + limit 100 `, params, ).then(result => { From 7add059f430895370241d6f83778be3589fc2fc1 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Sat, 17 Aug 2024 21:32:42 -0700 Subject: [PATCH 105/132] Updated weekly session labels. --- .../[websiteId]/sessions/SessionsWeekly.module.css | 4 ++-- .../websites/[websiteId]/sessions/SessionsWeekly.tsx | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css index 4b41d87ceb..b795ed704c 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css @@ -20,16 +20,16 @@ .cell { display: flex; align-items: center; - justify-content: center; background-color: var(--base100); width: 20px; height: 20px; + margin: auto; } .hour { font-weight: 700; color: var(--font-color300); - background-color: transparent; + height: 20px; } .block { diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx index 6c0984ea53..0ee4805dd1 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx @@ -1,4 +1,4 @@ -import { format } from 'date-fns'; +import { format, startOfDay, addHours } from 'date-fns'; import { useLocale, useMessages, useWebsiteSessionsWeekly } from 'components/hooks'; import { LoadingPanel } from 'components/common/LoadingPanel'; import { getDayOfWeekAsDate } from 'lib/date'; @@ -40,9 +40,10 @@ export function SessionsWeekly({ websiteId }: { websiteId: string }) { {Array(24) .fill(null) .map((_, i) => { + const label = format(addHours(startOfDay(new Date()), i), 'haaa'); return ( -
- {i.toString().padStart(2, '0')} +
+ {label}
); })} From e853ed7ae95a464cff79b0be5aca147e871216a0 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Sat, 17 Aug 2024 21:51:29 -0700 Subject: [PATCH 106/132] Updated weekly session styles. --- .../websites/[websiteId]/sessions/SessionsWeekly.module.css | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css index b795ed704c..59f0cca682 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css @@ -13,7 +13,7 @@ .day { display: flex; flex-direction: column; - gap: 5px; + gap: 1px; position: relative; } @@ -23,7 +23,6 @@ background-color: var(--base100); width: 20px; height: 20px; - margin: auto; } .hour { From ceacc25021737491c155de366deb31372583c102 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Sun, 18 Aug 2024 00:15:20 -0700 Subject: [PATCH 107/132] Fixed session weekly rendering bug. --- .../websites/[websiteId]/sessions/SessionsWeekly.module.css | 5 ++++- .../(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css index 59f0cca682..6f5bf6cb48 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css @@ -13,16 +13,19 @@ .day { display: flex; flex-direction: column; + align-items: start; + justify-content: start; gap: 1px; position: relative; } .cell { display: flex; - align-items: center; background-color: var(--base100); width: 20px; height: 20px; + margin: auto; + align-items: start; } .hour { diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx index 0ee4805dd1..f9acaefc60 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx @@ -34,7 +34,7 @@ export function SessionsWeekly({ websiteId }: { websiteId: string }) { return ( -
+
 
{Array(24) @@ -50,7 +50,7 @@ export function SessionsWeekly({ websiteId }: { websiteId: string }) {
{data?.map((day: number[], index: number) => { return ( -
+
{format(getDayOfWeekAsDate(index), 'EEE', { locale: dateLocale })}
From e98bc3bd742acd71d5b2abe13d42c8a79634e7a3 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Sun, 18 Aug 2024 10:38:28 -0700 Subject: [PATCH 108/132] Changed session weekly to dots. --- .../websites/[websiteId]/sessions/SessionsWeekly.module.css | 5 +++-- .../(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css index 6f5bf6cb48..ba15dfcc56 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css @@ -21,10 +21,11 @@ .cell { display: flex; - background-color: var(--base100); + background-color: var(--base75); width: 20px; height: 20px; margin: auto; + border-radius: 100%; align-items: start; } @@ -38,5 +39,5 @@ background-color: var(--primary400); width: 20px; height: 20px; - border-radius: 3px; + border-radius: 100%; } diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx index f9acaefc60..3e15ddfaeb 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx @@ -55,6 +55,7 @@ export function SessionsWeekly({ websiteId }: { websiteId: string }) { {format(getDayOfWeekAsDate(index), 'EEE', { locale: dateLocale })}
{day?.map((hour: number) => { + const pct = hour / max; return (
{hour > 0 && ( @@ -62,7 +63,10 @@ export function SessionsWeekly({ websiteId }: { websiteId: string }) { label={`${formatMessage(labels.visitors)}: ${hour}`} position="right" > -
+
)}
From a058552d3963203908e78a609366d4ada98721c0 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Mon, 19 Aug 2024 09:35:22 -0700 Subject: [PATCH 109/132] add sessionstats query for performance, countries, events values --- .../[websiteId]/events/EventsMetricsBar.tsx | 15 ++-- .../sessions/SessionsMetricsBar.tsx | 10 +-- .../hooks/queries/useWebsiteSessionStats.ts | 16 ++++ .../websites/[websiteId]/sessions/stats.ts | 84 +++++++++++++++++++ src/queries/analytics/getWebsiteStats.ts | 45 +++++----- .../sessions/getWebsiteSessionStats.ts | 82 ++++++++++++++++++ 6 files changed, 218 insertions(+), 34 deletions(-) create mode 100644 src/components/hooks/queries/useWebsiteSessionStats.ts create mode 100644 src/pages/api/websites/[websiteId]/sessions/stats.ts create mode 100644 src/queries/analytics/sessions/getWebsiteSessionStats.ts diff --git a/src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx b/src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx index 0a054e8d17..d039b67f71 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx @@ -1,14 +1,14 @@ +import { useMessages } from 'components/hooks'; +import useWebsiteSessionStats from 'components/hooks/queries/useWebsiteSessionStats'; import WebsiteDateFilter from 'components/input/WebsiteDateFilter'; -import { Flexbox } from 'react-basics'; -import MetricsBar from 'components/metrics/MetricsBar'; import MetricCard from 'components/metrics/MetricCard'; -import { useMessages } from 'components/hooks'; -import useWebsiteStats from 'components/hooks/queries/useWebsiteStats'; +import MetricsBar from 'components/metrics/MetricsBar'; import { formatLongNumber } from 'lib/format'; +import { Flexbox } from 'react-basics'; export function EventsMetricsBar({ websiteId }: { websiteId: string }) { const { formatMessage, labels } = useMessages(); - const { data, isLoading, isFetched, error } = useWebsiteStats(websiteId); + const { data, isLoading, isFetched, error } = useWebsiteSessionStats(websiteId); return ( @@ -28,6 +28,11 @@ export function EventsMetricsBar({ websiteId }: { websiteId: string }) { label={formatMessage(labels.views)} formatValue={formatLongNumber} /> + diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsMetricsBar.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsMetricsBar.tsx index 9133ca71c6..803e7a0690 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsMetricsBar.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsMetricsBar.tsx @@ -1,14 +1,14 @@ +import { useMessages } from 'components/hooks'; +import useWebsiteSessionStats from 'components/hooks/queries/useWebsiteSessionStats'; import WebsiteDateFilter from 'components/input/WebsiteDateFilter'; -import { Flexbox } from 'react-basics'; -import MetricsBar from 'components/metrics/MetricsBar'; import MetricCard from 'components/metrics/MetricCard'; -import { useMessages } from 'components/hooks'; -import useWebsiteStats from 'components/hooks/queries/useWebsiteStats'; +import MetricsBar from 'components/metrics/MetricsBar'; import { formatLongNumber } from 'lib/format'; +import { Flexbox } from 'react-basics'; export function SessionsMetricsBar({ websiteId }: { websiteId: string }) { const { formatMessage, labels } = useMessages(); - const { data, isLoading, isFetched, error } = useWebsiteStats(websiteId); + const { data, isLoading, isFetched, error } = useWebsiteSessionStats(websiteId); return ( diff --git a/src/components/hooks/queries/useWebsiteSessionStats.ts b/src/components/hooks/queries/useWebsiteSessionStats.ts new file mode 100644 index 0000000000..7671b2ebee --- /dev/null +++ b/src/components/hooks/queries/useWebsiteSessionStats.ts @@ -0,0 +1,16 @@ +import { useApi } from './useApi'; +import { useFilterParams } from '../useFilterParams'; + +export function useWebsiteSessionStats(websiteId: string, options?: { [key: string]: string }) { + const { get, useQuery } = useApi(); + const params = useFilterParams(websiteId); + + return useQuery({ + queryKey: ['sessions:stats', { websiteId, ...params }], + queryFn: () => get(`/websites/${websiteId}/sessions/stats`, { ...params }), + enabled: !!websiteId, + ...options, + }); +} + +export default useWebsiteSessionStats; diff --git a/src/pages/api/websites/[websiteId]/sessions/stats.ts b/src/pages/api/websites/[websiteId]/sessions/stats.ts new file mode 100644 index 0000000000..a522bd6bf5 --- /dev/null +++ b/src/pages/api/websites/[websiteId]/sessions/stats.ts @@ -0,0 +1,84 @@ +import { canViewWebsite } from 'lib/auth'; +import { useAuth, useCors, useValidate } from 'lib/middleware'; +import { getRequestDateRange, getRequestFilters } from 'lib/request'; +import { NextApiRequestQueryBody, WebsiteStats } from 'lib/types'; +import { NextApiResponse } from 'next'; +import { methodNotAllowed, ok, unauthorized } from 'next-basics'; +import { getWebsiteSessionStats } from 'queries/analytics/sessions/getWebsiteSessionStats'; +import * as yup from 'yup'; + +export interface WebsiteSessionStatsRequestQuery { + websiteId: string; + startAt: number; + endAt: number; + url?: string; + referrer?: string; + title?: string; + query?: string; + event?: string; + host?: string; + os?: string; + browser?: string; + device?: string; + country?: string; + region?: string; + city?: string; +} + +const schema = { + GET: yup.object().shape({ + websiteId: yup.string().uuid().required(), + startAt: yup.number().required(), + endAt: yup.number().required(), + url: yup.string(), + referrer: yup.string(), + title: yup.string(), + query: yup.string(), + event: yup.string(), + host: yup.string(), + os: yup.string(), + browser: yup.string(), + device: yup.string(), + country: yup.string(), + region: yup.string(), + city: yup.string(), + }), +}; + +export default async ( + req: NextApiRequestQueryBody, + res: NextApiResponse, +) => { + await useCors(req, res); + await useAuth(req, res); + await useValidate(schema, req, res); + + const { websiteId } = req.query; + + if (req.method === 'GET') { + if (!(await canViewWebsite(req.auth, websiteId))) { + return unauthorized(res); + } + + const { startDate, endDate } = await getRequestDateRange(req); + + const filters = getRequestFilters(req); + + const metrics = await getWebsiteSessionStats(websiteId, { + ...filters, + startDate, + endDate, + }); + + const stats = Object.keys(metrics[0]).reduce((obj, key) => { + obj[key] = { + value: Number(metrics[0][key]) || 0, + }; + return obj; + }, {}); + + return ok(res, stats); + } + + return methodNotAllowed(res); +}; diff --git a/src/queries/analytics/getWebsiteStats.ts b/src/queries/analytics/getWebsiteStats.ts index c35aea0693..c5141d3be7 100644 --- a/src/queries/analytics/getWebsiteStats.ts +++ b/src/queries/analytics/getWebsiteStats.ts @@ -1,4 +1,5 @@ import clickhouse from 'lib/clickhouse'; +import { EVENT_TYPE } from 'lib/constants'; import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; import prisma from 'lib/prisma'; import { QueryFilters } from 'lib/types'; @@ -24,6 +25,7 @@ async function relationalQuery( const { getTimestampDiffSQL, parseFilters, rawQuery } = prisma; const { filterQuery, joinSession, params } = await parseFilters(websiteId, { ...filters, + eventType: EVENT_TYPE.pageView, }); return rawQuery( @@ -32,14 +34,12 @@ async function relationalQuery( sum(t.c) as "pageviews", count(distinct t.session_id) as "visitors", count(distinct t.visit_id) as "visits", - count(distinct t.country) as "countries", sum(case when t.c = 1 then 1 else 0 end) as "bounces", - sum(${getTimestampDiffSQL('t.min_time', 't.max_time')}) as "totaltime", + sum(${getTimestampDiffSQL('t.min_time', 't.max_time')}) as "totaltime" from ( select website_event.session_id, website_event.visit_id, - session.country, count(*) as "c", min(website_event.created_at) as "min_time", max(website_event.created_at) as "max_time" @@ -47,8 +47,9 @@ async function relationalQuery( ${joinSession} where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} + and event_type = {{eventType}} ${filterQuery} - group by 1, 2, 3 + group by 1, 2 ) as t `, params, @@ -64,6 +65,7 @@ async function clickhouseQuery( const { rawQuery, parseFilters } = clickhouse; const { filterQuery, params } = await parseFilters(websiteId, { ...filters, + eventType: EVENT_TYPE.pageView, }); let sql = ''; @@ -74,22 +76,21 @@ async function clickhouseQuery( sum(t.c) as "pageviews", uniq(t.session_id) as "visitors", uniq(t.visit_id) as "visits", - uniq(t.country) as "countries", - sumIf(1, t.c = 1) as "bounces", + sum(if(t.c = 1, 1, 0)) as "bounces", sum(max_time-min_time) as "totaltime" from ( select session_id, visit_id, - country, count(*) c, min(created_at) min_time, max(created_at) max_time from website_event where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} + and event_type = {eventType:UInt32} ${filterQuery} - group by session_id, visit_id, country + group by session_id, visit_id ) as t; `; } else { @@ -98,22 +99,20 @@ async function clickhouseQuery( sum(t.c) as "pageviews", uniq(session_id) as "visitors", uniq(visit_id) as "visits", - uniq(country) as "countries", sumIf(1, t.c = 1) as "bounces", sum(max_time-min_time) as "totaltime" - from ( - select - session_id, - visit_id, - country, - sum(views) c, - min(min_time) min_time, - max(max_time) max_time - from umami.website_event_stats_hourly "website_event" - where website_id = {websiteId:UUID} - and created_at between {startDate:DateTime64} and {endDate:DateTime64} - ${filterQuery} - group by session_id, visit_id, country + from (select + session_id, + visit_id, + sum(views) c, + min(min_time) min_time, + max(max_time) max_time + from umami.website_event_stats_hourly "website_event" + where website_id = {websiteId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} + and event_type = {eventType:UInt32} + ${filterQuery} + group by session_id, visit_id ) as t; `; } @@ -126,8 +125,6 @@ async function clickhouseQuery( visits: Number(a.visits), bounces: Number(a.bounces), totaltime: Number(a.totaltime), - countries: Number(a.countries), - events: Number(a.events), }; }); }); diff --git a/src/queries/analytics/sessions/getWebsiteSessionStats.ts b/src/queries/analytics/sessions/getWebsiteSessionStats.ts new file mode 100644 index 0000000000..a3130c7c3a --- /dev/null +++ b/src/queries/analytics/sessions/getWebsiteSessionStats.ts @@ -0,0 +1,82 @@ +import clickhouse from 'lib/clickhouse'; +import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; +import prisma from 'lib/prisma'; +import { QueryFilters } from 'lib/types'; + +export async function getWebsiteSessionStats( + ...args: [websiteId: string, filters: QueryFilters] +): Promise< + { pageviews: number; visitors: number; visits: number; countries: number; events: number }[] +> { + return runQuery({ + [PRISMA]: () => relationalQuery(...args), + [CLICKHOUSE]: () => clickhouseQuery(...args), + }); +} + +async function relationalQuery( + websiteId: string, + filters: QueryFilters, +): Promise< + { pageviews: number; visitors: number; visits: number; countries: number; events: number }[] +> { + const { parseFilters, rawQuery } = prisma; + const { filterQuery, params } = await parseFilters(websiteId, { + ...filters, + }); + + return rawQuery( + ` + select + count(*) as "pageviews", + count(distinct t.session_id) as "visitors", + count(distinct t.visit_id) as "visits", + count(distinct t.country) as "countries", + sum(case when event_type = 2 then 1 else 0 end) as "events" + from website_event + join session on website_event.session_id = session.session_id + where website_event.website_id = {{websiteId::uuid}} + and website_event.created_at between {{startDate}} and {{endDate}} + ${filterQuery} + `, + params, + ); +} + +async function clickhouseQuery( + websiteId: string, + filters: QueryFilters, +): Promise< + { pageviews: number; visitors: number; visits: number; countries: number; events: number }[] +> { + const { rawQuery, parseFilters } = clickhouse; + const { filterQuery, params } = await parseFilters(websiteId, { + ...filters, + }); + + return rawQuery( + ` + select + sum(views) as "pageviews", + uniq(session_id) as "visitors", + uniq(visit_id) as "visits", + uniq(country) as "countries", + sum(length(event_name)) as "events" + from umami.website_event_stats_hourly "website_event" + where website_id = {websiteId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} + ${filterQuery} + `, + params, + ).then(result => { + return Object.values(result).map((a: any) => { + return { + pageviews: Number(a.pageviews), + visitors: Number(a.visitors), + visits: Number(a.visits), + countries: Number(a.countries), + events: Number(a.events), + }; + }); + }); +} From 7ae3c790cd290b377238a66e68a3f58afc4df154 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Mon, 19 Aug 2024 10:28:10 -0700 Subject: [PATCH 110/132] fix relational queries for new screens --- .../analytics/events/getEventDataProperties.ts | 13 +++++++------ .../analytics/sessions/getWebsiteSessionStats.ts | 8 ++++---- .../analytics/sessions/getWebsiteSessionsWeekly.ts | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/queries/analytics/events/getEventDataProperties.ts b/src/queries/analytics/events/getEventDataProperties.ts index 8d1eaf9de0..9d55f8967c 100644 --- a/src/queries/analytics/events/getEventDataProperties.ts +++ b/src/queries/analytics/events/getEventDataProperties.ts @@ -24,14 +24,15 @@ async function relationalQuery( return rawQuery( ` select - event_name as "eventName", - data_key as "propertyName", + we.event_name as "eventName", + ed.data_key as "propertyName", count(*) as "total" - from event_data - where website_id = {{websiteId::uuid}} - and created_at between {{startDate}} and {{endDate}} + from event_data ed + join website_event we on we.event_id = ed.website_event_id + where ed.website_id = {{websiteId::uuid}} + and ed.created_at between {{startDate}} and {{endDate}} ${filterQuery} - group by event_name, data_key + group by we.event_name, ed.data_key order by 3 desc limit 500 `, diff --git a/src/queries/analytics/sessions/getWebsiteSessionStats.ts b/src/queries/analytics/sessions/getWebsiteSessionStats.ts index a3130c7c3a..5660a5fa25 100644 --- a/src/queries/analytics/sessions/getWebsiteSessionStats.ts +++ b/src/queries/analytics/sessions/getWebsiteSessionStats.ts @@ -29,10 +29,10 @@ async function relationalQuery( ` select count(*) as "pageviews", - count(distinct t.session_id) as "visitors", - count(distinct t.visit_id) as "visits", - count(distinct t.country) as "countries", - sum(case when event_type = 2 then 1 else 0 end) as "events" + count(distinct website_event.session_id) as "visitors", + count(distinct website_event.visit_id) as "visits", + count(distinct session.country) as "countries", + sum(case when website_event.event_type = 2 then 1 else 0 end) as "events" from website_event join session on website_event.session_id = session.session_id where website_event.website_id = {{websiteId::uuid}} diff --git a/src/queries/analytics/sessions/getWebsiteSessionsWeekly.ts b/src/queries/analytics/sessions/getWebsiteSessionsWeekly.ts index 9031edf510..c92c2929be 100644 --- a/src/queries/analytics/sessions/getWebsiteSessionsWeekly.ts +++ b/src/queries/analytics/sessions/getWebsiteSessionsWeekly.ts @@ -21,7 +21,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { select ${getDateWeeklySQL('created_at')} as time, count(distinct session_id) as value - from website_event_stats_hourly + from website_event where website_id = {{websiteId::uuid}} and created_at between {{startDate}} and {{endDate}} group by time From 8d92509f71e065642d9f723f928e54afd2b9d554 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Mon, 19 Aug 2024 10:53:31 -0700 Subject: [PATCH 111/132] Fixed queries. --- .../sessions/[sessionId]/SessionActivity.module.css | 6 +++++- .../sessions/[sessionId]/SessionActivity.tsx | 2 +- src/queries/analytics/events/getWebsiteEvents.ts | 3 +-- src/queries/analytics/getWebsiteStats.ts | 12 ++++++++---- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.module.css b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.module.css index bcc6868d49..b49230c7b8 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.module.css +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.module.css @@ -12,7 +12,11 @@ .time { color: var(--font-color200); - width: 120px; + width: 150px; +} + +.value { + white-space: nowrap; } .header { diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx index 663934936e..187205e986 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx @@ -42,7 +42,7 @@ export function SessionActivity({
{eventName ? : } -
{eventName || urlPath}
+
{eventName || urlPath}
); diff --git a/src/queries/analytics/events/getWebsiteEvents.ts b/src/queries/analytics/events/getWebsiteEvents.ts index 9304d69e88..e558c6092b 100644 --- a/src/queries/analytics/events/getWebsiteEvents.ts +++ b/src/queries/analytics/events/getWebsiteEvents.ts @@ -17,12 +17,11 @@ async function relationalQuery(websiteId: string, filters: QueryFilters, pagePar const { query } = pageParams; const where = { - ...filters, id: websiteId, ...prisma.getSearchParameters(query, [{ eventName: 'contains' }, { urlPath: 'contains' }]), }; - return pagedQuery('website_event', { where }, pageParams); + return pagedQuery('WebsiteEvent', { where }, pageParams); } async function clickhouseQuery(websiteId: string, filters: QueryFilters, pageParams?: PageParams) { diff --git a/src/queries/analytics/getWebsiteStats.ts b/src/queries/analytics/getWebsiteStats.ts index c35aea0693..17bf0c8d95 100644 --- a/src/queries/analytics/getWebsiteStats.ts +++ b/src/queries/analytics/getWebsiteStats.ts @@ -22,9 +22,13 @@ async function relationalQuery( { pageviews: number; visitors: number; visits: number; bounces: number; totaltime: number }[] > { const { getTimestampDiffSQL, parseFilters, rawQuery } = prisma; - const { filterQuery, joinSession, params } = await parseFilters(websiteId, { - ...filters, - }); + const { filterQuery, joinSession, params } = await parseFilters( + websiteId, + { + ...filters, + }, + { joinSession: true }, + ); return rawQuery( ` @@ -34,7 +38,7 @@ async function relationalQuery( count(distinct t.visit_id) as "visits", count(distinct t.country) as "countries", sum(case when t.c = 1 then 1 else 0 end) as "bounces", - sum(${getTimestampDiffSQL('t.min_time', 't.max_time')}) as "totaltime", + sum(${getTimestampDiffSQL('t.min_time', 't.max_time')}) as "totaltime" from ( select website_event.session_id, From dc9b1dc44674c31f6ecf7404d9f378bc1d18afeb Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Mon, 19 Aug 2024 12:58:00 -0700 Subject: [PATCH 112/132] Rolled back Prisma to 5.17. --- package.json | 4 +-- yarn.lock | 82 ++++++++++++++++++++++++++-------------------------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/package.json b/package.json index 351596fb1a..bd1ed4e692 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "@dicebear/collection": "^9.2.1", "@dicebear/core": "^9.2.1", "@fontsource/inter": "^4.5.15", - "@prisma/client": "5.18.0", + "@prisma/client": "5.17", "@prisma/extension-read-replicas": "^0.3.0", "@react-spring/web": "^9.7.3", "@tanstack/react-query": "^5.28.6", @@ -104,7 +104,7 @@ "next-basics": "^0.39.0", "node-fetch": "^3.2.8", "npm-run-all": "^4.1.5", - "prisma": "5.18.0", + "prisma": "5.17", "react": "^18.2.0", "react-basics": "^0.125.0", "react-beautiful-dnd": "^13.1.0", diff --git a/yarn.lock b/yarn.lock index 0abea883e2..910f0c69f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2280,51 +2280,51 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@prisma/client@5.18.0": - version "5.18.0" - resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.18.0.tgz#526e4281a448f214c0ff81d65c39243608c98294" - integrity sha512-BWivkLh+af1kqC89zCJYkHsRcyWsM8/JHpsDMM76DjP3ZdEquJhXa4IeX+HkWPnwJ5FanxEJFZZDTWiDs/Kvyw== - -"@prisma/debug@5.18.0": - version "5.18.0" - resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-5.18.0.tgz#527799e044d2903a35945e61ac2d8916e4b61ead" - integrity sha512-f+ZvpTLidSo3LMJxQPVgAxdAjzv5OpzAo/eF8qZqbwvgi2F5cTOI9XCpdRzJYA0iGfajjwjOKKrVq64vkxEfUw== - -"@prisma/engines-version@5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169": - version "5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169" - resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169.tgz#203426ebf4ec4e1acce7da4a59ec8f0df92b29e7" - integrity sha512-a/+LpJj8vYU3nmtkg+N3X51ddbt35yYrRe8wqHTJtYQt7l1f8kjIBcCs6sHJvodW/EK5XGvboOiwm47fmNrbgg== - -"@prisma/engines@5.18.0": - version "5.18.0" - resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.18.0.tgz#26ea46e26498be622407cf95663d7fb4c39c895b" - integrity sha512-ofmpGLeJ2q2P0wa/XaEgTnX/IsLnvSp/gZts0zjgLNdBhfuj2lowOOPmDcfKljLQUXMvAek3lw5T01kHmCG8rg== - dependencies: - "@prisma/debug" "5.18.0" - "@prisma/engines-version" "5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169" - "@prisma/fetch-engine" "5.18.0" - "@prisma/get-platform" "5.18.0" +"@prisma/client@5.17": + version "5.17.0" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.17.0.tgz#9079947bd749689c2dabfb9ecc70a24ebefb1f43" + integrity sha512-N2tnyKayT0Zf7mHjwEyE8iG7FwTmXDHFZ1GnNhQp0pJUObsuel4ZZ1XwfuAYkq5mRIiC/Kot0kt0tGCfLJ70Jw== + +"@prisma/debug@5.17.0": + version "5.17.0" + resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-5.17.0.tgz#a765105848993984535b6066f8ebc6e6ead26533" + integrity sha512-l7+AteR3P8FXiYyo496zkuoiJ5r9jLQEdUuxIxNCN1ud8rdbH3GTxm+f+dCyaSv9l9WY+29L9czaVRXz9mULfg== + +"@prisma/engines-version@5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053": + version "5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053.tgz#3c7cc1ef3ebc34cbd069e5873b9982f2aabf5acd" + integrity sha512-tUuxZZysZDcrk5oaNOdrBnnkoTtmNQPkzINFDjz7eG6vcs9AVDmA/F6K5Plsb2aQc/l5M2EnFqn3htng9FA4hg== + +"@prisma/engines@5.17.0": + version "5.17.0" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.17.0.tgz#74dd1aabb22675892760b3cf69a448e3aef4616b" + integrity sha512-+r+Nf+JP210Jur+/X8SIPLtz+uW9YA4QO5IXA+KcSOBe/shT47bCcRMTYCbOESw3FFYFTwe7vU6KTWHKPiwvtg== + dependencies: + "@prisma/debug" "5.17.0" + "@prisma/engines-version" "5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053" + "@prisma/fetch-engine" "5.17.0" + "@prisma/get-platform" "5.17.0" "@prisma/extension-read-replicas@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@prisma/extension-read-replicas/-/extension-read-replicas-0.3.0.tgz#2842a7c928f957c1dd58a6256104797596d43426" integrity sha512-F9+rSmYday6GT2qjhJtkZcBOpLO5LtpvFcMGqrBDHf+78LEdSuxfFjOxYlNuqk4B+th62yxpbhfpmB9/Mca14Q== -"@prisma/fetch-engine@5.18.0": - version "5.18.0" - resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-5.18.0.tgz#5b343e2b36b27e2713901ddd032ddd6932b3d55f" - integrity sha512-I/3u0x2n31rGaAuBRx2YK4eB7R/1zCuayo2DGwSpGyrJWsZesrV7QVw7ND0/Suxeo/vLkJ5OwuBqHoCxvTHpOg== +"@prisma/fetch-engine@5.17.0": + version "5.17.0" + resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-5.17.0.tgz#f718dc7426411d1ebeeee53e2d0d38652387f87c" + integrity sha512-ESxiOaHuC488ilLPnrv/tM2KrPhQB5TRris/IeIV4ZvUuKeaicCl4Xj/JCQeG9IlxqOgf1cCg5h5vAzlewN91Q== dependencies: - "@prisma/debug" "5.18.0" - "@prisma/engines-version" "5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169" - "@prisma/get-platform" "5.18.0" + "@prisma/debug" "5.17.0" + "@prisma/engines-version" "5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053" + "@prisma/get-platform" "5.17.0" -"@prisma/get-platform@5.18.0": - version "5.18.0" - resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-5.18.0.tgz#0dc4c82fe9a4971f4519a57cb2dd69d8e0df4b71" - integrity sha512-Tk+m7+uhqcKDgnMnFN0lRiH7Ewea0OEsZZs9pqXa7i3+7svS3FSCqDBCaM9x5fmhhkufiG0BtunJVDka+46DlA== +"@prisma/get-platform@5.17.0": + version "5.17.0" + resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-5.17.0.tgz#89fdcae2adddebbbf0e7bd0474a6c49d6023519b" + integrity sha512-UlDgbRozCP1rfJ5Tlkf3Cnftb6srGrEQ4Nm3og+1Se2gWmCZ0hmPIi+tQikGDUVLlvOWx3Gyi9LzgRP+HTXV9w== dependencies: - "@prisma/debug" "5.18.0" + "@prisma/debug" "5.17.0" "@react-spring/animated@~9.7.4": version "9.7.4" @@ -8927,12 +8927,12 @@ pretty-format@^29.0.0, pretty-format@^29.7.0: ansi-styles "^5.0.0" react-is "^18.0.0" -prisma@5.18.0: - version "5.18.0" - resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.18.0.tgz#5ef69c802a075b7596231ea57003496873610b9e" - integrity sha512-+TrSIxZsh64OPOmaSgVPH7ALL9dfU0jceYaMJXsNrTkFHO7/3RANi5K2ZiPB1De9+KDxCWn7jvRq8y8pvk+o9g== +prisma@5.17: + version "5.17.0" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.17.0.tgz#267b43921ab94805b010537cffa5ccaf530fa066" + integrity sha512-m4UWkN5lBE6yevqeOxEvmepnL5cNPEjzMw2IqDB59AcEV6w7D8vGljDLd1gPFH+W6gUxw9x7/RmN5dCS/WTPxA== dependencies: - "@prisma/engines" "5.18.0" + "@prisma/engines" "5.17.0" process@^0.11.10: version "0.11.10" From 10239fefcdc789356cfa5bd187c56f678700ee7a Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Mon, 19 Aug 2024 15:24:02 -0700 Subject: [PATCH 113/132] Fixed docker build for Prisma. --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 801b2bc20a..0f7c2d3500 100644 --- a/Dockerfile +++ b/Dockerfile @@ -37,7 +37,7 @@ RUN adduser --system --uid 1001 nextjs RUN set -x \ && apk add --no-cache curl \ - && yarn add npm-run-all dotenv prisma semver + && yarn add npm-run-all dotenv semver prisma@5.17.0 # You only need to copy next.config.js if you are NOT using the default configuration COPY --from=builder /app/next.config.js . From 5aba9acb81be385a24715faa379548c06d7fa72d Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Tue, 20 Aug 2024 00:34:01 -0700 Subject: [PATCH 114/132] Fixed date value for properties. --- src/lib/clickhouse.ts | 2 +- src/queries/analytics/events/saveEventData.ts | 4 ++-- src/queries/analytics/sessions/saveSessionData.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/clickhouse.ts b/src/lib/clickhouse.ts index 70f8d31d7b..24e6a3b66d 100644 --- a/src/lib/clickhouse.ts +++ b/src/lib/clickhouse.ts @@ -49,7 +49,7 @@ function getClient() { return client; } -function getUTCString(date?: Date) { +function getUTCString(date?: Date | string | number) { return formatInTimeZone(date || new Date(), 'UTC', 'yyyy-MM-dd HH:mm:ss'); } diff --git a/src/queries/analytics/events/saveEventData.ts b/src/queries/analytics/events/saveEventData.ts index fec4cae393..7e7db8b465 100644 --- a/src/queries/analytics/events/saveEventData.ts +++ b/src/queries/analytics/events/saveEventData.ts @@ -60,7 +60,7 @@ async function clickhouseQuery(data: { }) { const { websiteId, sessionId, eventId, urlPath, eventName, eventData, createdAt } = data; - const { insert } = clickhouse; + const { insert, getUTCString } = clickhouse; const { sendMessages } = kafka; const jsonKeys = flattenJSON(eventData); @@ -76,7 +76,7 @@ async function clickhouseQuery(data: { data_type: dataType, string_value: getStringValue(value, dataType), number_value: dataType === DATA_TYPE.number ? value : null, - date_value: dataType === DATA_TYPE.date ? value?.toISOString() : null, + date_value: dataType === DATA_TYPE.date ? getUTCString(value) : null, created_at: createdAt, }; }); diff --git a/src/queries/analytics/sessions/saveSessionData.ts b/src/queries/analytics/sessions/saveSessionData.ts index 5259239a76..f9f0276ecc 100644 --- a/src/queries/analytics/sessions/saveSessionData.ts +++ b/src/queries/analytics/sessions/saveSessionData.ts @@ -94,7 +94,7 @@ async function clickhouseQuery(data: { data_type: dataType, string_value: getStringValue(value, dataType), number_value: dataType === DATA_TYPE.number ? value : null, - date_value: dataType === DATA_TYPE.date ? value?.toISOString() : null, + date_value: dataType === DATA_TYPE.date ? getUTCString(value) : null, created_at: createdAt, }; }); From da28fe8fe90d2f3b3ea87a05922eee4b831b94dc Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Tue, 20 Aug 2024 01:15:33 -0700 Subject: [PATCH 115/132] Fixed some styles. --- .../websites/[websiteId]/WebsiteExpandedView.module.css | 4 ++-- .../websites/[websiteId]/events/EventProperties.module.css | 2 +- .../[websiteId]/sessions/SessionProperties.module.css | 2 +- .../websites/[websiteId]/sessions/SessionsWeekly.module.css | 6 +++--- src/components/common/Pager.module.css | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.module.css b/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.module.css index f71032ae9f..007fa7f1b7 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.module.css +++ b/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.module.css @@ -38,7 +38,7 @@ } .back { - align-self: start; + align-self: flex-start; margin: 0; } @@ -49,7 +49,7 @@ .dropdown { display: flex; width: 200px; - align-self: end; + align-self: flex-end; } .menu { diff --git a/src/app/(main)/websites/[websiteId]/events/EventProperties.module.css b/src/app/(main)/websites/[websiteId]/events/EventProperties.module.css index 4f6a65ec9a..0b9c011d18 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventProperties.module.css +++ b/src/app/(main)/websites/[websiteId]/events/EventProperties.module.css @@ -6,7 +6,7 @@ } .table { - align-self: start; + align-self: flex-start; } .link:hover { diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.module.css b/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.module.css index 4f6a65ec9a..0b9c011d18 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.module.css +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.module.css @@ -6,7 +6,7 @@ } .table { - align-self: start; + align-self: flex-start; } .link:hover { diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css index ba15dfcc56..3536164353 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css @@ -13,8 +13,8 @@ .day { display: flex; flex-direction: column; - align-items: start; - justify-content: start; + align-items: flex-start; + justify-content: flex-start; gap: 1px; position: relative; } @@ -26,7 +26,7 @@ height: 20px; margin: auto; border-radius: 100%; - align-items: start; + align-items: flex-start; } .hour { diff --git a/src/components/common/Pager.module.css b/src/components/common/Pager.module.css index 880c1b401d..c9330c32d7 100644 --- a/src/components/common/Pager.module.css +++ b/src/components/common/Pager.module.css @@ -27,6 +27,6 @@ } .nav { - justify-content: end; + justify-content: flex-end; } } From eb05e2ada67e7a4c4362716e02225f7ff4468e01 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Tue, 20 Aug 2024 09:21:15 -0700 Subject: [PATCH 116/132] fix getDateWeeklySQL --- src/lib/prisma.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts index 85b1bea7e0..8c4f8c5c84 100644 --- a/src/lib/prisma.ts +++ b/src/lib/prisma.ts @@ -88,11 +88,11 @@ function getDateWeeklySQL(field: string) { const db = getDatabaseType(); if (db === POSTGRESQL) { - return `EXTRACT(DOW FROM ${field})`; + return `concat(extract(dow from ${field}), ':', to_char(${field}, 'HH24'))`; } if (db === MYSQL) { - return `DAYOFWEEK(${field})-1`; + return `date_format(${field}, '%w:%H')`; } } From 8ee4fc32fdf453aa9975cb3cce64b0aacd6af65b Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Tue, 20 Aug 2024 09:28:10 -0700 Subject: [PATCH 117/132] fix css console errors --- .../websites/[websiteId]/WebsiteExpandedView.module.css | 2 +- .../websites/[websiteId]/events/EventProperties.module.css | 2 +- .../[websiteId]/sessions/SessionProperties.module.css | 2 +- .../websites/[websiteId]/sessions/SessionsWeekly.module.css | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.module.css b/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.module.css index f71032ae9f..d17fad88e8 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.module.css +++ b/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.module.css @@ -38,7 +38,7 @@ } .back { - align-self: start; + align-self: flex-start; margin: 0; } diff --git a/src/app/(main)/websites/[websiteId]/events/EventProperties.module.css b/src/app/(main)/websites/[websiteId]/events/EventProperties.module.css index 4f6a65ec9a..0b9c011d18 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventProperties.module.css +++ b/src/app/(main)/websites/[websiteId]/events/EventProperties.module.css @@ -6,7 +6,7 @@ } .table { - align-self: start; + align-self: flex-start; } .link:hover { diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.module.css b/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.module.css index 4f6a65ec9a..0b9c011d18 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.module.css +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.module.css @@ -6,7 +6,7 @@ } .table { - align-self: start; + align-self: flex-start; } .link:hover { diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css index ba15dfcc56..3536164353 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css @@ -13,8 +13,8 @@ .day { display: flex; flex-direction: column; - align-items: start; - justify-content: start; + align-items: flex-start; + justify-content: flex-start; gap: 1px; position: relative; } @@ -26,7 +26,7 @@ height: 20px; margin: auto; border-radius: 100%; - align-items: start; + align-items: flex-start; } .hour { From adc65e4487447f2fcc869add42381b8e9c60498f Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Tue, 20 Aug 2024 13:57:40 -0700 Subject: [PATCH 118/132] fix websiteEvent query --- src/queries/analytics/events/getWebsiteEvents.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/queries/analytics/events/getWebsiteEvents.ts b/src/queries/analytics/events/getWebsiteEvents.ts index e558c6092b..60a932f559 100644 --- a/src/queries/analytics/events/getWebsiteEvents.ts +++ b/src/queries/analytics/events/getWebsiteEvents.ts @@ -15,13 +15,22 @@ export function getWebsiteEvents( async function relationalQuery(websiteId: string, filters: QueryFilters, pageParams?: PageParams) { const { pagedQuery } = prisma; const { query } = pageParams; + const { startDate, endDate } = filters; const where = { - id: websiteId, + websiteId, + createdAt: { + lte: endDate, + gte: startDate, + }, ...prisma.getSearchParameters(query, [{ eventName: 'contains' }, { urlPath: 'contains' }]), }; - return pagedQuery('WebsiteEvent', { where }, pageParams); + return pagedQuery( + 'websiteEvent', + { where }, + { orderBy: 'createdAt', sortDescending: true, ...pageParams }, + ); } async function clickhouseQuery(websiteId: string, filters: QueryFilters, pageParams?: PageParams) { From 511fec2ddba3282abc632b0774375c047c8ea09c Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Tue, 20 Aug 2024 14:15:47 -0700 Subject: [PATCH 119/132] Fixed timezone date format. --- .../[websiteId]/events/EventsTable.tsx | 4 ++-- .../[websiteId]/realtime/RealtimeLog.tsx | 4 ++-- .../[websiteId]/sessions/SessionsTable.tsx | 4 ++-- .../sessions/[sessionId]/SessionActivity.tsx | 6 +++--- .../sessions/[sessionId]/SessionInfo.tsx | 6 +++--- src/components/hooks/useTimezone.ts | 18 ++++++++++-------- 6 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx index 4100202b36..42eb8f7ad0 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx @@ -6,7 +6,7 @@ import Link from 'next/link'; import Icons from 'components/icons'; export function EventsTable({ data = [] }) { - const { formatDate } = useTimezone(); + const { formatTimezoneDate } = useTimezone(); const { formatMessage, labels } = useMessages(); const { renderTeamUrl } = useTeamUrl(); @@ -35,7 +35,7 @@ export function EventsTable({ data = [] }) { }} - {row => formatDate(row.createdAt, 'PPPpp')} + {row => formatTimezoneDate(row.createdAt, 'PPPpp')} ); diff --git a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx index 8bae2c17bd..98b7a924be 100644 --- a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx +++ b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx @@ -31,7 +31,7 @@ export function RealtimeLog({ data }: { data: RealtimeData }) { const { formatMessage, labels, messages, FormattedMessage } = useMessages(); const { formatValue } = useFormat(); const { locale } = useLocale(); - const { formatDate } = useTimezone(); + const { formatTimezoneDate } = useTimezone(); const { countryNames } = useCountryNames(locale); const [filter, setFilter] = useState(TYPE_ALL); @@ -54,7 +54,7 @@ export function RealtimeLog({ data }: { data: RealtimeData }) { }, ]; - const getTime = ({ createdAt, firstAt }) => formatDate(firstAt || createdAt, 'h:mm:ss'); + const getTime = ({ createdAt, firstAt }) => formatTimezoneDate(firstAt || createdAt, 'h:mm:ss'); const getColor = ({ id, sessionId }) => stringToColor(sessionId || id); diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx index e266885642..3fea4836c0 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx @@ -6,7 +6,7 @@ import styles from './SessionsTable.module.css'; import TypeIcon from 'components/common/TypeIcon'; export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean }) { - const { formatDate } = useTimezone(); + const { formatTimezoneDate } = useTimezone(); const { formatMessage, labels } = useMessages(); const { formatValue } = useFormat(); @@ -51,7 +51,7 @@ export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean )} - {row => formatDate(row.createdAt, 'PPPpp')} + {row => formatTimezoneDate(row.createdAt, 'PPPpp')} ); diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx index 187205e986..fd498ac3ab 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx @@ -15,7 +15,7 @@ export function SessionActivity({ startDate: Date; endDate: Date; }) { - const { formatDate } = useTimezone(); + const { formatTimezoneDate } = useTimezone(); const { data, isLoading } = useSessionActivity(websiteId, sessionId, startDate, endDate); if (isLoading) { @@ -33,12 +33,12 @@ export function SessionActivity({ return ( <> {showHeader && ( -
{formatDate(createdAt, 'EEEE, PPP')}
+
{formatTimezoneDate(createdAt, 'EEEE, PPP')}
)}
- {formatDate(createdAt, 'h:mm:ss aaa')} + {formatTimezoneDate(createdAt, 'h:mm:ss aaa')}
{eventName ? : } diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.tsx index 641d2941b0..6f9a8f3d57 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.tsx @@ -6,7 +6,7 @@ import styles from './SessionInfo.module.css'; export default function SessionInfo({ data }) { const { locale } = useLocale(); - const { formatDate } = useTimezone(); + const { formatTimezoneDate } = useTimezone(); const { formatMessage, labels } = useMessages(); const { formatValue } = useFormat(); const { getRegionName } = useRegionNames(locale); @@ -20,10 +20,10 @@ export default function SessionInfo({ data }) {
{formatMessage(labels.lastSeen)}
-
{formatDate(data?.lastAt, 'EEEE, PPPpp')}
+
{formatTimezoneDate(data?.lastAt, 'EEEE, PPPpp')}
{formatMessage(labels.firstSeen)}
-
{formatDate(data?.firstAt, 'EEEE, PPPpp')}
+
{formatTimezoneDate(data?.firstAt, 'EEEE, PPPpp')}
{formatMessage(labels.country)}
diff --git a/src/components/hooks/useTimezone.ts b/src/components/hooks/useTimezone.ts index 24cef02cce..d06562a2c4 100644 --- a/src/components/hooks/useTimezone.ts +++ b/src/components/hooks/useTimezone.ts @@ -13,13 +13,15 @@ export function useTimezone() { setTimezone(value); }; - const formatDate = (date: string, pattern: string) => { - return formatInTimeZone( - /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$/.test(date) - ? date - : date.split(' ').join('T') + 'Z', - timezone, - pattern, + const formatTimezoneDate = (date: string, pattern: string) => { + return ( + formatInTimeZone( + /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{3})?Z$/.test(date) + ? date + : date.split(' ').join('T') + 'Z', + timezone, + pattern, + ) + `, ${date}, ${pattern}, ${timezone}, ${formatInTimeZone(date, timezone, pattern)}` ); }; @@ -31,7 +33,7 @@ export function useTimezone() { return utcToZonedTime(date, timezone); }; - return { timezone, saveTimezone, formatDate, toUtc, fromUtc }; + return { timezone, saveTimezone, formatTimezoneDate, toUtc, fromUtc }; } export default useTimezone; From caa9da9166d35502315ba1026bbde03c622d2be5 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Tue, 20 Aug 2024 14:16:35 -0700 Subject: [PATCH 120/132] Removed junk. --- src/components/hooks/useTimezone.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/hooks/useTimezone.ts b/src/components/hooks/useTimezone.ts index d06562a2c4..c74f513f5f 100644 --- a/src/components/hooks/useTimezone.ts +++ b/src/components/hooks/useTimezone.ts @@ -14,14 +14,12 @@ export function useTimezone() { }; const formatTimezoneDate = (date: string, pattern: string) => { - return ( - formatInTimeZone( - /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{3})?Z$/.test(date) - ? date - : date.split(' ').join('T') + 'Z', - timezone, - pattern, - ) + `, ${date}, ${pattern}, ${timezone}, ${formatInTimeZone(date, timezone, pattern)}` + return formatInTimeZone( + /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{3})?Z$/.test(date) + ? date + : date.split(' ').join('T') + 'Z', + timezone, + pattern, ); }; From 04e0b33622463ee79c1b993cc4405c823bb39c8c Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Tue, 20 Aug 2024 14:53:53 -0700 Subject: [PATCH 121/132] Updated Clickhouse number handling. Removed number formatting. --- src/lib/clickhouse.ts | 1 + .../analytics/events/getEventDataEvents.ts | 23 ++----------------- .../events/getEventDataProperties.ts | 10 +------- .../analytics/events/getEventDataStats.ts | 12 ++-------- .../analytics/events/getEventDataUsage.ts | 6 +---- .../analytics/events/getEventDataValues.ts | 9 +------- src/queries/analytics/getActiveVisitors.ts | 14 ++++------- src/queries/analytics/getWebsiteStats.ts | 12 +--------- .../analytics/pageviews/getPageviewStats.ts | 6 +---- src/queries/analytics/reports/getRetention.ts | 16 ++----------- src/queries/analytics/reports/getRevenue.ts | 23 ++----------------- .../sessions/getSessionDataProperties.ts | 9 +------- .../sessions/getSessionDataValues.ts | 9 +------- .../analytics/sessions/getSessionStats.ts | 6 +---- .../sessions/getWebsiteSessionStats.ts | 12 +--------- 15 files changed, 23 insertions(+), 145 deletions(-) diff --git a/src/lib/clickhouse.ts b/src/lib/clickhouse.ts index 24e6a3b66d..397883fb58 100644 --- a/src/lib/clickhouse.ts +++ b/src/lib/clickhouse.ts @@ -187,6 +187,7 @@ async function rawQuery( query: query, query_params: params, format: 'JSONEachRow', + clickhouse_settings: { output_format_json_quote_64bit_integers: 0 }, }); return (await resultSet.json()) as T; diff --git a/src/queries/analytics/events/getEventDataEvents.ts b/src/queries/analytics/events/getEventDataEvents.ts index 6b57330c59..0b19c5be33 100644 --- a/src/queries/analytics/events/getEventDataEvents.ts +++ b/src/queries/analytics/events/getEventDataEvents.ts @@ -85,17 +85,7 @@ async function clickhouseQuery( limit 500 `, params, - ).then(result => { - return Object.values(result).map((a: any) => { - return { - eventName: a.eventName, - propertyName: a.propertyName, - dataType: Number(a.dataType), - propertyValue: a.propertyValue, - total: Number(a.total), - }; - }); - }); + ); } return rawQuery( @@ -113,14 +103,5 @@ async function clickhouseQuery( limit 500 `, params, - ).then(result => { - return Object.values(result).map((a: any) => { - return { - eventName: a.eventName, - propertyName: a.propertyName, - dataType: Number(a.dataType), - total: Number(a.total), - }; - }); - }); + ); } diff --git a/src/queries/analytics/events/getEventDataProperties.ts b/src/queries/analytics/events/getEventDataProperties.ts index 9d55f8967c..cdf76c3f5f 100644 --- a/src/queries/analytics/events/getEventDataProperties.ts +++ b/src/queries/analytics/events/getEventDataProperties.ts @@ -64,13 +64,5 @@ async function clickhouseQuery( limit 500 `, params, - ).then(result => { - return Object.values(result).map((a: any) => { - return { - eventName: a.eventName, - propertyName: a.propertyName, - total: Number(a.total), - }; - }); - }); + ); } diff --git a/src/queries/analytics/events/getEventDataStats.ts b/src/queries/analytics/events/getEventDataStats.ts index 51a0da746e..adeeda46ef 100644 --- a/src/queries/analytics/events/getEventDataStats.ts +++ b/src/queries/analytics/events/getEventDataStats.ts @@ -13,7 +13,7 @@ export async function getEventDataStats( return runQuery({ [PRISMA]: () => relationalQuery(...args), [CLICKHOUSE]: () => clickhouseQuery(...args), - }).then(results => results[0]); + }).then(results => results?.[0]); } async function relationalQuery(websiteId: string, filters: QueryFilters) { @@ -68,13 +68,5 @@ async function clickhouseQuery( ) as t `, params, - ).then(result => { - return Object.values(result).map((a: any) => { - return { - events: Number(a.events), - properties: Number(a.properties), - records: Number(a.records), - }; - }); - }); + ); } diff --git a/src/queries/analytics/events/getEventDataUsage.ts b/src/queries/analytics/events/getEventDataUsage.ts index 2a19f33e6d..1d146c9c71 100644 --- a/src/queries/analytics/events/getEventDataUsage.ts +++ b/src/queries/analytics/events/getEventDataUsage.ts @@ -30,9 +30,5 @@ function clickhouseQuery( startDate, endDate, }, - ).then(result => { - return Object.values(result).map((a: any) => { - return { websiteId: a.websiteId, count: Number(a.count) }; - }); - }); + ); } diff --git a/src/queries/analytics/events/getEventDataValues.ts b/src/queries/analytics/events/getEventDataValues.ts index 7ccd7a2863..f6b36b566c 100644 --- a/src/queries/analytics/events/getEventDataValues.ts +++ b/src/queries/analytics/events/getEventDataValues.ts @@ -66,12 +66,5 @@ async function clickhouseQuery( limit 100 `, params, - ).then(result => { - return Object.values(result).map((a: any) => { - return { - ...a, - total: Number(a.total), - }; - }); - }); + ); } diff --git a/src/queries/analytics/getActiveVisitors.ts b/src/queries/analytics/getActiveVisitors.ts index 6d55f5dedc..c59a265a8f 100644 --- a/src/queries/analytics/getActiveVisitors.ts +++ b/src/queries/analytics/getActiveVisitors.ts @@ -18,9 +18,9 @@ async function relationalQuery(websiteId: string) { select count(distinct session_id) x from website_event where website_id = {{websiteId::uuid}} - and created_at >= {{startAt}} + and created_at >= {{startDate}} `, - { websiteId, startAt: subMinutes(new Date(), 5) }, + { websiteId, startDate: subMinutes(new Date(), 5) }, ); return result[0] ?? null; @@ -35,14 +35,10 @@ async function clickhouseQuery(websiteId: string): Promise<{ x: number }> { count(distinct session_id) x from website_event where website_id = {websiteId:UUID} - and created_at >= {startAt:DateTime64} + and created_at >= {startDate:DateTime64} `, - { websiteId, startAt: subMinutes(new Date(), 5) }, - ).then(a => { - return Object.values(a).map(a => { - return { x: Number(a.x) }; - }); - }); + { websiteId, startDate: subMinutes(new Date(), 5) }, + ); return result[0] ?? null; } diff --git a/src/queries/analytics/getWebsiteStats.ts b/src/queries/analytics/getWebsiteStats.ts index c5141d3be7..061d487ead 100644 --- a/src/queries/analytics/getWebsiteStats.ts +++ b/src/queries/analytics/getWebsiteStats.ts @@ -117,15 +117,5 @@ async function clickhouseQuery( `; } - return rawQuery(sql, params).then(result => { - return Object.values(result).map((a: any) => { - return { - pageviews: Number(a.pageviews), - visitors: Number(a.visitors), - visits: Number(a.visits), - bounces: Number(a.bounces), - totaltime: Number(a.totaltime), - }; - }); - }); + return rawQuery(sql, params); } diff --git a/src/queries/analytics/pageviews/getPageviewStats.ts b/src/queries/analytics/pageviews/getPageviewStats.ts index 1d027e794f..0b80a2ed6a 100644 --- a/src/queries/analytics/pageviews/getPageviewStats.ts +++ b/src/queries/analytics/pageviews/getPageviewStats.ts @@ -87,9 +87,5 @@ async function clickhouseQuery( `; } - return rawQuery(sql, params).then(result => { - return Object.values(result).map((a: any) => { - return { x: a.x, y: Number(a.y) }; - }); - }); + return rawQuery(sql, params); } diff --git a/src/queries/analytics/reports/getRetention.ts b/src/queries/analytics/reports/getRetention.ts index 24aa2e3a81..eb184e1806 100644 --- a/src/queries/analytics/reports/getRetention.ts +++ b/src/queries/analytics/reports/getRetention.ts @@ -90,9 +90,7 @@ async function relationalQuery( startDate, endDate, }, - ).then(results => { - return results.map(i => ({ ...i, percentage: Number(i.percentage) || 0 })); - }); + ); } async function clickhouseQuery( @@ -169,15 +167,5 @@ async function clickhouseQuery( startDate, endDate, }, - ).then(result => { - return Object.values(result).map((a: any) => { - return { - date: a.date, - day: Number(a.day), - visitors: Number(a.visitors), - returnVisitors: Number(a.returnVisitors), - percentage: Number(a.percentage), - }; - }); - }); + ); } diff --git a/src/queries/analytics/reports/getRevenue.ts b/src/queries/analytics/reports/getRevenue.ts index e4857a43e5..502505f44f 100644 --- a/src/queries/analytics/reports/getRevenue.ts +++ b/src/queries/analytics/reports/getRevenue.ts @@ -143,17 +143,7 @@ async function clickhouseQuery( order by time `, { websiteId, startDate, endDate, eventName, revenueProperty, userProperty }, - ).then(result => { - return Object.values(result).map((a: any) => { - return { - time: a.time, - sum: Number(a.sum), - avg: Number(a.avg), - count: Number(a.count), - uniqueCount: Number(!a.avg ? 0 : a.uniqueCount), - }; - }); - }); + ).then(result => result?.[0]); const totalRes = await rawQuery<{ sum: number; @@ -174,16 +164,7 @@ async function clickhouseQuery( and data_key in ({revenueProperty:String}, {userProperty:String}) `, { websiteId, startDate, endDate, eventName, revenueProperty, userProperty }, - ).then(results => { - const result = results[0]; - - return { - sum: Number(result.sum), - avg: Number(result.avg), - count: Number(result.count), - uniqueCount: Number(!result.avg ? 0 : result.uniqueCount), - }; - }); + ); return { chart: chartRes, total: totalRes }; } diff --git a/src/queries/analytics/sessions/getSessionDataProperties.ts b/src/queries/analytics/sessions/getSessionDataProperties.ts index b33fecfbd2..1d15ea8de1 100644 --- a/src/queries/analytics/sessions/getSessionDataProperties.ts +++ b/src/queries/analytics/sessions/getSessionDataProperties.ts @@ -61,12 +61,5 @@ async function clickhouseQuery( limit 500 `, params, - ).then(result => { - return Object.values(result).map((a: any) => { - return { - propertyName: a.propertyName, - total: Number(a.total), - }; - }); - }); + ); } diff --git a/src/queries/analytics/sessions/getSessionDataValues.ts b/src/queries/analytics/sessions/getSessionDataValues.ts index 0f0f806a70..d9a7eb596b 100644 --- a/src/queries/analytics/sessions/getSessionDataValues.ts +++ b/src/queries/analytics/sessions/getSessionDataValues.ts @@ -61,12 +61,5 @@ async function clickhouseQuery( limit 100 `, params, - ).then(result => { - return Object.values(result).map((a: any) => { - return { - ...a, - total: Number(a.total), - }; - }); - }); + ); } diff --git a/src/queries/analytics/sessions/getSessionStats.ts b/src/queries/analytics/sessions/getSessionStats.ts index fa7483333d..af69f454c7 100644 --- a/src/queries/analytics/sessions/getSessionStats.ts +++ b/src/queries/analytics/sessions/getSessionStats.ts @@ -87,9 +87,5 @@ async function clickhouseQuery( `; } - return rawQuery(sql, params).then(result => { - return Object.values(result).map((a: any) => { - return { x: a.x, y: Number(a.y) }; - }); - }); + return rawQuery(sql, params); } diff --git a/src/queries/analytics/sessions/getWebsiteSessionStats.ts b/src/queries/analytics/sessions/getWebsiteSessionStats.ts index 5660a5fa25..648be14000 100644 --- a/src/queries/analytics/sessions/getWebsiteSessionStats.ts +++ b/src/queries/analytics/sessions/getWebsiteSessionStats.ts @@ -68,15 +68,5 @@ async function clickhouseQuery( ${filterQuery} `, params, - ).then(result => { - return Object.values(result).map((a: any) => { - return { - pageviews: Number(a.pageviews), - visitors: Number(a.visitors), - visits: Number(a.visits), - countries: Number(a.countries), - events: Number(a.events), - }; - }); - }); + ); } From 0220091cff9218f12eb630f2de036c42b93beb75 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Tue, 20 Aug 2024 18:59:24 -0700 Subject: [PATCH 122/132] add paging for relational raw query --- src/lib/prisma.ts | 27 ++++++++ .../analytics/events/getWebsiteEvents.ts | 54 +++++++++++----- .../analytics/sessions/getWebsiteSessions.ts | 63 ++++++++++++------- 3 files changed, 107 insertions(+), 37 deletions(-) diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts index 8c4f8c5c84..c450f1f023 100644 --- a/src/lib/prisma.ts +++ b/src/lib/prisma.ts @@ -261,6 +261,32 @@ async function pagedQuery(model: string, criteria: T, pageParams: PageParams) return { data, count, page: +page, pageSize: size, orderBy }; } +async function pagedRawQuery( + query: string, + queryParams: { [key: string]: any }, + pageParams: PageParams = {}, +) { + const { page = 1, pageSize, orderBy, sortDescending = false } = pageParams; + const size = +pageSize || DEFAULT_PAGE_SIZE; + const offset = +size * (page - 1); + const direction = sortDescending ? 'desc' : 'asc'; + + const statements = [ + orderBy && `order by ${orderBy} ${direction}`, + +size > 0 && `limit ${+size} offset ${offset}`, + ] + .filter(n => n) + .join('\n'); + + const count = await rawQuery(`select count(*) as num from (${query}) t`, queryParams).then( + res => res[0].num, + ); + + const data = await rawQuery(`${query}${statements}`, queryParams); + + return { data, count, page: +page, pageSize: size, orderBy }; +} + function getQueryMode(): { mode?: Prisma.QueryMode } { const db = getDatabaseType(); @@ -311,6 +337,7 @@ export default { getSearchSQL, getQueryMode, pagedQuery, + pagedRawQuery, parseFilters, rawQuery, }; diff --git a/src/queries/analytics/events/getWebsiteEvents.ts b/src/queries/analytics/events/getWebsiteEvents.ts index 60a932f559..5ac42dbb5e 100644 --- a/src/queries/analytics/events/getWebsiteEvents.ts +++ b/src/queries/analytics/events/getWebsiteEvents.ts @@ -1,5 +1,5 @@ import clickhouse from 'lib/clickhouse'; -import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; +import { CLICKHOUSE, getDatabaseType, POSTGRESQL, PRISMA, runQuery } from 'lib/db'; import prisma from 'lib/prisma'; import { PageParams, QueryFilters } from 'lib/types'; @@ -13,23 +13,47 @@ export function getWebsiteEvents( } async function relationalQuery(websiteId: string, filters: QueryFilters, pageParams?: PageParams) { - const { pagedQuery } = prisma; + const { pagedRawQuery, parseFilters } = prisma; const { query } = pageParams; - const { startDate, endDate } = filters; + const { filterQuery, params } = await parseFilters(websiteId, { + ...filters, + }); - const where = { - websiteId, - createdAt: { - lte: endDate, - gte: startDate, - }, - ...prisma.getSearchParameters(query, [{ eventName: 'contains' }, { urlPath: 'contains' }]), - }; + const db = getDatabaseType(); + const like = db === POSTGRESQL ? 'ilike' : 'like'; - return pagedQuery( - 'websiteEvent', - { where }, - { orderBy: 'createdAt', sortDescending: true, ...pageParams }, + return pagedRawQuery( + ` + with events as ( + select + event_id as "id", + website_id as "websiteId", + session_id as "sessionId", + created_at as "createdAt", + url_path as "urlPath", + url_query as "urlQuery", + referrer_path as "referrerPath", + referrer_query as "referrerQuery", + referrer_domain as "referrerDomain", + page_title as "pageTitle", + event_type as "eventType", + event_name as "eventName" + from website_event + where website_id = {{websiteId::uuid}} + and created_at between {{startDate}} and {{endDate}} + ${filterQuery} + ${ + query + ? `and (event_name ${like} {{query}} + or url_path ${like} {{query}})` + : '' + } + order by created_at desc + limit 1000) + select * from events + `, + { ...params, query: `%${query}%` }, + pageParams, ); } diff --git a/src/queries/analytics/sessions/getWebsiteSessions.ts b/src/queries/analytics/sessions/getWebsiteSessions.ts index 1ea3ef49e6..81e007ca08 100644 --- a/src/queries/analytics/sessions/getWebsiteSessions.ts +++ b/src/queries/analytics/sessions/getWebsiteSessions.ts @@ -1,6 +1,6 @@ -import prisma from 'lib/prisma'; import clickhouse from 'lib/clickhouse'; -import { runQuery, PRISMA, CLICKHOUSE } from 'lib/db'; +import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; +import prisma from 'lib/prisma'; import { PageParams, QueryFilters } from 'lib/types'; export async function getWebsiteSessions( @@ -13,14 +13,44 @@ export async function getWebsiteSessions( } async function relationalQuery(websiteId: string, filters: QueryFilters, pageParams: PageParams) { - const { pagedQuery } = prisma; - - const where = { + const { pagedRawQuery, parseFilters } = prisma; + const { filterQuery, params } = await parseFilters(websiteId, { ...filters, - id: websiteId, - }; + }); - return pagedQuery('session', { where }, pageParams); + return pagedRawQuery( + ` + with sessions as ( + select + s.session_id as "id", + s.website_id as "websiteId", + hostname, + browser, + os, + device, + screen, + language, + country, + subdivision1, + city, + min(we.created_at) as "firstAt", + max(we.created_at) as "lastAt", + count(distinct we.visit_id) as "visits", + sum(case when we.event_type = 1 then 1 else 0 end) as "views", + max(we.created_at) as "createdAt" + from website_event we + join session s on s.session_id = we.session_id + where we.website_id = {{websiteId::uuid}} + and we.created_at between {{startDate}} and {{endDate}} + ${filterQuery} + group by s.session_id, s.website_id, s.hostname, s.browser, s.os, s.device, s.screen, s.language, s.country, s.subdivision1, s.city + order by max(we.created_at) desc + limit 1000) + select * from sessions + `, + params, + pageParams, + ); } async function clickhouseQuery(websiteId: string, filters: QueryFilters, pageParams?: PageParams) { @@ -45,7 +75,8 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar ${getDateStringSQL('min(min_time)')} as firstAt, ${getDateStringSQL('max(max_time)')} as lastAt, uniq(visit_id) as visits, - sumIf(views, event_type = 1) as views + sumIf(views, event_type = 1) as views, + lastAt as createdAt from website_event_stats_hourly where website_id = {websiteId:UUID} ${dateQuery} @@ -57,17 +88,5 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar `, params, pageParams, - ).then((result: any) => { - return { - ...result, - data: result.data.map((row: any) => { - return { - ...row, - createdAt: row.lastAt, - visits: Number(row.visits), - views: Number(row.views), - }; - }), - }; - }); + ); } From e35c11c3d648f9ebd69698551c2dfd48d0f3afec Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Tue, 20 Aug 2024 23:58:20 -0700 Subject: [PATCH 123/132] Updated realtime data fetch. --- .../[websiteId]/realtime/RealtimeLog.tsx | 11 +-- src/components/hooks/queries/useRealtime.ts | 4 +- src/lib/clickhouse.ts | 5 +- src/pages/api/realtime/[websiteId].ts | 6 +- src/queries/analytics/getRealtimeActivity.ts | 67 +++++++++++++++ src/queries/analytics/getRealtimeData.ts | 85 +++++++------------ .../analytics/pageviews/getPageviewStats.ts | 12 +-- .../analytics/sessions/getSessionStats.ts | 16 ++-- src/queries/index.ts | 1 + 9 files changed, 123 insertions(+), 84 deletions(-) create mode 100644 src/queries/analytics/getRealtimeActivity.ts diff --git a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx index 98b7a924be..8a5f3a55f5 100644 --- a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx +++ b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx @@ -10,7 +10,6 @@ import { safeDecodeURI } from 'next-basics'; import { useContext, useMemo, useState } from 'react'; import { Icon, SearchField, StatusLight, Text } from 'react-basics'; import { FixedSizeList } from 'react-window'; -import thenby from 'thenby'; import { WebsiteContext } from '../WebsiteProvider'; import styles from './RealtimeLog.module.css'; @@ -141,15 +140,7 @@ export function RealtimeLog({ data }: { data: RealtimeData }) { return []; } - const { events, visitors } = data; - - let logs = [ - ...events.map(e => ({ - __type: e.eventName ? TYPE_EVENT : TYPE_PAGEVIEW, - ...e, - })), - ...visitors.map(v => ({ __type: TYPE_SESSION, ...v })), - ].sort(thenby.firstBy('createdAt', -1)); + let logs = data.events; if (search) { logs = logs.filter(({ eventName, urlPath, browser, os, country, device }) => { diff --git a/src/components/hooks/queries/useRealtime.ts b/src/components/hooks/queries/useRealtime.ts index 9c0c54f6be..88bf06e4b1 100644 --- a/src/components/hooks/queries/useRealtime.ts +++ b/src/components/hooks/queries/useRealtime.ts @@ -1,15 +1,13 @@ import { RealtimeData } from 'lib/types'; import { useApi } from './useApi'; import { REALTIME_INTERVAL } from 'lib/constants'; -import { useTimezone } from '../useTimezone'; export function useRealtime(websiteId: string) { const { get, useQuery } = useApi(); - const { timezone } = useTimezone(); const { data, isLoading, error } = useQuery({ queryKey: ['realtime', websiteId], queryFn: async () => { - return get(`/realtime/${websiteId}`, { timezone }); + return get(`/realtime/${websiteId}`); }, enabled: !!websiteId, refetchInterval: REALTIME_INTERVAL, diff --git a/src/lib/clickhouse.ts b/src/lib/clickhouse.ts index 397883fb58..332d07275c 100644 --- a/src/lib/clickhouse.ts +++ b/src/lib/clickhouse.ts @@ -187,7 +187,10 @@ async function rawQuery( query: query, query_params: params, format: 'JSONEachRow', - clickhouse_settings: { output_format_json_quote_64bit_integers: 0 }, + clickhouse_settings: { + date_time_output_format: 'iso', + output_format_json_quote_64bit_integers: 0, + }, }); return (await resultSet.json()) as T; diff --git a/src/pages/api/realtime/[websiteId].ts b/src/pages/api/realtime/[websiteId].ts index 82854f1c17..66dcabc97e 100644 --- a/src/pages/api/realtime/[websiteId].ts +++ b/src/pages/api/realtime/[websiteId].ts @@ -10,13 +10,11 @@ import { REALTIME_RANGE } from 'lib/constants'; export interface RealtimeRequestQuery { websiteId: string; - timezone: string; } const schema = { GET: yup.object().shape({ websiteId: yup.string().uuid().required(), - timezone: yup.string().required(), }), }; @@ -25,7 +23,7 @@ export default async (req: NextApiRequestQueryBody, res: N await useValidate(schema, req, res); if (req.method === 'GET') { - const { websiteId, timezone } = req.query; + const { websiteId } = req.query; if (!(await canViewWebsite(req.auth, websiteId))) { return unauthorized(res); @@ -33,7 +31,7 @@ export default async (req: NextApiRequestQueryBody, res: N const startDate = subMinutes(startOfMinute(new Date()), REALTIME_RANGE); - const data = await getRealtimeData(websiteId, { startDate, timezone }); + const data = await getRealtimeData(websiteId, { startDate }); return ok(res, data); } diff --git a/src/queries/analytics/getRealtimeActivity.ts b/src/queries/analytics/getRealtimeActivity.ts new file mode 100644 index 0000000000..11fcf2d8a8 --- /dev/null +++ b/src/queries/analytics/getRealtimeActivity.ts @@ -0,0 +1,67 @@ +import prisma from 'lib/prisma'; +import clickhouse from 'lib/clickhouse'; +import { runQuery, CLICKHOUSE, PRISMA } from 'lib/db'; +import { QueryFilters } from 'lib/types'; + +export async function getRealtimeActivity(...args: [websiteId: string, filters: QueryFilters]) { + return runQuery({ + [PRISMA]: () => relationalQuery(...args), + [CLICKHOUSE]: () => clickhouseQuery(...args), + }); +} + +async function relationalQuery(websiteId: string, filters: QueryFilters) { + const { rawQuery, parseFilters } = prisma; + const { params, filterQuery, dateQuery } = await parseFilters(websiteId, filters); + + return rawQuery( + ` + select + website_event.session_id as sessionId, + website_event.event_name as eventName, + website_event.created_at as createdAt, + session.browser, + session.os, + session.device, + session.country, + session.url_path as urlPath, + session.referrer_domain as referrerDomain + from website_event + inner join session + on session.session_id = website_event.session_id + where website_id = {{websiteId::uuid}} + ${filterQuery} + ${dateQuery} + order by created_at asc + limit 100 + `, + params, + ); +} + +async function clickhouseQuery(websiteId: string, filters: QueryFilters): Promise<{ x: number }> { + const { rawQuery, parseFilters } = clickhouse; + const { params, filterQuery, dateQuery } = await parseFilters(websiteId, filters); + + return rawQuery( + ` + select + session_id as sessionId, + event_name as eventName, + created_at as createdAt, + browser, + os, + device, + country, + url_path as urlPath, + referrer_domain as referrerDomain + from website_event + where website_id = {websiteId:UUID} + ${filterQuery} + ${dateQuery} + order by createdAt asc + limit 100 + `, + params, + ); +} diff --git a/src/queries/analytics/getRealtimeData.ts b/src/queries/analytics/getRealtimeData.ts index 3e691b4edb..bd0f8d5c87 100644 --- a/src/queries/analytics/getRealtimeData.ts +++ b/src/queries/analytics/getRealtimeData.ts @@ -1,11 +1,4 @@ -import { - getWebsiteSessions, - getWebsiteEvents, - getPageviewStats, - getSessionStats, -} from 'queries/index'; - -const MAX_SIZE = 50; +import { getRealtimeActivity, getPageviewStats, getSessionStats } from 'queries/index'; function increment(data: object, key: string) { if (key) { @@ -17,61 +10,47 @@ function increment(data: object, key: string) { } } -export async function getRealtimeData( - websiteId: string, - criteria: { startDate: Date; timezone: string }, -) { - const { startDate, timezone } = criteria; - const filters = { startDate, endDate: new Date(), unit: 'minute', timezone }; - const [events, sessions, pageviews, sessionviews] = await Promise.all([ - getWebsiteEvents(websiteId, { startDate, timezone }, { pageSize: 10000 }), - getWebsiteSessions(websiteId, { startDate, timezone }, { pageSize: 10000 }), +export async function getRealtimeData(websiteId: string, criteria: { startDate: Date }) { + const { startDate } = criteria; + const filters = { startDate, endDate: new Date(), unit: 'minute' }; + const [activity, pageviews, sessions] = await Promise.all([ + getRealtimeActivity(websiteId, filters), getPageviewStats(websiteId, filters), getSessionStats(websiteId, filters), ]); const uniques = new Set(); - const sessionStats = sessions.data.reduce( - (obj: { visitors: any; countries: any }, session: { id: any; country: any }) => { - const { countries, visitors } = obj; - const { id, country } = session; + const { countries, urls, referrers, events } = activity.reduce( + ( + obj: { countries: any; urls: any; referrers: any; events: any }, + event: { + sessionId: string; + urlPath: string; + referrerDomain: string; + country: string; + eventName: string; + }, + ) => { + const { countries, urls, referrers, events } = obj; + const { sessionId, urlPath, referrerDomain, country, eventName } = event; - if (!uniques.has(id)) { - uniques.add(id); + if (!uniques.has(sessionId)) { + uniques.add(sessionId); increment(countries, country); - if (visitors.length < MAX_SIZE) { - visitors.push(session); - } + events.push({ __type: 'session', ...event }); } - return obj; - }, - { - countries: {}, - visitors: [], - }, - ); - - const eventStats = events.data.reduce( - ( - obj: { urls: any; referrers: any; events: any }, - event: { urlPath: any; referrerDomain: any }, - ) => { - const { urls, referrers, events } = obj; - const { urlPath, referrerDomain } = event; - increment(urls, urlPath); increment(referrers, referrerDomain); - if (events.length < MAX_SIZE) { - events.push(event); - } + events.push({ __type: eventName ? 'event' : 'pageview', ...event }); return obj; }, { + countries: {}, urls: {}, referrers: {}, events: [], @@ -79,17 +58,19 @@ export async function getRealtimeData( ); return { - ...sessionStats, - ...eventStats, + countries, + urls, + referrers, + events: events.reverse(), series: { views: pageviews, - visitors: sessionviews, + visitors: sessions, }, totals: { - views: events.data.filter(e => !e.eventName).length, - visitors: uniques.size, - events: events.data.filter(e => e.eventName).length, - countries: Object.keys(sessionStats.countries).length, + views: pageviews.reduce((sum: number, { y }: { y: number }) => sum + y, 0), + visitors: sessions.reduce((sum: number, { y }: { y: number }) => sum + y, 0), + events: activity.filter(e => e.eventName).length, + countries: Object.keys(countries).length, }, timestamp: Date.now(), }; diff --git a/src/queries/analytics/pageviews/getPageviewStats.ts b/src/queries/analytics/pageviews/getPageviewStats.ts index 0b80a2ed6a..bf8a0e8f6a 100644 --- a/src/queries/analytics/pageviews/getPageviewStats.ts +++ b/src/queries/analytics/pageviews/getPageviewStats.ts @@ -40,8 +40,8 @@ async function clickhouseQuery( websiteId: string, filters: QueryFilters, ): Promise<{ x: string; y: number }[]> { - const { timezone = 'UTC', unit = 'day' } = filters; - const { parseFilters, rawQuery, getDateStringSQL, getDateSQL } = clickhouse; + const { unit = 'day' } = filters; + const { parseFilters, rawQuery } = clickhouse; const { filterQuery, params } = await parseFilters(websiteId, { ...filters, eventType: EVENT_TYPE.pageView, @@ -52,11 +52,11 @@ async function clickhouseQuery( if (EVENT_COLUMNS.some(item => Object.keys(filters).includes(item)) || unit === 'minute') { sql = ` select - ${getDateStringSQL('g.t', unit)} as x, + g.t as x, g.y as y from ( select - ${getDateSQL('created_at', unit, timezone)} as t, + date_trunc('${unit}', created_at) as t, count(*) as y from website_event where website_id = {websiteId:UUID} @@ -70,11 +70,11 @@ async function clickhouseQuery( } else { sql = ` select - ${getDateStringSQL('g.t', unit)} as x, + g.t as x, g.y as y from ( select - ${getDateSQL('created_at', unit, timezone)} as t, + date_trunc('${unit}', created_at) as t, sum(views)as y from website_event_stats_hourly website_event where website_id = {websiteId:UUID} diff --git a/src/queries/analytics/sessions/getSessionStats.ts b/src/queries/analytics/sessions/getSessionStats.ts index af69f454c7..ee16e42560 100644 --- a/src/queries/analytics/sessions/getSessionStats.ts +++ b/src/queries/analytics/sessions/getSessionStats.ts @@ -40,8 +40,8 @@ async function clickhouseQuery( websiteId: string, filters: QueryFilters, ): Promise<{ x: string; y: number }[]> { - const { timezone = 'UTC', unit = 'day' } = filters; - const { parseFilters, rawQuery, getDateStringSQL, getDateSQL } = clickhouse; + const { unit = 'day' } = filters; + const { parseFilters, rawQuery } = clickhouse; const { filterQuery, params } = await parseFilters(websiteId, { ...filters, eventType: EVENT_TYPE.pageView, @@ -52,11 +52,11 @@ async function clickhouseQuery( if (EVENT_COLUMNS.some(item => Object.keys(filters).includes(item)) || unit === 'minute') { sql = ` select - ${getDateStringSQL('g.t', unit)} as x, + g.t as x, g.y as y from ( - select - ${getDateSQL('created_at', unit, timezone)} as t, + select + date_trunc('${unit}', created_at) as t, count(distinct session_id) as y from website_event where website_id = {websiteId:UUID} @@ -70,11 +70,11 @@ async function clickhouseQuery( } else { sql = ` select - ${getDateStringSQL('g.t', unit)} as x, + g.t as x, g.y as y from ( - select - ${getDateSQL('created_at', unit, timezone)} as t, + select + date_trunc('${unit}', created_at) as t, uniq(session_id) as y from website_event_stats_hourly website_event where website_id = {websiteId:UUID} diff --git a/src/queries/index.ts b/src/queries/index.ts index 26c1df09a6..a2697ced58 100644 --- a/src/queries/index.ts +++ b/src/queries/index.ts @@ -31,6 +31,7 @@ export * from './analytics/sessions/getSessionActivity'; export * from './analytics/sessions/getSessionStats'; export * from './analytics/sessions/saveSessionData'; export * from './analytics/getActiveVisitors'; +export * from './analytics/getRealtimeActivity'; export * from './analytics/getRealtimeData'; export * from './analytics/getValues'; export * from './analytics/getWebsiteDateRange'; From 917916c2247dfff7a4f6ed0ba43420ab71de9b22 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Wed, 21 Aug 2024 13:51:28 -0700 Subject: [PATCH 124/132] update queries for event / session properties and values screens --- src/components/common/TypeIcon.tsx | 2 +- .../events/getEventDataProperties.ts | 14 ++--- .../analytics/events/getEventDataValues.ts | 19 ++++--- .../sessions/getSessionDataValues.ts | 10 ++-- .../analytics/sessions/getWebsiteSessions.ts | 54 +++++++++++-------- 5 files changed, 59 insertions(+), 40 deletions(-) diff --git a/src/components/common/TypeIcon.tsx b/src/components/common/TypeIcon.tsx index bb99c2f7f0..d617e75925 100644 --- a/src/components/common/TypeIcon.tsx +++ b/src/components/common/TypeIcon.tsx @@ -13,7 +13,7 @@ export function TypeIcon({ <> {value} Date: Wed, 21 Aug 2024 15:08:22 -0700 Subject: [PATCH 125/132] fix websiteSession screens --- src/queries/analytics/getRealtimeActivity.ts | 14 ++--- src/queries/analytics/getRealtimeData.ts | 4 +- .../analytics/sessions/getSessionActivity.ts | 2 +- .../analytics/sessions/getSessionData.ts | 28 +++++++--- .../analytics/sessions/getWebsiteSession.ts | 53 ++++++++++++++++--- 5 files changed, 78 insertions(+), 23 deletions(-) diff --git a/src/queries/analytics/getRealtimeActivity.ts b/src/queries/analytics/getRealtimeActivity.ts index 11fcf2d8a8..fba303b2f9 100644 --- a/src/queries/analytics/getRealtimeActivity.ts +++ b/src/queries/analytics/getRealtimeActivity.ts @@ -17,22 +17,22 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { return rawQuery( ` select - website_event.session_id as sessionId, - website_event.event_name as eventName, - website_event.created_at as createdAt, + website_event.session_id as "sessionId", + website_event.event_name as "eventName", + website_event.created_at as "createdAt", session.browser, session.os, session.device, session.country, - session.url_path as urlPath, - session.referrer_domain as referrerDomain + website_event.url_path as "urlPath", + website_event.referrer_domain as "referrerDomain" from website_event inner join session on session.session_id = website_event.session_id - where website_id = {{websiteId::uuid}} + where website_event.website_id = {{websiteId::uuid}} ${filterQuery} ${dateQuery} - order by created_at asc + order by website_event.created_at asc limit 100 `, params, diff --git a/src/queries/analytics/getRealtimeData.ts b/src/queries/analytics/getRealtimeData.ts index bd0f8d5c87..63aa9aecb8 100644 --- a/src/queries/analytics/getRealtimeData.ts +++ b/src/queries/analytics/getRealtimeData.ts @@ -67,8 +67,8 @@ export async function getRealtimeData(websiteId: string, criteria: { startDate: visitors: sessions, }, totals: { - views: pageviews.reduce((sum: number, { y }: { y: number }) => sum + y, 0), - visitors: sessions.reduce((sum: number, { y }: { y: number }) => sum + y, 0), + views: pageviews.reduce((sum: number, { y }: { y: number }) => Number(sum) + Number(y), 0), + visitors: sessions.reduce((sum: number, { y }: { y: number }) => Number(sum) + Number(y), 0), events: activity.filter(e => e.eventName).length, countries: Object.keys(countries).length, }, diff --git a/src/queries/analytics/sessions/getSessionActivity.ts b/src/queries/analytics/sessions/getSessionActivity.ts index c50a82d97e..3bda0d2917 100644 --- a/src/queries/analytics/sessions/getSessionActivity.ts +++ b/src/queries/analytics/sessions/getSessionActivity.ts @@ -19,7 +19,7 @@ async function relationalQuery( ) { return prisma.client.websiteEvent.findMany({ where: { - id: sessionId, + sessionId, websiteId, createdAt: { gte: startDate, lte: endDate }, }, diff --git a/src/queries/analytics/sessions/getSessionData.ts b/src/queries/analytics/sessions/getSessionData.ts index 509fcdbf6b..ce80b0351a 100644 --- a/src/queries/analytics/sessions/getSessionData.ts +++ b/src/queries/analytics/sessions/getSessionData.ts @@ -10,12 +10,26 @@ export async function getSessionData(...args: [websiteId: string, sessionId: str } async function relationalQuery(websiteId: string, sessionId: string) { - return prisma.client.sessionData.findMany({ - where: { - id: sessionId, - websiteId, - }, - }); + const { rawQuery } = prisma; + + return rawQuery( + ` + select + website_id as "websiteId", + session_id as "sessionId", + data_key as "dataKey", + data_type as "dataType", + replace(string_value, '.0000', '') as "stringValue", + number_value as "numberValue", + date_value as "dateValue", + created_at as "createdAt" + from session_data + where website_id = {{websiteId::uuid}} + and session_id = {{sessionId::uuid}} + order by data_key asc + `, + { websiteId, sessionId }, + ); } async function clickhouseQuery(websiteId: string, sessionId: string) { @@ -28,7 +42,7 @@ async function clickhouseQuery(websiteId: string, sessionId: string) { session_id as sessionId, data_key as dataKey, data_type as dataType, - string_value as stringValue, + replace(string_value, '.0000', '') as stringValue, number_value as numberValue, date_value as dateValue, created_at as createdAt diff --git a/src/queries/analytics/sessions/getWebsiteSession.ts b/src/queries/analytics/sessions/getWebsiteSession.ts index 6f672e7d8d..f9b9f39a08 100644 --- a/src/queries/analytics/sessions/getWebsiteSession.ts +++ b/src/queries/analytics/sessions/getWebsiteSession.ts @@ -10,12 +10,53 @@ export async function getWebsiteSession(...args: [websiteId: string, sessionId: } async function relationalQuery(websiteId: string, sessionId: string) { - return prisma.client.session.findUnique({ - where: { - id: sessionId, - websiteId, - }, - }); + const { rawQuery, getTimestampDiffSQL } = prisma; + + return rawQuery( + ` + select id, + website_id as "websiteId", + hostname, + browser, + os, + device, + screen, + language, + country, + subdivision1, + city, + min(min_time) as "firstAt", + max(max_time) as "lastAt", + count(distinct visit_id) as visits, + sum(views) as views, + sum(events) as events, + sum(${getTimestampDiffSQL('min_time', 'max_time')}) as "totaltime" + from (select + session.session_id as id, + website_event.visit_id, + session.website_id, + session.hostname, + session.browser, + session.os, + session.device, + session.screen, + session.language, + session.country, + session.subdivision1, + session.city, + min(website_event.created_at) as min_time, + max(website_event.created_at) as max_time, + sum(case when website_event.event_type = 1 then 1 else 0 end) as views, + sum(case when website_event.event_type = 1 then 1 else 0 end) as events + from session + join website_event on website_event.session_id = session.session_id + where session.website_id = {{websiteId::uuid}} + and session.session_id = {{sessionId::uuid}} + group by session.session_id, visit_id, session.website_id, session.hostname, session.browser, session.os, session.device, session.screen, session.language, session.country, session.subdivision1, session.city) t + group by id, website_id, hostname, browser, os, device, screen, language, country, subdivision1, city; + `, + { websiteId, sessionId }, + ).then(result => result?.[0]); } async function clickhouseQuery(websiteId: string, sessionId: string) { From db7d705767fc7f45d13ad15a856c7f5d54998914 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Wed, 21 Aug 2024 15:25:42 -0700 Subject: [PATCH 126/132] fix getwebsiteevent search --- src/queries/analytics/events/getWebsiteEvents.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/queries/analytics/events/getWebsiteEvents.ts b/src/queries/analytics/events/getWebsiteEvents.ts index 5ac42dbb5e..21e6270c76 100644 --- a/src/queries/analytics/events/getWebsiteEvents.ts +++ b/src/queries/analytics/events/getWebsiteEvents.ts @@ -44,8 +44,8 @@ async function relationalQuery(websiteId: string, filters: QueryFilters, pagePar ${filterQuery} ${ query - ? `and (event_name ${like} {{query}} - or url_path ${like} {{query}})` + ? `and ((event_name ${like} {{query}} and event_type = 2) + or (url_path ${like} {{query}} and event_type = 1))` : '' } order by created_at desc @@ -84,8 +84,8 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar ${filterQuery} ${ query - ? `and (positionCaseInsensitive(event_name, {query:String}) > 0 - or positionCaseInsensitive(url_path, {query:String}) > 0)` + ? `and ((positionCaseInsensitive(event_name, {query:String}) > 0 and event_type = 2) + or (positionCaseInsensitive(url_path, {query:String}) > 0 and event_type = 1))` : '' } order by created_at desc From 9b88611a38d22a7ae1d12205fa9b006665a6e648 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Thu, 22 Aug 2024 03:10:48 -0700 Subject: [PATCH 127/132] Fixed search field disappearing when results are empty. Closes #2879. --- src/app/(main)/reports/ReportsDataTable.tsx | 6 +-- .../(main)/settings/teams/TeamsDataTable.tsx | 6 +-- .../(main)/settings/users/UsersDataTable.tsx | 6 +-- .../settings/websites/WebsitesDataTable.tsx | 6 +-- .../sessions/SessionsDataTable.tsx | 6 +-- src/components/common/DataTable.tsx | 49 ++++++++++--------- 6 files changed, 31 insertions(+), 48 deletions(-) diff --git a/src/app/(main)/reports/ReportsDataTable.tsx b/src/app/(main)/reports/ReportsDataTable.tsx index 7c99fb25f7..b03edfc273 100644 --- a/src/app/(main)/reports/ReportsDataTable.tsx +++ b/src/app/(main)/reports/ReportsDataTable.tsx @@ -14,12 +14,8 @@ export default function ReportsDataTable({ }) { const queryResult = useReports({ websiteId, teamId }); - if (queryResult?.result?.data?.length === 0) { - return children; - } - return ( - + children}> {({ data }) => } ); diff --git a/src/app/(main)/settings/teams/TeamsDataTable.tsx b/src/app/(main)/settings/teams/TeamsDataTable.tsx index 6baeba3313..e8563b2eae 100644 --- a/src/app/(main)/settings/teams/TeamsDataTable.tsx +++ b/src/app/(main)/settings/teams/TeamsDataTable.tsx @@ -15,12 +15,8 @@ export function TeamsDataTable({ const { user } = useLogin(); const queryResult = useTeams(user.id); - if (queryResult?.result?.data?.length === 0) { - return children; - } - return ( - + children}> {({ data }) => { return ; }} diff --git a/src/app/(main)/settings/users/UsersDataTable.tsx b/src/app/(main)/settings/users/UsersDataTable.tsx index 681b4dc2d6..03addc749a 100644 --- a/src/app/(main)/settings/users/UsersDataTable.tsx +++ b/src/app/(main)/settings/users/UsersDataTable.tsx @@ -12,12 +12,8 @@ export function UsersDataTable({ }) { const queryResult = useUsers(); - if (queryResult?.result?.data?.length === 0) { - return children; - } - return ( - + children}> {({ data }) => } ); diff --git a/src/app/(main)/settings/websites/WebsitesDataTable.tsx b/src/app/(main)/settings/websites/WebsitesDataTable.tsx index 1780dfb4a3..d91bbeefa5 100644 --- a/src/app/(main)/settings/websites/WebsitesDataTable.tsx +++ b/src/app/(main)/settings/websites/WebsitesDataTable.tsx @@ -18,12 +18,8 @@ export function WebsitesDataTable({ }) { const queryResult = useWebsites({ teamId }); - if (queryResult?.result?.data?.length === 0) { - return children; - } - return ( - + children}> {({ data }) => ( + children}> {({ data }) => } ); diff --git a/src/components/common/DataTable.tsx b/src/components/common/DataTable.tsx index c3bab88ff4..22373f608e 100644 --- a/src/components/common/DataTable.tsx +++ b/src/components/common/DataTable.tsx @@ -1,11 +1,12 @@ import { ReactNode } from 'react'; import classNames from 'classnames'; -import { Banner, Loading, SearchField } from 'react-basics'; +import { Loading, SearchField } from 'react-basics'; import { useMessages, useNavigation } from 'components/hooks'; import Empty from 'components/common/Empty'; import Pager from 'components/common/Pager'; import { PagedQueryResult } from 'lib/types'; import styles from './DataTable.module.css'; +import { LoadingPanel } from 'components/common/LoadingPanel'; const DEFAULT_SEARCH_DELAY = 600; @@ -14,6 +15,7 @@ export interface DataTableProps { searchDelay?: number; allowSearch?: boolean; allowPaging?: boolean; + renderEmpty?: () => ReactNode; children: ReactNode | ((data: any) => ReactNode); } @@ -22,6 +24,7 @@ export function DataTable({ searchDelay = 600, allowSearch = true, allowPaging = true, + renderEmpty, children, }: DataTableProps) { const { formatMessage, labels, messages } = useMessages(); @@ -29,7 +32,7 @@ export function DataTable({ result, params, setParams, - query: { error, isLoading }, + query: { error, isLoading, isFetched }, } = queryResult || {}; const { page, pageSize, count, data } = result || {}; const { query } = params || {}; @@ -46,10 +49,6 @@ export function DataTable({ router.push(renderUrl({ page })); }; - if (error) { - return {formatMessage(messages.error)}; - } - return ( <> {allowSearch && (hasData || query) && ( @@ -62,23 +61,27 @@ export function DataTable({ placeholder={formatMessage(labels.search)} /> )} -
- {hasData ? (typeof children === 'function' ? children(result) : children) : null} - {isLoading && } - {!isLoading && !hasData && !query && } - {!isLoading && noResults && } -
- {allowPaging && hasData && ( - - )} + +
+ {hasData ? (typeof children === 'function' ? children(result) : children) : null} + {isLoading && } + {!isLoading && !hasData && !query && (renderEmpty ? renderEmpty() : )} + {!isLoading && noResults && } +
+ {allowPaging && hasData && ( + + )} +
); } From cfb3fcafc955b05cccdd41e0ee16df1b70efd7aa Mon Sep 17 00:00:00 2001 From: wjsoj Date: Thu, 22 Aug 2024 23:54:45 +0800 Subject: [PATCH 128/132] fix: a small type error --- src/lib/types.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lib/types.ts b/src/lib/types.ts index e659655202..d5bf05f5cf 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -143,10 +143,11 @@ export interface WebsitePageviews { } export interface WebsiteStats { - pageviews: { value: number; change: number }; - uniques: { value: number; change: number }; - bounces: { value: number; change: number }; - totalTime: { value: number; change: number }; + pageviews: { value: number; prev: number }; + visitors: { value: number; prev: number }; + visits: { value: number; prev: number }; + bounces: { value: number; prev: number }; + totalTime: { value: number; prev: number }; } export interface DateRange { From 647dcb5f25cb7672be71ea3bdb0845165a1c1bbd Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Thu, 22 Aug 2024 12:02:36 -0700 Subject: [PATCH 129/132] Fixed realtime chart for relational. Removed getDateArray. Added chart min/max dates. --- .../websites/[websiteId]/WebsiteChart.tsx | 15 +++++++++---- src/components/charts/BarChart.tsx | 6 +++++ src/components/metrics/EventsChart.tsx | 5 ----- src/components/metrics/RealtimeChart.tsx | 14 ++++++++---- src/lib/date.ts | 13 ----------- src/lib/prisma.ts | 22 ++++++------------- .../analytics/sessions/getSessionStats.ts | 17 +++++++++----- 7 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/WebsiteChart.tsx b/src/app/(main)/websites/[websiteId]/WebsiteChart.tsx index 48da23776c..fa5ab69423 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteChart.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteChart.tsx @@ -1,6 +1,5 @@ import { useMemo } from 'react'; import PageviewsChart from 'components/metrics/PageviewsChart'; -import { getDateArray } from 'lib/date'; import useWebsitePageviews from 'components/hooks/queries/useWebsitePageviews'; import { useDateRange } from 'components/hooks'; @@ -19,8 +18,8 @@ export function WebsiteChart({ const chartData = useMemo(() => { if (data) { const result = { - pageviews: getDateArray(pageviews, startDate, endDate, unit), - sessions: getDateArray(sessions, startDate, endDate, unit), + pageviews, + sessions, }; if (compare) { @@ -43,7 +42,15 @@ export function WebsiteChart({ return { pageviews: [], sessions: [] }; }, [data, startDate, endDate, unit]); - return ; + return ( + + ); } export default WebsiteChart; diff --git a/src/components/charts/BarChart.tsx b/src/components/charts/BarChart.tsx index cfcbe74350..b96812210e 100644 --- a/src/components/charts/BarChart.tsx +++ b/src/components/charts/BarChart.tsx @@ -12,6 +12,8 @@ export interface BarChartProps extends ChartProps { renderYLabel?: (label: string, index: number, values: any[]) => string; XAxisType?: string; YAxisType?: string; + minDate?: number | string; + maxDate?: number | string; } export function BarChart(props: BarChartProps) { @@ -24,6 +26,8 @@ export function BarChart(props: BarChartProps) { XAxisType = 'time', YAxisType = 'linear', stacked = false, + minDate, + maxDate, } = props; const options: any = useMemo(() => { @@ -32,6 +36,8 @@ export function BarChart(props: BarChartProps) { x: { type: XAxisType, stacked: true, + min: minDate, + max: maxDate, time: { unit, }, diff --git a/src/components/metrics/EventsChart.tsx b/src/components/metrics/EventsChart.tsx index 0bb65eb298..ee7f866cb3 100644 --- a/src/components/metrics/EventsChart.tsx +++ b/src/components/metrics/EventsChart.tsx @@ -1,7 +1,6 @@ import { useMemo } from 'react'; import { colord } from 'colord'; import BarChart from 'components/charts/BarChart'; -import { getDateArray } from 'lib/date'; import { useLocale, useDateRange, useWebsiteEventsSeries } from 'components/hooks'; import { CHART_COLORS } from 'lib/constants'; import { renderDateLabels } from 'lib/charts'; @@ -31,10 +30,6 @@ export function EventsChart({ websiteId, className }: EventsChartProps) { return obj; }, {}); - Object.keys(map).forEach(key => { - map[key] = getDateArray(map[key], startDate, endDate, unit); - }); - return { datasets: Object.keys(map).map((key, index) => { const color = colord(CHART_COLORS[index % CHART_COLORS.length]); diff --git a/src/components/metrics/RealtimeChart.tsx b/src/components/metrics/RealtimeChart.tsx index b24eedb50c..b2819f9c88 100644 --- a/src/components/metrics/RealtimeChart.tsx +++ b/src/components/metrics/RealtimeChart.tsx @@ -1,7 +1,6 @@ import { useMemo, useRef } from 'react'; import { startOfMinute, subMinutes, isBefore } from 'date-fns'; import PageviewsChart from './PageviewsChart'; -import { getDateArray } from 'lib/date'; import { DEFAULT_ANIMATION_DURATION, REALTIME_RANGE } from 'lib/constants'; import { RealtimeData } from 'lib/types'; @@ -22,8 +21,8 @@ export function RealtimeChart({ data, unit, ...props }: RealtimeChartProps) { } return { - pageviews: getDateArray(data.series.views, startDate, endDate, unit), - sessions: getDateArray(data.series.visitors, startDate, endDate, unit), + pageviews: data.series.views, + sessions: data.series.visitors, }; }, [data, startDate, endDate, unit]); @@ -37,7 +36,14 @@ export function RealtimeChart({ data, unit, ...props }: RealtimeChartProps) { }, [endDate]); return ( - + ); } diff --git a/src/lib/date.ts b/src/lib/date.ts index b8bfa6c765..b731140cf2 100644 --- a/src/lib/date.ts +++ b/src/lib/date.ts @@ -273,19 +273,6 @@ export function getMinimumUnit(startDate: number | Date, endDate: number | Date) return 'year'; } -export function getDateFromString(str: string) { - const [ymd, hms] = str.split(' '); - const [year, month, day] = ymd.split('-'); - - if (hms) { - const [hour, min, sec] = hms.split(':'); - - return new Date(+year, +month - 1, +day, +hour, +min, +sec); - } - - return new Date(+year, +month - 1, +day); -} - export function getDateArray(data: any[], startDate: Date, endDate: Date, unit: string) { const arr = []; const { diff, add, start } = DATE_FUNCTIONS[unit]; diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts index c450f1f023..110b4750e2 100644 --- a/src/lib/prisma.ts +++ b/src/lib/prisma.ts @@ -12,19 +12,11 @@ import { filtersToArray } from './params'; const log = debug('umami:prisma'); const MYSQL_DATE_FORMATS = { - minute: '%Y-%m-%d %H:%i:00', - hour: '%Y-%m-%d %H:00:00', - day: '%Y-%m-%d', - month: '%Y-%m-01', - year: '%Y-01-01', -}; - -const POSTGRESQL_DATE_FORMATS = { - minute: 'YYYY-MM-DD HH24:MI:00', - hour: 'YYYY-MM-DD HH24:00:00', - day: 'YYYY-MM-DD', - month: 'YYYY-MM-01', - year: 'YYYY-01-01', + minute: '%Y-%m-%dT%H:%i:00Z', + hour: '%Y-%m-%dT%H:00:00Z', + day: '%Y-%m-%dT00:00:00Z', + month: '%Y-%m-01T00:00:00Z', + year: '%Y-01-01T00:00:00Z', }; function getAddIntervalQuery(field: string, interval: string): string { @@ -68,9 +60,9 @@ function getDateSQL(field: string, unit: string, timezone?: string): string { if (db === POSTGRESQL) { if (timezone) { - return `to_char(date_trunc('${unit}', ${field} at time zone '${timezone}'), '${POSTGRESQL_DATE_FORMATS[unit]}')`; + return `date_trunc('${unit}', ${field} at time zone '${timezone}')`; } - return `to_char(date_trunc('${unit}', ${field}), '${POSTGRESQL_DATE_FORMATS[unit]}')`; + return `date_trunc('${unit}', ${field})`; } if (db === MYSQL) { diff --git a/src/queries/analytics/sessions/getSessionStats.ts b/src/queries/analytics/sessions/getSessionStats.ts index ee16e42560..cd04976155 100644 --- a/src/queries/analytics/sessions/getSessionStats.ts +++ b/src/queries/analytics/sessions/getSessionStats.ts @@ -22,14 +22,21 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { return rawQuery( ` select - ${getDateSQL('website_event.created_at', unit, timezone)} x, - count(distinct website_event.session_id) y - from website_event + ${getDateSQL('g.created_at', unit, timezone)} as x, + count(distinct g.session_id) as y + from ( + select + website_event.session_id, + min(website_event.created_at) as created_at, + count(*) y + from website_event ${joinSession} - where website_event.website_id = {{websiteId::uuid}} + where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} - and event_type = {{eventType}} + and event_type = {{eventType}} ${filterQuery} + group by website_event.session_id, website_event.created_at + ) as g group by 1 `, params, From 5e5b61dc7ee3026fabd988b578ffa9e35799845f Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Thu, 22 Aug 2024 13:14:40 -0700 Subject: [PATCH 130/132] Revert "Fixed realtime chart for relational. Removed getDateArray. Added chart min/max dates." This reverts commit 647dcb5f25cb7672be71ea3bdb0845165a1c1bbd. --- .../websites/[websiteId]/WebsiteChart.tsx | 15 ++++--------- src/components/charts/BarChart.tsx | 6 ----- src/components/metrics/EventsChart.tsx | 5 +++++ src/components/metrics/RealtimeChart.tsx | 14 ++++-------- src/lib/date.ts | 13 +++++++++++ src/lib/prisma.ts | 22 +++++++++++++------ .../analytics/sessions/getSessionStats.ts | 17 +++++--------- 7 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/WebsiteChart.tsx b/src/app/(main)/websites/[websiteId]/WebsiteChart.tsx index fa5ab69423..48da23776c 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteChart.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteChart.tsx @@ -1,5 +1,6 @@ import { useMemo } from 'react'; import PageviewsChart from 'components/metrics/PageviewsChart'; +import { getDateArray } from 'lib/date'; import useWebsitePageviews from 'components/hooks/queries/useWebsitePageviews'; import { useDateRange } from 'components/hooks'; @@ -18,8 +19,8 @@ export function WebsiteChart({ const chartData = useMemo(() => { if (data) { const result = { - pageviews, - sessions, + pageviews: getDateArray(pageviews, startDate, endDate, unit), + sessions: getDateArray(sessions, startDate, endDate, unit), }; if (compare) { @@ -42,15 +43,7 @@ export function WebsiteChart({ return { pageviews: [], sessions: [] }; }, [data, startDate, endDate, unit]); - return ( - - ); + return ; } export default WebsiteChart; diff --git a/src/components/charts/BarChart.tsx b/src/components/charts/BarChart.tsx index b96812210e..cfcbe74350 100644 --- a/src/components/charts/BarChart.tsx +++ b/src/components/charts/BarChart.tsx @@ -12,8 +12,6 @@ export interface BarChartProps extends ChartProps { renderYLabel?: (label: string, index: number, values: any[]) => string; XAxisType?: string; YAxisType?: string; - minDate?: number | string; - maxDate?: number | string; } export function BarChart(props: BarChartProps) { @@ -26,8 +24,6 @@ export function BarChart(props: BarChartProps) { XAxisType = 'time', YAxisType = 'linear', stacked = false, - minDate, - maxDate, } = props; const options: any = useMemo(() => { @@ -36,8 +32,6 @@ export function BarChart(props: BarChartProps) { x: { type: XAxisType, stacked: true, - min: minDate, - max: maxDate, time: { unit, }, diff --git a/src/components/metrics/EventsChart.tsx b/src/components/metrics/EventsChart.tsx index ee7f866cb3..0bb65eb298 100644 --- a/src/components/metrics/EventsChart.tsx +++ b/src/components/metrics/EventsChart.tsx @@ -1,6 +1,7 @@ import { useMemo } from 'react'; import { colord } from 'colord'; import BarChart from 'components/charts/BarChart'; +import { getDateArray } from 'lib/date'; import { useLocale, useDateRange, useWebsiteEventsSeries } from 'components/hooks'; import { CHART_COLORS } from 'lib/constants'; import { renderDateLabels } from 'lib/charts'; @@ -30,6 +31,10 @@ export function EventsChart({ websiteId, className }: EventsChartProps) { return obj; }, {}); + Object.keys(map).forEach(key => { + map[key] = getDateArray(map[key], startDate, endDate, unit); + }); + return { datasets: Object.keys(map).map((key, index) => { const color = colord(CHART_COLORS[index % CHART_COLORS.length]); diff --git a/src/components/metrics/RealtimeChart.tsx b/src/components/metrics/RealtimeChart.tsx index b2819f9c88..b24eedb50c 100644 --- a/src/components/metrics/RealtimeChart.tsx +++ b/src/components/metrics/RealtimeChart.tsx @@ -1,6 +1,7 @@ import { useMemo, useRef } from 'react'; import { startOfMinute, subMinutes, isBefore } from 'date-fns'; import PageviewsChart from './PageviewsChart'; +import { getDateArray } from 'lib/date'; import { DEFAULT_ANIMATION_DURATION, REALTIME_RANGE } from 'lib/constants'; import { RealtimeData } from 'lib/types'; @@ -21,8 +22,8 @@ export function RealtimeChart({ data, unit, ...props }: RealtimeChartProps) { } return { - pageviews: data.series.views, - sessions: data.series.visitors, + pageviews: getDateArray(data.series.views, startDate, endDate, unit), + sessions: getDateArray(data.series.visitors, startDate, endDate, unit), }; }, [data, startDate, endDate, unit]); @@ -36,14 +37,7 @@ export function RealtimeChart({ data, unit, ...props }: RealtimeChartProps) { }, [endDate]); return ( - + ); } diff --git a/src/lib/date.ts b/src/lib/date.ts index b731140cf2..b8bfa6c765 100644 --- a/src/lib/date.ts +++ b/src/lib/date.ts @@ -273,6 +273,19 @@ export function getMinimumUnit(startDate: number | Date, endDate: number | Date) return 'year'; } +export function getDateFromString(str: string) { + const [ymd, hms] = str.split(' '); + const [year, month, day] = ymd.split('-'); + + if (hms) { + const [hour, min, sec] = hms.split(':'); + + return new Date(+year, +month - 1, +day, +hour, +min, +sec); + } + + return new Date(+year, +month - 1, +day); +} + export function getDateArray(data: any[], startDate: Date, endDate: Date, unit: string) { const arr = []; const { diff, add, start } = DATE_FUNCTIONS[unit]; diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts index 110b4750e2..c450f1f023 100644 --- a/src/lib/prisma.ts +++ b/src/lib/prisma.ts @@ -12,11 +12,19 @@ import { filtersToArray } from './params'; const log = debug('umami:prisma'); const MYSQL_DATE_FORMATS = { - minute: '%Y-%m-%dT%H:%i:00Z', - hour: '%Y-%m-%dT%H:00:00Z', - day: '%Y-%m-%dT00:00:00Z', - month: '%Y-%m-01T00:00:00Z', - year: '%Y-01-01T00:00:00Z', + minute: '%Y-%m-%d %H:%i:00', + hour: '%Y-%m-%d %H:00:00', + day: '%Y-%m-%d', + month: '%Y-%m-01', + year: '%Y-01-01', +}; + +const POSTGRESQL_DATE_FORMATS = { + minute: 'YYYY-MM-DD HH24:MI:00', + hour: 'YYYY-MM-DD HH24:00:00', + day: 'YYYY-MM-DD', + month: 'YYYY-MM-01', + year: 'YYYY-01-01', }; function getAddIntervalQuery(field: string, interval: string): string { @@ -60,9 +68,9 @@ function getDateSQL(field: string, unit: string, timezone?: string): string { if (db === POSTGRESQL) { if (timezone) { - return `date_trunc('${unit}', ${field} at time zone '${timezone}')`; + return `to_char(date_trunc('${unit}', ${field} at time zone '${timezone}'), '${POSTGRESQL_DATE_FORMATS[unit]}')`; } - return `date_trunc('${unit}', ${field})`; + return `to_char(date_trunc('${unit}', ${field}), '${POSTGRESQL_DATE_FORMATS[unit]}')`; } if (db === MYSQL) { diff --git a/src/queries/analytics/sessions/getSessionStats.ts b/src/queries/analytics/sessions/getSessionStats.ts index cd04976155..ee16e42560 100644 --- a/src/queries/analytics/sessions/getSessionStats.ts +++ b/src/queries/analytics/sessions/getSessionStats.ts @@ -22,21 +22,14 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { return rawQuery( ` select - ${getDateSQL('g.created_at', unit, timezone)} as x, - count(distinct g.session_id) as y - from ( - select - website_event.session_id, - min(website_event.created_at) as created_at, - count(*) y - from website_event + ${getDateSQL('website_event.created_at', unit, timezone)} x, + count(distinct website_event.session_id) y + from website_event ${joinSession} - where website_event.website_id = {{websiteId::uuid}} + where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} - and event_type = {{eventType}} + and event_type = {{eventType}} ${filterQuery} - group by website_event.session_id, website_event.created_at - ) as g group by 1 `, params, From 8f934d2552eb8daedda321875333bfad39dc106c Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Thu, 22 Aug 2024 13:15:48 -0700 Subject: [PATCH 131/132] Reapply "Fixed realtime chart for relational. Removed getDateArray. Added chart min/max dates." This reverts commit 5e5b61dc7ee3026fabd988b578ffa9e35799845f. --- .../websites/[websiteId]/WebsiteChart.tsx | 15 +++++++++---- src/components/charts/BarChart.tsx | 6 +++++ src/components/metrics/EventsChart.tsx | 5 ----- src/components/metrics/RealtimeChart.tsx | 14 ++++++++---- src/lib/date.ts | 13 ----------- src/lib/prisma.ts | 22 ++++++------------- .../analytics/sessions/getSessionStats.ts | 17 +++++++++----- 7 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/WebsiteChart.tsx b/src/app/(main)/websites/[websiteId]/WebsiteChart.tsx index 48da23776c..fa5ab69423 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteChart.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteChart.tsx @@ -1,6 +1,5 @@ import { useMemo } from 'react'; import PageviewsChart from 'components/metrics/PageviewsChart'; -import { getDateArray } from 'lib/date'; import useWebsitePageviews from 'components/hooks/queries/useWebsitePageviews'; import { useDateRange } from 'components/hooks'; @@ -19,8 +18,8 @@ export function WebsiteChart({ const chartData = useMemo(() => { if (data) { const result = { - pageviews: getDateArray(pageviews, startDate, endDate, unit), - sessions: getDateArray(sessions, startDate, endDate, unit), + pageviews, + sessions, }; if (compare) { @@ -43,7 +42,15 @@ export function WebsiteChart({ return { pageviews: [], sessions: [] }; }, [data, startDate, endDate, unit]); - return ; + return ( + + ); } export default WebsiteChart; diff --git a/src/components/charts/BarChart.tsx b/src/components/charts/BarChart.tsx index cfcbe74350..b96812210e 100644 --- a/src/components/charts/BarChart.tsx +++ b/src/components/charts/BarChart.tsx @@ -12,6 +12,8 @@ export interface BarChartProps extends ChartProps { renderYLabel?: (label: string, index: number, values: any[]) => string; XAxisType?: string; YAxisType?: string; + minDate?: number | string; + maxDate?: number | string; } export function BarChart(props: BarChartProps) { @@ -24,6 +26,8 @@ export function BarChart(props: BarChartProps) { XAxisType = 'time', YAxisType = 'linear', stacked = false, + minDate, + maxDate, } = props; const options: any = useMemo(() => { @@ -32,6 +36,8 @@ export function BarChart(props: BarChartProps) { x: { type: XAxisType, stacked: true, + min: minDate, + max: maxDate, time: { unit, }, diff --git a/src/components/metrics/EventsChart.tsx b/src/components/metrics/EventsChart.tsx index 0bb65eb298..ee7f866cb3 100644 --- a/src/components/metrics/EventsChart.tsx +++ b/src/components/metrics/EventsChart.tsx @@ -1,7 +1,6 @@ import { useMemo } from 'react'; import { colord } from 'colord'; import BarChart from 'components/charts/BarChart'; -import { getDateArray } from 'lib/date'; import { useLocale, useDateRange, useWebsiteEventsSeries } from 'components/hooks'; import { CHART_COLORS } from 'lib/constants'; import { renderDateLabels } from 'lib/charts'; @@ -31,10 +30,6 @@ export function EventsChart({ websiteId, className }: EventsChartProps) { return obj; }, {}); - Object.keys(map).forEach(key => { - map[key] = getDateArray(map[key], startDate, endDate, unit); - }); - return { datasets: Object.keys(map).map((key, index) => { const color = colord(CHART_COLORS[index % CHART_COLORS.length]); diff --git a/src/components/metrics/RealtimeChart.tsx b/src/components/metrics/RealtimeChart.tsx index b24eedb50c..b2819f9c88 100644 --- a/src/components/metrics/RealtimeChart.tsx +++ b/src/components/metrics/RealtimeChart.tsx @@ -1,7 +1,6 @@ import { useMemo, useRef } from 'react'; import { startOfMinute, subMinutes, isBefore } from 'date-fns'; import PageviewsChart from './PageviewsChart'; -import { getDateArray } from 'lib/date'; import { DEFAULT_ANIMATION_DURATION, REALTIME_RANGE } from 'lib/constants'; import { RealtimeData } from 'lib/types'; @@ -22,8 +21,8 @@ export function RealtimeChart({ data, unit, ...props }: RealtimeChartProps) { } return { - pageviews: getDateArray(data.series.views, startDate, endDate, unit), - sessions: getDateArray(data.series.visitors, startDate, endDate, unit), + pageviews: data.series.views, + sessions: data.series.visitors, }; }, [data, startDate, endDate, unit]); @@ -37,7 +36,14 @@ export function RealtimeChart({ data, unit, ...props }: RealtimeChartProps) { }, [endDate]); return ( - + ); } diff --git a/src/lib/date.ts b/src/lib/date.ts index b8bfa6c765..b731140cf2 100644 --- a/src/lib/date.ts +++ b/src/lib/date.ts @@ -273,19 +273,6 @@ export function getMinimumUnit(startDate: number | Date, endDate: number | Date) return 'year'; } -export function getDateFromString(str: string) { - const [ymd, hms] = str.split(' '); - const [year, month, day] = ymd.split('-'); - - if (hms) { - const [hour, min, sec] = hms.split(':'); - - return new Date(+year, +month - 1, +day, +hour, +min, +sec); - } - - return new Date(+year, +month - 1, +day); -} - export function getDateArray(data: any[], startDate: Date, endDate: Date, unit: string) { const arr = []; const { diff, add, start } = DATE_FUNCTIONS[unit]; diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts index c450f1f023..110b4750e2 100644 --- a/src/lib/prisma.ts +++ b/src/lib/prisma.ts @@ -12,19 +12,11 @@ import { filtersToArray } from './params'; const log = debug('umami:prisma'); const MYSQL_DATE_FORMATS = { - minute: '%Y-%m-%d %H:%i:00', - hour: '%Y-%m-%d %H:00:00', - day: '%Y-%m-%d', - month: '%Y-%m-01', - year: '%Y-01-01', -}; - -const POSTGRESQL_DATE_FORMATS = { - minute: 'YYYY-MM-DD HH24:MI:00', - hour: 'YYYY-MM-DD HH24:00:00', - day: 'YYYY-MM-DD', - month: 'YYYY-MM-01', - year: 'YYYY-01-01', + minute: '%Y-%m-%dT%H:%i:00Z', + hour: '%Y-%m-%dT%H:00:00Z', + day: '%Y-%m-%dT00:00:00Z', + month: '%Y-%m-01T00:00:00Z', + year: '%Y-01-01T00:00:00Z', }; function getAddIntervalQuery(field: string, interval: string): string { @@ -68,9 +60,9 @@ function getDateSQL(field: string, unit: string, timezone?: string): string { if (db === POSTGRESQL) { if (timezone) { - return `to_char(date_trunc('${unit}', ${field} at time zone '${timezone}'), '${POSTGRESQL_DATE_FORMATS[unit]}')`; + return `date_trunc('${unit}', ${field} at time zone '${timezone}')`; } - return `to_char(date_trunc('${unit}', ${field}), '${POSTGRESQL_DATE_FORMATS[unit]}')`; + return `date_trunc('${unit}', ${field})`; } if (db === MYSQL) { diff --git a/src/queries/analytics/sessions/getSessionStats.ts b/src/queries/analytics/sessions/getSessionStats.ts index ee16e42560..cd04976155 100644 --- a/src/queries/analytics/sessions/getSessionStats.ts +++ b/src/queries/analytics/sessions/getSessionStats.ts @@ -22,14 +22,21 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { return rawQuery( ` select - ${getDateSQL('website_event.created_at', unit, timezone)} x, - count(distinct website_event.session_id) y - from website_event + ${getDateSQL('g.created_at', unit, timezone)} as x, + count(distinct g.session_id) as y + from ( + select + website_event.session_id, + min(website_event.created_at) as created_at, + count(*) y + from website_event ${joinSession} - where website_event.website_id = {{websiteId::uuid}} + where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} - and event_type = {{eventType}} + and event_type = {{eventType}} ${filterQuery} + group by website_event.session_id, website_event.created_at + ) as g group by 1 `, params, From 60f1b7b7558a30b54e7bcdec1f0d9ced3f55d919 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Thu, 22 Aug 2024 13:42:22 -0700 Subject: [PATCH 132/132] Revert session stats change. --- .../analytics/sessions/getSessionStats.ts | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/queries/analytics/sessions/getSessionStats.ts b/src/queries/analytics/sessions/getSessionStats.ts index cd04976155..ee16e42560 100644 --- a/src/queries/analytics/sessions/getSessionStats.ts +++ b/src/queries/analytics/sessions/getSessionStats.ts @@ -22,21 +22,14 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { return rawQuery( ` select - ${getDateSQL('g.created_at', unit, timezone)} as x, - count(distinct g.session_id) as y - from ( - select - website_event.session_id, - min(website_event.created_at) as created_at, - count(*) y - from website_event + ${getDateSQL('website_event.created_at', unit, timezone)} x, + count(distinct website_event.session_id) y + from website_event ${joinSession} - where website_event.website_id = {{websiteId::uuid}} + where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} - and event_type = {{eventType}} + and event_type = {{eventType}} ${filterQuery} - group by website_event.session_id, website_event.created_at - ) as g group by 1 `, params,