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 . diff --git a/db/clickhouse/schema.sql b/db/clickhouse/schema.sql index 53fba1fb76..3dcfda9f69 100644 --- a/db/clickhouse/schema.sql +++ b/db/clickhouse/schema.sql @@ -29,9 +29,11 @@ CREATE TABLE umami.website_event created_at DateTime('UTC'), job_id Nullable(UUID) ) - engine = MergeTree - ORDER BY (website_id, session_id, created_at) - 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 ( @@ -48,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 ( @@ -64,6 +66,129 @@ 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; \ No newline at end of file +ENGINE = ReplacingMergeTree + ORDER BY (website_id, session_id, data_key) + SETTINGS index_granularity = 8192; + +-- stats hourly +CREATE TABLE umami.website_event_stats_hourly +( + website_id UUID, + session_id UUID, + visit_id UUID, + hostname LowCardinality(String), + browser LowCardinality(String), + os LowCardinality(String), + device LowCardinality(String), + screen LowCardinality(String), + language LowCardinality(String), + country LowCardinality(String), + subdivision1 LowCardinality(String), + city String, + entry_url AggregateFunction(argMin, String, DateTime('UTC')), + exit_url AggregateFunction(argMax, String, DateTime('UTC')), + url_path SimpleAggregateFunction(groupArrayArray, Array(String)), + url_query SimpleAggregateFunction(groupArrayArray, Array(String)), + referrer_domain SimpleAggregateFunction(groupArrayArray, Array(String)), + page_title SimpleAggregateFunction(groupArrayArray, Array(String)), + event_type UInt32, + event_name SimpleAggregateFunction(groupArrayArray, Array(String)), + views SimpleAggregateFunction(sum, UInt64), + min_time SimpleAggregateFunction(min, DateTime('UTC')), + max_time SimpleAggregateFunction(max, DateTime('UTC')), + created_at Datetime('UTC') +) +ENGINE = AggregatingMergeTree + PARTITION BY toYYYYMM(created_at) + ORDER BY ( + website_id, + event_type, + toStartOfHour(created_at), + cityHash64(visit_id), + visit_id + ) + SAMPLE BY cityHash64(visit_id); + +CREATE MATERIALIZED VIEW umami.website_event_stats_hourly_mv +TO umami.website_event_stats_hourly +AS +SELECT + website_id, + session_id, + visit_id, + hostname, + browser, + os, + device, + screen, + language, + country, + subdivision1, + city, + entry_url, + exit_url, + url_paths as url_path, + url_query, + referrer_domain, + page_title, + event_type, + event_name, + views, + min_time, + max_time, + timestamp as created_at +FROM (SELECT + website_id, + session_id, + visit_id, + hostname, + browser, + os, + device, + screen, + language, + country, + subdivision1, + city, + argMinState(url_path, created_at) entry_url, + argMaxState(url_path, created_at) exit_url, + arrayFilter(x -> x != '', groupArray(url_path)) as url_paths, + arrayFilter(x -> x != '', groupArray(url_query)) url_query, + arrayFilter(x -> x != '', groupArray(referrer_domain)) referrer_domain, + arrayFilter(x -> x != '', groupArray(page_title)) page_title, + event_type, + if(event_type = 2, groupArray(event_name), []) event_name, + sumIf(1, event_type = 1) views, + min(created_at) min_time, + max(created_at) max_time, + toStartOfHour(created_at) timestamp +FROM umami.website_event +GROUP BY website_id, + session_id, + visit_id, + hostname, + browser, + os, + device, + screen, + language, + country, + subdivision1, + city, + event_type, + timestamp); + +-- 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; 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 d002388f8a..bd1ed4e692 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", @@ -64,9 +64,12 @@ ".next/cache" ], "dependencies": { - "@clickhouse/client": "^1.0.2", + "@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.14.0", + "@prisma/client": "5.17", "@prisma/extension-read-replicas": "^0.3.0", "@react-spring/web": "^9.7.3", "@tanstack/react-query": "^5.28.6", @@ -81,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", @@ -93,18 +95,18 @@ "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", "moment-timezone": "^0.5.35", - "next": "14.2.3", + "next": "14.2.5", "next-basics": "^0.39.0", "node-fetch": "^3.2.8", "npm-run-all": "^4.1.5", - "prisma": "5.14.0", + "prisma": "5.17", "react": "^18.2.0", - "react-basics": "^0.123.0", + "react-basics": "^0.125.0", "react-beautiful-dnd": "^13.1.0", "react-dom": "^18.2.0", "react-error-boundary": "^4.0.4", @@ -117,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", @@ -175,6 +177,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/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/public/intl/messages/am-ET.json b/public/intl/messages/am-ET.json index 6565c98b20..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 721584d5a1..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 9af4e89523..d5ca0a1e78 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": [ @@ -11,34 +11,34 @@ "value": "Дзеянні" } ], - "label.activity-log": [ + "label.activity": [ { "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,127 @@ "label.filters": [ { "type": 0, - "value": "Filters" + "value": "Фільтры" + } + ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" } ], "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": [ @@ -616,7 +622,7 @@ }, { "type": 0, - "value": " дні" + "value": " дзён" } ], "label.last-hours": [ @@ -636,7 +642,7 @@ "label.last-months": [ { "type": 0, - "value": "Last " + "value": "Апошнія " }, { "type": 1, @@ -644,37 +650,43 @@ }, { "type": 0, - "value": " months" + "value": " месяцаў" + } + ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" } ], "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 +698,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 +746,13 @@ "label.my-account": [ { "type": 0, - "value": "My account" + "value": "Мой уліковы запіс" } ], "label.my-websites": [ { "type": 0, - "value": "My websites" + "value": "Мае сайты" } ], "label.name": [ @@ -777,7 +789,7 @@ "value": [ { "type": 0, - "value": "record" + "value": "запіс" } ] }, @@ -785,7 +797,7 @@ "value": [ { "type": 0, - "value": "records" + "value": "запісаў" } ] } @@ -798,19 +810,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 +834,7 @@ "label.page-of": [ { "type": 0, - "value": "Page " + "value": "Старонка " }, { "type": 1, @@ -830,7 +842,7 @@ }, { "type": 0, - "value": " of " + "value": " з " }, { "type": 1, @@ -846,7 +858,7 @@ "label.pageTitle": [ { "type": 0, - "value": "Page title" + "value": "Загаловак старонкі" } ], "label.pages": [ @@ -861,6 +873,18 @@ "value": "Пароль" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -874,19 +898,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": [ @@ -895,28 +919,34 @@ "value": "Профіль" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "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 +958,13 @@ "label.referrer": [ { "type": 0, - "value": "Referrer" + "value": "Рэферэр" } ], "label.referrers": [ { "type": 0, - "value": "Referrers" + "value": "Рэферэры" } ], "label.refresh": [ @@ -946,37 +976,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 +1030,43 @@ "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.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, - "value": "Role" + "value": "Роля" } ], "label.run-query": [ { "type": 0, - "value": "Run query" + "value": "Запусціць запыт" } ], "label.save": [ @@ -1036,37 +1084,43 @@ "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.session": [ + { + "type": 0, + "value": "Session" } ], "label.sessions": [ { "type": 0, - "value": "Sessions" + "value": "Сесіі" } ], "label.settings": [ @@ -1090,19 +1144,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 +1168,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 +1246,13 @@ "label.timezone": [ { "type": 0, - "value": "Таймзона" + "value": "Часавы пояс" } ], "label.title": [ { "type": 0, - "value": "Title" + "value": "Загаловак" } ], "label.today": [ @@ -1216,13 +1270,13 @@ "label.total": [ { "type": 0, - "value": "Total" + "value": "Агульная колькасць" } ], "label.total-records": [ { "type": 0, - "value": "Total records" + "value": "Агульная колькасць запісаў" } ], "label.tracking-code": [ @@ -1231,34 +1285,40 @@ "value": "Код адсочвання" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "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": [ @@ -1267,6 +1327,12 @@ "value": "Унікальныя наведвальнікі" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1276,13 +1342,13 @@ "label.untitled": [ { "type": 0, - "value": "Untitled" + "value": "Без назвы" } ], "label.update": [ { "type": 0, - "value": "Update" + "value": "Абнавіць" } ], "label.url": [ @@ -1300,7 +1366,13 @@ "label.user": [ { "type": 0, - "value": "User" + "value": "Карыстальнік" + } + ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" } ], "label.username": [ @@ -1312,7 +1384,7 @@ "label.users": [ { "type": 0, - "value": "Users" + "value": "Карыстальнікі" } ], "label.utm": [ @@ -1324,31 +1396,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 +1432,7 @@ "label.views-per-visit": [ { "type": 0, - "value": "Views per visit" + "value": "Прагляды за наведванне" } ], "label.visit-duration": [ @@ -1378,19 +1450,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 +1474,7 @@ "label.window": [ { "type": 0, - "value": "Window" + "value": "Вакно" } ], "label.yesterday": [ @@ -1414,7 +1486,7 @@ "message.action-confirmation": [ { "type": 0, - "value": "Type " + "value": "Увядзіце " }, { "type": 1, @@ -1422,7 +1494,7 @@ }, { "type": 0, - "value": " in the box below to confirm." + "value": " у поле ніжэй, каб пацвердзіць." } ], "message.active-users": [ @@ -1432,7 +1504,7 @@ }, { "type": 0, - "value": " тякучых " + "value": " цякучых " }, { "offset": 0, @@ -1462,7 +1534,7 @@ "message.collected-data": [ { "type": 0, - "value": "Collected data" + "value": "Сабраныя дадзеныя" } ], "message.confirm-delete": [ @@ -1482,7 +1554,7 @@ "message.confirm-leave": [ { "type": 0, - "value": "Are you sure you want to leave " + "value": "Вы дакладна хочаце пакінуць " }, { "type": 1, @@ -1496,7 +1568,7 @@ "message.confirm-remove": [ { "type": 0, - "value": "Are you sure you want to remove " + "value": "Вы дакладна хочаце выдаліць " }, { "type": 1, @@ -1524,7 +1596,7 @@ "message.delete-team-warning": [ { "type": 0, - "value": "Deleting a team will also delete all team websites." + "value": "Выдаленне каманды таксама выдаліць усе сайты каманды." } ], "message.delete-website-warning": [ @@ -1546,7 +1618,7 @@ }, { "type": 0, - "value": " on " + "value": " на " }, { "type": 1, @@ -1562,7 +1634,7 @@ "message.incorrect-username-password": [ { "type": 0, - "value": "Некарэктны username/password." + "value": "Некарэктнае імя карыстальніка/пароль." } ], "message.invalid-domain": [ @@ -1574,7 +1646,7 @@ "message.min-password-length": [ { "type": 0, - "value": "Minimum length of " + "value": "Мінімальная даўжыня " }, { "type": 1, @@ -1582,13 +1654,13 @@ }, { "type": 0, - "value": " characters" + "value": " знакаў" } ], "message.new-version-available": [ { "type": 0, - "value": "A new version of Umami " + "value": "Даступная новая версія Umami " }, { "type": 1, @@ -1596,7 +1668,7 @@ }, { "type": 0, - "value": " is available!" + "value": "!" } ], "message.no-data-available": [ @@ -1608,7 +1680,7 @@ "message.no-event-data": [ { "type": 0, - "value": "No event data is available." + "value": "Дадзеныя падзеі недаступныя." } ], "message.no-match-password": [ @@ -1620,31 +1692,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 +1728,7 @@ "message.reset-website": [ { "type": 0, - "value": "To reset this website, type " + "value": "Каб скінуць гэты сайт, увядзіце " }, { "type": 1, @@ -1664,13 +1736,13 @@ }, { "type": 0, - "value": " in the box below to confirm." + "value": " у поле ніжэй для пацверджання." } ], "message.reset-website-warning": [ { "type": 0, - "value": "Уся статыстыка для гэтага сайту будзе выдалена, але код адсочвання будзе працягваць працаваць." + "value": "Уся статыстыка для гэтага сайта будзе выдалена, але код адсочвання будзе працягваць працаваць." } ], "message.saved": [ @@ -1696,19 +1768,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 +1792,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 +1862,7 @@ "message.visitors-dropped-off": [ { "type": 0, - "value": "Visitors dropped off" + "value": "Наведвальнікі сышлі" } ] } diff --git a/public/intl/messages/bg-BG.json b/public/intl/messages/bg-BG.json index bf5f15dcd5..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 827271e3b6..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 f3edb77352..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 f9b63535f0..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" @@ -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": [ @@ -473,6 +473,12 @@ "value": "Filtres" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -488,19 +494,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 +524,13 @@ "label.host": [ { "type": 0, - "value": "Host" + "value": "Amfitrió" } ], "label.hosts": [ { "type": 0, - "value": "Hosts" + "value": "Amfitrions" } ], "label.insights": [ @@ -578,13 +584,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": [ @@ -647,6 +653,12 @@ "value": " mesos" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -777,7 +789,7 @@ "value": [ { "type": 0, - "value": "record" + "value": "registre" } ] }, @@ -785,7 +797,7 @@ "value": [ { "type": 0, - "value": "records" + "value": "registres" } ] } @@ -861,6 +873,18 @@ "value": "Contrasenya" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -874,19 +898,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": [ @@ -895,10 +919,16 @@ "value": "Perfil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, - "value": "Property" + "value": "Propietat" } ], "label.queries": [ @@ -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, @@ -1090,7 +1144,7 @@ "label.start-step": [ { "type": 0, - "value": "Start Step" + "value": "Pas inicial" } ], "label.steps": [ @@ -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, @@ -1360,7 +1432,7 @@ "label.views-per-visit": [ { "type": 0, - "value": "Views per visit" + "value": "Vistes per visita" } ], "label.visit-duration": [ @@ -1462,7 +1534,7 @@ "message.collected-data": [ { "type": 0, - "value": "Collected data" + "value": "Dades recol·lectades" } ], "message.confirm-delete": [ @@ -1790,7 +1862,7 @@ "message.visitors-dropped-off": [ { "type": 0, - "value": "Els visitants han sortit" + "value": "Visitants han sortit" } ] } diff --git a/public/intl/messages/cs-CZ.json b/public/intl/messages/cs-CZ.json index a2500688a0..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 d9a6903f80..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 32d3b4434c..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 9a9b6d14f7..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" @@ -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": "Analysen" } ], "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": "Vergleichen" } ], "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": [ @@ -368,7 +368,7 @@ "label.dropoff": [ { "type": 0, - "value": "Dropoff" + "value": "Absprung" } ], "label.edit": [ @@ -386,7 +386,7 @@ "label.edit-member": [ { "type": 0, - "value": "Edit member" + "value": "Mitglied bearbeiten" } ], "label.enable-share-url": [ @@ -398,13 +398,13 @@ "label.end-step": [ { "type": 0, - "value": "End Step" + "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": [ @@ -473,6 +473,12 @@ "value": "Filter" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -482,25 +488,25 @@ "label.funnel-description": [ { "type": 0, - "value": "Understand the conversion and drop-off rate of users." + "value": "Verstehe die Konversions- und Abbruchrate der Nutzer." } ], "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 Seitenaufrufe und Ereignisse." } ], "label.greater-than": [ @@ -530,13 +536,13 @@ "label.insights": [ { "type": 0, - "value": "Insights" + "value": "Einblicke" } ], "label.insights-description": [ { "type": 0, - "value": "Dive deeper into your data by using segments and filters." + "value": "Vertiefen Sie Ihre Daten mit Hilfe von Segmenten und Filtern." } ], "label.is": [ @@ -578,13 +584,13 @@ "label.journey": [ { "type": 0, - "value": "Journey" + "value": "Reise" } ], "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Verstehe, wie Nutzer auf der Webseite navigieren" } ], "label.language": [ @@ -608,7 +614,7 @@ "label.last-days": [ { "type": 0, - "value": "Letzte " + "value": "Letzten " }, { "type": 1, @@ -622,7 +628,7 @@ "label.last-hours": [ { "type": 0, - "value": "Letzte " + "value": "Letzten " }, { "type": 1, @@ -636,7 +642,7 @@ "label.last-months": [ { "type": 0, - "value": "Last " + "value": "Letzten " }, { "type": 1, @@ -644,7 +650,13 @@ }, { "type": 0, - "value": " months" + "value": " Monate" + } + ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" } ], "label.leave": [ @@ -686,13 +698,13 @@ "label.manage": [ { "type": 0, - "value": "Manage" + "value": "Verwalten" } ], "label.manager": [ { "type": 0, - "value": "Manager" + "value": "Verwaltung" } ], "label.max": [ @@ -704,7 +716,7 @@ "label.member": [ { "type": 0, - "value": "Member" + "value": "Mitglied" } ], "label.members": [ @@ -734,13 +746,13 @@ "label.my-account": [ { "type": 0, - "value": "My account" + "value": "Mein Account" } ], "label.my-websites": [ { "type": 0, - "value": "Meine Websites" + "value": "Meine Webseiten" } ], "label.name": [ @@ -861,6 +873,18 @@ "value": "Passwort" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -874,19 +898,19 @@ "label.previous": [ { "type": 0, - "value": "Previous" + "value": "Vorheriges" } ], "label.previous-period": [ { "type": 0, - "value": "Previous period" + "value": "Vorherige Periode" } ], "label.previous-year": [ { "type": 0, - "value": "Previous year" + "value": "Vorheriges Jahr" } ], "label.profile": [ @@ -895,10 +919,16 @@ "value": "Profil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, - "value": "Property" + "value": "Eigentum" } ], "label.queries": [ @@ -970,7 +1000,7 @@ "label.remove-member": [ { "type": 0, - "value": "Remove member" + "value": "Mitglied entfernen" } ], "label.reports": [ @@ -1000,13 +1030,31 @@ "label.retention": [ { "type": 0, - "value": "Retention" + "value": "Bewahrung" } ], "label.retention-description": [ { "type": 0, - "value": "Measure your website stickiness by tracking how often users return." + "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": [ @@ -1036,13 +1084,13 @@ "label.search": [ { "type": 0, - "value": "Search" + "value": "Suche" } ], "label.select": [ { "type": 0, - "value": "Select" + "value": "Auswählen" } ], "label.select-date": [ @@ -1054,13 +1102,19 @@ "label.select-role": [ { "type": 0, - "value": "Select role" + "value": "Rolle auswählen" } ], "label.select-website": [ { "type": 0, - "value": "Website auswählen" + "value": "Webseite auswählen" + } + ], + "label.session": [ + { + "type": 0, + "value": "Session" } ], "label.sessions": [ @@ -1090,13 +1144,13 @@ "label.start-step": [ { "type": 0, - "value": "Start Step" + "value": "Startschritt" } ], "label.steps": [ { "type": 0, - "value": "Steps" + "value": "Schritte" } ], "label.sum": [ @@ -1126,7 +1180,7 @@ "label.team-manager": [ { "type": 0, - "value": "Team manager" + "value": "Team-Manager" } ], "label.team-member": [ @@ -1150,7 +1204,7 @@ "label.team-view-only": [ { "type": 0, - "value": "Team view only" + "value": "Nur für Team sichtbar" } ], "label.team-websites": [ @@ -1231,16 +1285,22 @@ "value": "Tracking Code" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "label.transfer": [ { "type": 0, - "value": "Transfer" + "value": "Übertragung" } ], "label.transfer-website": [ { "type": 0, - "value": "Transfer website" + "value": "Website übertragen" } ], "label.true": [ @@ -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, @@ -1324,7 +1396,7 @@ "label.utm-description": [ { "type": 0, - "value": "Track your campaigns through UTM parameters." + "value": "Tracke deine Kampagnen mit UTM parameterns." } ], "label.value": [ @@ -1360,7 +1432,7 @@ "label.views-per-visit": [ { "type": 0, - "value": "Views per visit" + "value": "Aufrufe pro Besuch" } ], "label.visit-duration": [ @@ -1378,25 +1450,25 @@ "label.visits": [ { "type": 0, - "value": "Visits" + "value": "Besuche" } ], "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 +1486,7 @@ "message.action-confirmation": [ { "type": 0, - "value": "Type " + "value": "Schreibe " }, { "type": 1, @@ -1422,7 +1494,7 @@ }, { "type": 0, - "value": " in the box below to confirm." + "value": " in die Box zur bestätigung." } ], "message.active-users": [ @@ -1462,7 +1534,7 @@ "message.collected-data": [ { "type": 0, - "value": "Collected data" + "value": "Gesammelte Daten" } ], "message.confirm-delete": [ @@ -1496,7 +1568,7 @@ "message.confirm-remove": [ { "type": 0, - "value": "Are you sure you want to remove " + "value": "Sind Sie sicher, " }, { "type": 1, @@ -1504,7 +1576,7 @@ }, { "type": 0, - "value": "?" + "value": " zu entfernen?" } ], "message.confirm-reset": [ @@ -1524,7 +1596,7 @@ "message.delete-team-warning": [ { "type": 0, - "value": "Deleting a team will also delete all team websites." + "value": "Ein Team zu löschen, wird auch alle Team-Webseiten löschen." } ], "message.delete-website-warning": [ @@ -1708,25 +1780,25 @@ "message.transfer-team-website-to-user": [ { "type": 0, - "value": "Transfer this website to your account?" + "value": "Diese Webseite zu deinem Account transferieren?" } ], "message.transfer-user-website-to-team": [ { "type": 0, - "value": "Select the team to transfer this website to." + "value": "Wähle ein Team aus, zu dem die Webseite transferiert werden soll." } ], "message.transfer-website": [ { "type": 0, - "value": "Transfer website ownership to your account or another team." + "value": "Übertrage die Eigentümerrechte zu deinem Account oder einem anderen Team." } ], "message.triggered-event": [ { "type": 0, - "value": "Triggered event" + "value": "Ausgelöstes Event" } ], "message.user-deleted": [ @@ -1738,7 +1810,7 @@ "message.viewed-page": [ { "type": 0, - "value": "Viewed page" + "value": "Besuchte Seite" } ], "message.visitor-log": [ @@ -1778,7 +1850,7 @@ "message.visitors-dropped-off": [ { "type": 0, - "value": "Visitors dropped off" + "value": "Besucherverlust" } ] } diff --git a/public/intl/messages/el-GR.json b/public/intl/messages/el-GR.json index f44bd026a0..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 efb8f1be4d..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 bf911728a8..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 40ddded568..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" @@ -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": [ @@ -473,10 +473,16 @@ "value": "Filtros" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, - "value": "Funnel" + "value": "Embudo" } ], "label.funnel-description": [ @@ -488,19 +494,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 +584,13 @@ "label.journey": [ { "type": 0, - "value": "Journey" + "value": "Viaje" } ], "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Comprenda cómo los usuarios navegan por su sitio web." } ], "label.language": [ @@ -636,7 +642,7 @@ "label.last-months": [ { "type": 0, - "value": "Last " + "value": "Últimos " }, { "type": 1, @@ -644,7 +650,13 @@ }, { "type": 0, - "value": " months" + "value": " meses" + } + ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" } ], "label.leave": [ @@ -698,7 +710,7 @@ "label.max": [ { "type": 0, - "value": "Máx" + "value": "Max" } ], "label.member": [ @@ -716,7 +728,7 @@ "label.min": [ { "type": 0, - "value": "Mín" + "value": "Min" } ], "label.mobile": [ @@ -861,6 +873,18 @@ "value": "Contraseña" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -874,19 +898,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": [ @@ -895,10 +919,16 @@ "value": "Perfil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, - "value": "Property" + "value": "Propiedad" } ], "label.queries": [ @@ -952,7 +982,7 @@ "label.region": [ { "type": 0, - "value": "Region" + "value": "Región" } ], "label.regions": [ @@ -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, @@ -1042,7 +1090,7 @@ "label.select": [ { "type": 0, - "value": "Select" + "value": "Seleccionar" } ], "label.select-date": [ @@ -1063,6 +1111,12 @@ "value": "Seleccionar sitio web" } ], + "label.session": [ + { + "type": 0, + "value": "Session" + } + ], "label.sessions": [ { "type": 0, @@ -1090,13 +1144,13 @@ "label.start-step": [ { "type": 0, - "value": "Start Step" + "value": "Paso inical" } ], "label.steps": [ { "type": 0, - "value": "Steps" + "value": "Pasos" } ], "label.sum": [ @@ -1126,7 +1180,7 @@ "label.team-manager": [ { "type": 0, - "value": "Team manager" + "value": "Jefe de equipo" } ], "label.team-member": [ @@ -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, @@ -1282,7 +1348,7 @@ "label.update": [ { "type": 0, - "value": "Update" + "value": "Actualizar" } ], "label.url": [ @@ -1303,6 +1369,12 @@ "value": "Usuario" } ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" + } + ], "label.username": [ { "type": 0, @@ -1324,7 +1396,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 +1432,7 @@ "label.views-per-visit": [ { "type": 0, - "value": "Views per visit" + "value": "Vistas por visita" } ], "label.visit-duration": [ @@ -1378,7 +1450,7 @@ "label.visits": [ { "type": 0, - "value": "Visits" + "value": "Visitas" } ], "label.website": [ @@ -1462,7 +1534,7 @@ "message.collected-data": [ { "type": 0, - "value": "Collected data" + "value": "Datos obtenidos" } ], "message.confirm-delete": [ @@ -1726,7 +1798,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/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 cb10df4b1e..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 e43a185500..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 1b2d4f0721..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 44fd708bee..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 690083ae05..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 740dfffaa5..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 2c178669b5..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 974bdebd71..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 227ef208a0..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 dbe78a5018..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 21b70650dc..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 c11311c455..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 d8f7d50123..ca8a40a934 100644 --- a/public/intl/messages/km-KH.json +++ b/public/intl/messages/km-KH.json @@ -11,34 +11,34 @@ "value": "សកម្មភាព" } ], - "label.activity-log": [ + "label.activity": [ { "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,13 @@ "label.filters": [ { "type": 0, - "value": "Filters" + "value": "ចម្រោះ" + } + ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" } ], "label.funnel": [ @@ -488,31 +494,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 +548,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 nagivate through your website." + "value": "ស្វែងយល់ពីការប្រើប្រាស់គេហទំព័ររបស់អតិថិជនអ្នក។" } ], "label.language": [ @@ -612,7 +618,7 @@ }, { "type": 0, - "value": " ថ្ងៃមុន" + "value": " ថ្ងៃចុងក្រោយ" } ], "label.last-hours": [ @@ -622,45 +628,47 @@ }, { "type": 0, - "value": " ម៉ោងមុន" + "value": " ម៉ោងចុងក្រោយ" } ], "label.last-months": [ - { - "type": 0, - "value": "Last " - }, { "type": 1, "value": "x" }, { "type": 0, - "value": " months" + "value": " ខែចុងក្រោយ" + } + ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" } ], "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 +680,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 +704,13 @@ "label.member": [ { "type": 0, - "value": "Member" + "value": "សមាជិក" } ], "label.members": [ { "type": 0, - "value": "Members" + "value": "សមាជិក" } ], "label.min": [ @@ -726,13 +734,13 @@ "label.my-account": [ { "type": 0, - "value": "My account" + "value": "គណនី​របស់ខ្ញុំ" } ], "label.my-websites": [ { "type": 0, - "value": "My websites" + "value": "គេហទំព័ររបស់ខ្ញុំ" } ], "label.name": [ @@ -802,7 +810,7 @@ "label.overview": [ { "type": 0, - "value": "Overview" + "value": "ទិដ្ឋភាពរួម" } ], "label.owner": [ @@ -814,7 +822,7 @@ "label.page-of": [ { "type": 0, - "value": "Page " + "value": "ទំព័រទី " }, { "type": 1, @@ -822,7 +830,7 @@ }, { "type": 0, - "value": " of " + "value": " នៃ " }, { "type": 1, @@ -838,7 +846,7 @@ "label.pageTitle": [ { "type": 0, - "value": "Page title" + "value": "ចំណងជើងទំព័រ" } ], "label.pages": [ @@ -853,6 +861,18 @@ "value": "ពាក្យសម្ងាត់​" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -866,25 +886,31 @@ "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.properties": [ + { + "type": 0, + "value": "Properties" } ], "label.property": [ @@ -920,7 +946,7 @@ "label.referrer": [ { "type": 0, - "value": "Referrer" + "value": "អ្នកណែនាំ" } ], "label.referrers": [ @@ -944,31 +970,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 +1006,7 @@ "label.reset": [ { "type": 0, - "value": "កំណត់ឡើងវិញ" + "value": "កែសម្រួល" } ], "label.reset-website": [ @@ -992,7 +1018,7 @@ "label.retention": [ { "type": 0, - "value": "Retention" + "value": "ការរក្សាទុក" } ], "label.retention-description": [ @@ -1001,10 +1027,28 @@ "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, - "value": "Role" + "value": "មុខងារ" } ], "label.run-query": [ @@ -1028,31 +1072,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.session": [ + { + "type": 0, + "value": "Session" } ], "label.sessions": [ @@ -1082,13 +1132,13 @@ "label.start-step": [ { "type": 0, - "value": "Start Step" + "value": "ជំហានចាប់ផ្តើម" } ], "label.steps": [ { "type": 0, - "value": "Steps" + "value": "ជំហាន" } ], "label.sum": [ @@ -1106,37 +1156,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 +1198,13 @@ "label.team-websites": [ { "type": 0, - "value": "Team websites" + "value": "គេហទំព័ររបស់ក្រុម" } ], "label.teams": [ { "type": 0, - "value": "Teams" + "value": "ក្រុម" } ], "label.theme": [ @@ -1190,7 +1240,7 @@ "label.title": [ { "type": 0, - "value": "Title" + "value": "ចំណងជើង" } ], "label.today": [ @@ -1208,13 +1258,13 @@ "label.total": [ { "type": 0, - "value": "Total" + "value": "សរុប" } ], "label.total-records": [ { "type": 0, - "value": "Total records" + "value": "កំណត់ត្រាសរុប" } ], "label.tracking-code": [ @@ -1223,22 +1273,28 @@ "value": "លេខកូដតាមដាន" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "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": [ @@ -1259,6 +1315,12 @@ "value": "អ្នកចូលមើលម្នាក់ៗ" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1268,7 +1330,7 @@ "label.untitled": [ { "type": 0, - "value": "Untitled" + "value": "គ្មានចំណងជើង" } ], "label.update": [ @@ -1292,7 +1354,13 @@ "label.user": [ { "type": 0, - "value": "User" + "value": "អ្នកប្រើប្រាស់" + } + ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" } ], "label.username": [ @@ -1304,7 +1372,7 @@ "label.users": [ { "type": 0, - "value": "Users" + "value": "អ្នកប្រើប្រាស់" } ], "label.utm": [ @@ -1316,7 +1384,7 @@ "label.utm-description": [ { "type": 0, - "value": "Track your campaigns through UTM parameters." + "value": "តាមដានយុទ្ធនាការរបស់អ្នកតាមរយៈប៉ារ៉ាម៉ែត្រ UTM។" } ], "label.value": [ @@ -1340,7 +1408,7 @@ "label.view-only": [ { "type": 0, - "value": "View only" + "value": "បានតែមើលប៉ុណ្ណោះ" } ], "label.views": [ @@ -1358,7 +1426,7 @@ "label.visit-duration": [ { "type": 0, - "value": "មើលជាមធ្យម" + "value": "រយៈពេលទស្សនា" } ], "label.visitors": [ @@ -1370,19 +1438,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 +1474,7 @@ "message.action-confirmation": [ { "type": 0, - "value": "Type " + "value": "សសេរ " }, { "type": 1, @@ -1414,7 +1482,7 @@ }, { "type": 0, - "value": " in the box below to confirm." + "value": " នៅក្នុងប្រអប់ខាងក្រោមដើម្បីបញ្ជាក់។" } ], "message.active-users": [ @@ -1434,7 +1502,7 @@ "message.collected-data": [ { "type": 0, - "value": "Collected data" + "value": "ទិន្នន័យដែលបានប្រមូលទុក" } ], "message.confirm-delete": [ @@ -1454,7 +1522,7 @@ "message.confirm-leave": [ { "type": 0, - "value": "Are you sure you want to leave " + "value": "តើអ្នកប្រាកដថាចង់ចាកចេញ " }, { "type": 1, @@ -1462,13 +1530,13 @@ }, { "type": 0, - "value": "?" + "value": " ទេ?" } ], "message.confirm-remove": [ { "type": 0, - "value": "Are you sure you want to remove " + "value": "តើអ្នកប្រាកដថាចង់លុប " }, { "type": 1, @@ -1476,7 +1544,7 @@ }, { "type": 0, - "value": "?" + "value": " ទេ?" } ], "message.confirm-reset": [ @@ -1496,13 +1564,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 +1602,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 +1622,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 +1636,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 +1696,7 @@ "message.reset-website": [ { "type": 0, - "value": "To reset this website, type " + "value": "ដើម្បីកែគេហទំព័រនេះឡើងវិញ សូមសរសេរ " }, { "type": 1, @@ -1636,7 +1704,7 @@ }, { "type": 0, - "value": " in the box below to confirm." + "value": " នៅក្នុងប្រអប់ខាងក្រោមដើម្បីបញ្ជាក់។" } ], "message.reset-website-warning": [ @@ -1654,7 +1722,7 @@ "message.share-url": [ { "type": 0, - "value": "នេះគឺជា URL ដែលបានចែករំលែកជាសាធារណៈសម្រាប់ " + "value": "នេះគឺជា URL ដែលអាចចែករំលែកជាសាធារណៈបានសម្រាប់ " }, { "type": 1, @@ -1668,43 +1736,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 +1798,13 @@ "message.user-deleted": [ { "type": 0, - "value": "User deleted." + "value": "អ្នកប្រើប្រាស់ត្រូវបានលុបចោល។" } ], "message.viewed-page": [ { "type": 0, - "value": "Viewed page" + "value": "ទំព័រដែលបានមើល" } ], "message.visitor-log": [ @@ -1762,7 +1844,7 @@ "message.visitors-dropped-off": [ { "type": 0, - "value": "Visitors dropped off" + "value": "ចំនួនអ្នកទស្សនាធ្លាក់ចុះ" } ] } diff --git a/public/intl/messages/ko-KR.json b/public/intl/messages/ko-KR.json index da196a3c96..9f6520ea40 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": [ @@ -11,34 +11,34 @@ "value": "액션" } ], - "label.activity-log": [ + "label.activity": [ { "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,139 @@ "label.filters": [ { "type": 0, - "value": "Filters" + "value": "필터" + } + ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" } ], "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 +622,7 @@ }, { "type": 0, - "value": " 일간" + "value": " 일" } ], "label.last-hours": [ @@ -636,7 +642,7 @@ "label.last-months": [ { "type": 0, - "value": "Last " + "value": "최근 " }, { "type": 1, @@ -644,31 +650,37 @@ }, { "type": 0, - "value": " months" + "value": " 개월" + } + ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" } ], "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 +698,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 +746,13 @@ "label.my-account": [ { "type": 0, - "value": "My account" + "value": "내 계정" } ], "label.my-websites": [ { "type": 0, - "value": "My websites" + "value": "내 웹사이트" } ], "label.name": [ @@ -758,7 +770,7 @@ "label.none": [ { "type": 0, - "value": "None" + "value": "없음" } ], "label.number-of-records": [ @@ -785,7 +797,7 @@ "value": [ { "type": 0, - "value": "records" + "value": "레코드" } ] } @@ -798,31 +810,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 +846,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": [ @@ -861,6 +873,18 @@ "value": "비밀번호" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -878,19 +902,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": [ @@ -899,28 +923,34 @@ "value": "프로필" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "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 +962,7 @@ "label.referrer": [ { "type": 0, - "value": "Referrer" + "value": "리퍼러" } ], "label.referrers": [ @@ -950,37 +980,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 +1028,49 @@ "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.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, - "value": "Role" + "value": "역할" } ], "label.run-query": [ { "type": 0, - "value": "Run query" + "value": "쿼리 실행" } ], "label.save": [ @@ -1034,43 +1082,49 @@ "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.session": [ + { + "type": 0, + "value": "Session" } ], "label.sessions": [ { "type": 0, - "value": "Sessions" + "value": "세션" } ], "label.settings": [ @@ -1094,19 +1148,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 +1172,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 +1256,7 @@ "label.title": [ { "type": 0, - "value": "Title" + "value": "제목" } ], "label.today": [ @@ -1214,19 +1268,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": [ @@ -1235,34 +1289,40 @@ "value": "추적 코드" } ], + "label.transactions": [ + { + "type": 0, + "value": "Transactions" + } + ], "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": [ @@ -1271,6 +1331,12 @@ "value": "순방문자(UV)" } ], + "label.uniqueCustomers": [ + { + "type": 0, + "value": "Unique Customers" + } + ], "label.unknown": [ { "type": 0, @@ -1280,13 +1346,13 @@ "label.untitled": [ { "type": 0, - "value": "Untitled" + "value": "제목 없음" } ], "label.update": [ { "type": 0, - "value": "Update" + "value": "업데이트" } ], "label.url": [ @@ -1298,13 +1364,19 @@ "label.urls": [ { "type": 0, - "value": "URLs" + "value": "URL" } ], "label.user": [ { "type": 0, - "value": "User" + "value": "사용자" + } + ], + "label.user-property": [ + { + "type": 0, + "value": "User Property" } ], "label.username": [ @@ -1316,7 +1388,7 @@ "label.users": [ { "type": 0, - "value": "Users" + "value": "사용자" } ], "label.utm": [ @@ -1328,19 +1400,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 +1424,7 @@ "label.view-only": [ { "type": 0, - "value": "View only" + "value": "보기 전용" } ], "label.views": [ @@ -1364,7 +1436,7 @@ "label.views-per-visit": [ { "type": 0, - "value": "Views per visit" + "value": "방문당 조회수" } ], "label.visit-duration": [ @@ -1382,19 +1454,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 +1478,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 +1498,7 @@ }, { "type": 0, - "value": " in the box below to confirm." + "value": "을(를) 입력하십시오." } ], "message.active-users": [ @@ -1442,7 +1514,7 @@ "message.collected-data": [ { "type": 0, - "value": "Collected data" + "value": "수집된 데이터" } ], "message.confirm-delete": [ @@ -1456,57 +1528,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 +1582,7 @@ }, { "type": 0, - "value": " on " + "value": " - " }, { "type": 1, @@ -1538,7 +1598,7 @@ "message.incorrect-username-password": [ { "type": 0, - "value": "사용자 이름/비밀번호가 잘못되었습니다.." + "value": "사용자 이름/비밀번호가 잘못되었습니다." } ], "message.invalid-domain": [ @@ -1550,7 +1610,7 @@ "message.min-password-length": [ { "type": 0, - "value": "Minimum length of " + "value": "최소 길이는 " }, { "type": 1, @@ -1558,21 +1618,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 +1640,7 @@ "message.no-event-data": [ { "type": 0, - "value": "No event data is available." + "value": "사용 가능한 이벤트 데이터가 없습니다." } ], "message.no-match-password": [ @@ -1596,31 +1652,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 +1688,7 @@ "message.reset-website": [ { "type": 0, - "value": "To reset this website, type " + "value": "이 웹사이트를 초기화하려면, 아래 상자에 " }, { "type": 1, @@ -1640,13 +1696,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 +1714,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/lt-LT.json b/public/intl/messages/lt-LT.json index 891aed6ffa..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 b11d88c12e..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 996af333db..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 34f83c029f..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 eeb648c482..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 16f2de07f9..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 29e99d4fe6..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" @@ -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": [ @@ -473,6 +473,12 @@ "value": "Filtry" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -488,13 +494,13 @@ "label.goal": [ { "type": 0, - "value": "Goal" + "value": "Cel" } ], "label.goals": [ { "type": 0, - "value": "Goals" + "value": "Cele" } ], "label.goals-description": [ @@ -524,7 +530,7 @@ "label.hosts": [ { "type": 0, - "value": "Hosts" + "value": "Hosty" } ], "label.insights": [ @@ -578,13 +584,13 @@ "label.journey": [ { "type": 0, - "value": "Journey" + "value": "Droga" } ], "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Zrozum, w jaki sposób użytkownicy poruszają się po Twojej witrynie." } ], "label.language": [ @@ -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, @@ -874,19 +898,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": [ @@ -895,6 +919,12 @@ "value": "Profil" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, @@ -928,7 +958,7 @@ "label.referrer": [ { "type": 0, - "value": "Referrer" + "value": "Źródło odsyłające" } ], "label.referrers": [ @@ -970,7 +1000,7 @@ "label.remove-member": [ { "type": 0, - "value": "Remove member" + "value": "Usuń członka" } ], "label.reports": [ @@ -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, @@ -1090,7 +1144,7 @@ "label.start-step": [ { "type": 0, - "value": "Start Step" + "value": "Krok startowy" } ], "label.steps": [ @@ -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, @@ -1378,7 +1450,7 @@ "label.visits": [ { "type": 0, - "value": "Odwiedząjący" + "value": "Wizyty" } ], "label.website": [ @@ -1462,7 +1534,7 @@ "message.collected-data": [ { "type": 0, - "value": "Collected data" + "value": "Zebrane dane" } ], "message.confirm-delete": [ diff --git a/public/intl/messages/pt-BR.json b/public/intl/messages/pt-BR.json index 149af775da..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 340ebb8852..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 67d0929bcf..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 1d0a86a1e7..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 d7cfd7bf43..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 2be1f01521..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 83c383c1aa..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 a6336a3044..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 4afcb8db74..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 32d5e677d2..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 ac49dd47d5..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 6ea471a6b3..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, @@ -566,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 18fce7ebb7..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 50409fab26..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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 e66a8eb158..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": "活动日志" @@ -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": [ @@ -473,6 +473,12 @@ "value": "筛选" } ], + "label.first-seen": [ + { + "type": 0, + "value": "First seen" + } + ], "label.funnel": [ { "type": 0, @@ -488,19 +494,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 +524,13 @@ "label.host": [ { "type": 0, - "value": "Host" + "value": "主机" } ], "label.hosts": [ { "type": 0, - "value": "Hosts" + "value": "主机" } ], "label.insights": [ @@ -578,13 +584,13 @@ "label.journey": [ { "type": 0, - "value": "Journey" + "value": "用户浏览轨迹" } ], "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "了解用户如何浏览网站。" } ], "label.language": [ @@ -647,6 +653,12 @@ "value": " 个月" } ], + "label.last-seen": [ + { + "type": 0, + "value": "Last seen" + } + ], "label.leave": [ { "type": 0, @@ -692,7 +704,7 @@ "label.manager": [ { "type": 0, - "value": "Manager" + "value": "管理者" } ], "label.max": [ @@ -798,7 +810,7 @@ "label.ok": [ { "type": 0, - "value": "OK" + "value": "好的" } ], "label.os": [ @@ -865,6 +877,18 @@ "value": "密码" } ], + "label.path": [ + { + "type": 0, + "value": "Path" + } + ], + "label.paths": [ + { + "type": 0, + "value": "Paths" + } + ], "label.powered-by": [ { "type": 0, @@ -882,19 +906,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": [ @@ -903,10 +927,16 @@ "value": "个人资料" } ], + "label.properties": [ + { + "type": 0, + "value": "Properties" + } + ], "label.property": [ { "type": 0, - "value": "Property" + "value": "属性" } ], "label.queries": [ @@ -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, @@ -1098,7 +1152,7 @@ "label.start-step": [ { "type": 0, - "value": "Start Step" + "value": "开始步骤" } ], "label.steps": [ @@ -1134,7 +1188,7 @@ "label.team-manager": [ { "type": 0, - "value": "Team manager" + "value": "团队管理者" } ], "label.team-member": [ @@ -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, @@ -1450,7 +1522,7 @@ "message.collected-data": [ { "type": 0, - "value": "Collected data" + "value": "已收集的数据" } ], "message.confirm-delete": [ diff --git a/public/intl/messages/zh-TW.json b/public/intl/messages/zh-TW.json index d6514ab4df..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, @@ -584,7 +590,7 @@ "label.journey-description": [ { "type": 0, - "value": "Understand how users nagivate through your website." + "value": "Understand how users navigate through your website." } ], "label.language": [ @@ -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/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); } } 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/scripts/start-env.js b/scripts/start-env.js index e9fe2a4b4e..264c4e92bc 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', }); 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/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/(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)/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/[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/src/app/(main)/reports/create/ReportTemplates.tsx b/src/app/(main)/reports/create/ReportTemplates.tsx index e37efc0367..fcfa6f6a92 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)/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)/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; 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/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/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/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/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/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/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 }) => ( + {showActions && ( 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)} > - - 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/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]/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/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]/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]/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 { diff --git a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx index 0cbaeb44d7..edd10b99b0 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({ @@ -31,25 +32,30 @@ 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.realtime), icon: , path: '/realtime', }, + { + label: formatMessage(labels.compare), + icon: , + path: '/compare', + }, { label: formatMessage(labels.reports), icon: , path: '/reports', }, - { - label: formatMessage(labels.eventData), - icon: , - path: '/event-data', - }, ]; return ( @@ -64,7 +70,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]/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]/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/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]/event-data/EventDataMetricsBar.module.css b/src/app/(main)/websites/[websiteId]/event-data/EventDataMetricsBar.module.css deleted file mode 100644 index 408396c37b..0000000000 --- a/src/app/(main)/websites/[websiteId]/event-data/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]/event-data/EventDataMetricsBar.tsx b/src/app/(main)/websites/[websiteId]/event-data/EventDataMetricsBar.tsx deleted file mode 100644 index 86417c96d3..0000000000 --- a/src/app/(main)/websites/[websiteId]/event-data/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]/event-data/EventDataPage.tsx b/src/app/(main)/websites/[websiteId]/event-data/EventDataPage.tsx deleted file mode 100644 index 77d367e58f..0000000000 --- a/src/app/(main)/websites/[websiteId]/event-data/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]/event-data/EventDataTable.tsx b/src/app/(main)/websites/[websiteId]/event-data/EventDataTable.tsx deleted file mode 100644 index 71c3699222..0000000000 --- a/src/app/(main)/websites/[websiteId]/event-data/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]/event-data/EventDataValueTable.tsx b/src/app/(main)/websites/[websiteId]/event-data/EventDataValueTable.tsx deleted file mode 100644 index cddeea23fd..0000000000 --- a/src/app/(main)/websites/[websiteId]/event-data/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]/event-data/WebsiteEventData.module.css b/src/app/(main)/websites/[websiteId]/event-data/WebsiteEventData.module.css deleted file mode 100644 index 1d1782312d..0000000000 --- a/src/app/(main)/websites/[websiteId]/event-data/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]/event-data/WebsiteEventData.tsx b/src/app/(main)/websites/[websiteId]/event-data/WebsiteEventData.tsx deleted file mode 100644 index d7d24ceed1..0000000000 --- a/src/app/(main)/websites/[websiteId]/event-data/WebsiteEventData.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { Flexbox, Loading } from 'react-basics'; -import EventDataTable from './EventDataTable'; -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]/events/EventProperties.module.css b/src/app/(main)/websites/[websiteId]/events/EventProperties.module.css new file mode 100644 index 0000000000..0b9c011d18 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/events/EventProperties.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: flex-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]/events/EventProperties.tsx b/src/app/(main)/websites/[websiteId]/events/EventProperties.tsx new file mode 100644 index 0000000000..794a5053d5 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/events/EventProperties.tsx @@ -0,0 +1,65 @@ +import { GridColumn, GridTable } from 'react-basics'; +import { useEventDataProperties, useEventDataValues, 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 './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, eventName, propertyName); + const chartData = + propertyName && values + ? { + labels: values.map(({ value }) => value), + datasets: [ + { + data: values.map(({ total }) => total), + backgroundColor: CHART_COLORS, + borderWidth: 0, + }, + ], + } + : null; + + const handleRowClick = row => { + setEventName(row.eventName); + setPropertyName(row.propertyName); + }; + + return ( + +
+ + + {row => ( +
handleRowClick(row)}> + {row.eventName} +
+ )} +
+ + {row => ( +
handleRowClick(row)}> + {row.propertyName} +
+ )} +
+ +
+ {propertyName && ( +
+
{propertyName}
+ +
+ )} +
+
+ ); +} + +export default EventProperties; 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..49b31656d3 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx @@ -0,0 +1,20 @@ +import { useWebsiteEvents } from 'components/hooks'; +import EventsTable from './EventsTable'; +import DataTable from 'components/common/DataTable'; +import { ReactNode } from 'react'; + +export default function EventsDataTable({ + websiteId, +}: { + websiteId?: string; + teamId?: string; + children?: ReactNode; +}) { + const queryResult = useWebsiteEvents(websiteId); + + 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..d039b67f71 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx @@ -0,0 +1,42 @@ +import { useMessages } from 'components/hooks'; +import useWebsiteSessionStats from 'components/hooks/queries/useWebsiteSessionStats'; +import WebsiteDateFilter from 'components/input/WebsiteDateFilter'; +import MetricCard from 'components/metrics/MetricCard'; +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 } = useWebsiteSessionStats(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 new file mode 100644 index 0000000000..7dfc0394c6 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx @@ -0,0 +1,44 @@ +'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'; +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 ( + <> + + + + + + +
+ setTab(value)} + style={{ marginBottom: 30 }} + > + {formatMessage(labels.activity)} + {formatMessage(labels.properties)} + + {tab === 'activity' && } + {tab === 'properties' && } +
+ + ); +} 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..42eb8f7ad0 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx @@ -0,0 +1,44 @@ +import { GridTable, GridColumn, Icon } from 'react-basics'; +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'; + +export function EventsTable({ data = [] }) { + const { formatTimezoneDate } = useTimezone(); + const { formatMessage, labels } = useMessages(); + const { renderTeamUrl } = useTeamUrl(); + + if (data.length === 0) { + return ; + } + + return ( + + + {row => ( + + + + )} + + + {row => { + return ( + <> + {row.eventName ? : } + {formatMessage(row.eventName ? labels.triggeredEvent : labels.viewedPage)} + {row.eventName || row.urlPath} + + ); + }} + + + {row => formatTimezoneDate(row.createdAt, 'PPPpp')} + + + ); +} + +export default EventsTable; diff --git a/src/app/(main)/websites/[websiteId]/event-data/page.tsx b/src/app/(main)/websites/[websiteId]/events/page.tsx similarity index 62% rename from src/app/(main)/websites/[websiteId]/event-data/page.tsx rename to src/app/(main)/websites/[websiteId]/events/page.tsx index 24cf4feaab..b5dc4d62ca 100644 --- a/src/app/(main)/websites/[websiteId]/event-data/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/app/(main)/websites/[websiteId]/layout.tsx b/src/app/(main)/websites/[websiteId]/layout.tsx new file mode 100644 index 0000000000..f8756ea3e7 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/layout.tsx @@ -0,0 +1,13 @@ +import { Metadata } from 'next'; +import WebsiteProvider from './WebsiteProvider'; + +export default function ({ children, params: { websiteId } }) { + 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 cbdeb1ac43..8a5f3a55f5 100644 --- a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx +++ b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx @@ -1,17 +1,15 @@ -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 { WebsiteContext } from '../WebsiteProvider'; import styles from './RealtimeLog.module.css'; @@ -32,7 +30,8 @@ export function RealtimeLog({ data }: { data: RealtimeData }) { const { formatMessage, labels, messages, FormattedMessage } = useMessages(); const { formatValue } = useFormat(); const { locale } = useLocale(); - const countryNames = useCountryNames(locale); + const { formatTimezoneDate } = useTimezone(); + const { countryNames } = useCountryNames(locale); const [filter, setFilter] = useState(TYPE_ALL); const buttons = [ @@ -54,7 +53,7 @@ export function RealtimeLog({ data }: { data: RealtimeData }) { }, ]; - const getTime = ({ timestamp }) => format(timestamp * 1000, 'h:mm:ss'); + const getTime = ({ createdAt, firstAt }) => formatTimezoneDate(firstAt || createdAt, 'h:mm:ss'); const getColor = ({ id, sessionId }) => stringToColor(sessionId || id); @@ -141,12 +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('timestamp', -1)); + let logs = data.events; if (search) { logs = logs.filter(({ eventName, urlPath, browser, os, country, device }) => { @@ -178,7 +172,7 @@ export function RealtimeLog({ data }: { data: RealtimeData }) {
-
{formatMessage(labels.activityLog)}
+
{formatMessage(labels.activity)}
{logs?.length === 0 && } {logs?.length > 0 && ( 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/SessionProperties.module.css b/src/app/(main)/websites/[websiteId]/sessions/SessionProperties.module.css new file mode 100644 index 0000000000..0b9c011d18 --- /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: flex-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/SessionsDataTable.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsDataTable.tsx new file mode 100644 index 0000000000..788d0066c8 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsDataTable.tsx @@ -0,0 +1,21 @@ +import { useWebsiteSessions } 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 = useWebsiteSessions(websiteId); + + return ( + children}> + {({ data }) => } + + ); +} 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..803e7a0690 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsMetricsBar.tsx @@ -0,0 +1,42 @@ +import { useMessages } from 'components/hooks'; +import useWebsiteSessionStats from 'components/hooks/queries/useWebsiteSessionStats'; +import WebsiteDateFilter from 'components/input/WebsiteDateFilter'; +import MetricCard from 'components/metrics/MetricCard'; +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 } = useWebsiteSessionStats(websiteId); + + return ( + + + + + + + + + + ); +} + +export default SessionsMetricsBar; 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..30fd193dbf --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx @@ -0,0 +1,35 @@ +'use client'; +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'; +import SessionsWeekly from './SessionsWeekly'; + +export function SessionsPage({ websiteId }) { + const [tab, setTab] = useState('activity'); + const { formatMessage, labels } = useMessages(); + + return ( + <> + + + + + + + setTab(value)} style={{ marginBottom: 30 }}> + {formatMessage(labels.activity)} + {formatMessage(labels.properties)} + + {tab === 'activity' && } + {tab === 'properties' && } + + ); +} + +export default SessionsPage; 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 new file mode 100644 index 0000000000..3fea4836c0 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx @@ -0,0 +1,60 @@ +import Link from 'next/link'; +import { GridColumn, GridTable } from 'react-basics'; +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'; + +export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean }) { + const { formatTimezoneDate } = useTimezone(); + const { formatMessage, labels } = useMessages(); + const { formatValue } = useFormat(); + + return ( + + + {row => ( + + + + )} + + + + + {row => ( + + {formatValue(row.country, 'country')} + + )} + + + + {row => ( + + {formatValue(row.browser, 'browser')} + + )} + + + {row => ( + + {formatValue(row.os, 'os')} + + )} + + + {row => ( + + {formatValue(row.device, 'device')} + + )} + + + {row => formatTimezoneDate(row.createdAt, 'PPPpp')} + + + ); +} + +export default SessionsTable; 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..3536164353 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.module.css @@ -0,0 +1,43 @@ +.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; + align-items: flex-start; + justify-content: flex-start; + gap: 1px; + position: relative; +} + +.cell { + display: flex; + background-color: var(--base75); + width: 20px; + height: 20px; + margin: auto; + border-radius: 100%; + align-items: flex-start; +} + +.hour { + font-weight: 700; + color: var(--font-color300); + height: 20px; +} + +.block { + background-color: var(--primary400); + width: 20px; + height: 20px; + border-radius: 100%; +} 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..3e15ddfaeb --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsWeekly.tsx @@ -0,0 +1,83 @@ +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'; +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) => { + const label = format(addHours(startOfDay(new Date()), i), 'haaa'); + return ( +
+ {label} +
+ ); + })} +
+ {data?.map((day: number[], index: number) => { + return ( +
+
+ {format(getDayOfWeekAsDate(index), 'EEE', { locale: dateLocale })} +
+ {day?.map((hour: number) => { + const pct = hour / max; + return ( +
+ {hour > 0 && ( + +
+ + )} +
+ ); + })} +
+ ); + })} +
+ + ); +} + +export default SessionsWeekly; 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..b49230c7b8 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.module.css @@ -0,0 +1,24 @@ +.timeline { + display: flex; + flex-direction: column; + gap: 20px; +} + +.row { + display: flex; + align-items: center; + gap: 20px; +} + +.time { + color: var(--font-color200); + width: 150px; +} + +.value { + white-space: nowrap; +} + +.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..fd498ac3ab --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionActivity.tsx @@ -0,0 +1,52 @@ +import { isSameDay } from 'date-fns'; +import { Loading, Icon, StatusLight } from 'react-basics'; +import Icons from 'components/icons'; +import { useSessionActivity, useTimezone } from 'components/hooks'; +import styles from './SessionActivity.module.css'; + +export function SessionActivity({ + websiteId, + sessionId, + startDate, + endDate, +}: { + websiteId: string; + sessionId: string; + startDate: Date; + endDate: Date; +}) { + const { formatTimezoneDate } = useTimezone(); + const { data, isLoading } = useSessionActivity(websiteId, sessionId, startDate, endDate); + + if (isLoading) { + return ; + } + + let lastDay = null; + + return ( +
+ {data.map(({ eventId, createdAt, urlPath, eventName, visitId }) => { + const showHeader = !lastDay || !isSameDay(new Date(lastDay), new Date(createdAt)); + lastDay = createdAt; + + return ( + <> + {showHeader && ( +
{formatTimezoneDate(createdAt, 'EEEE, PPP')}
+ )} +
+
+ + {formatTimezoneDate(createdAt, 'h:mm:ss aaa')} + +
+ {eventName ? : } +
{eventName || urlPath}
+
+ + ); + })} +
+ ); +} 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..4794803d22 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.module.css @@ -0,0 +1,38 @@ +.data { + display: flex; + flex-direction: column; + gap: 20px; + position: relative; +} + +.header { + font-weight: bold; + margin-bottom: 20px; +} + +.empty { + color: var(--font-color300); + text-align: center; +} + +.label { + display: flex; + align-items: center; + justify-content: space-between; +} + +.type { + font-size: 11px; + padding: 0 6px; + border-radius: 4px; + border: 1px solid var(--base400); +} + +.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..39b6afd1f5 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionData.tsx @@ -0,0 +1,33 @@ +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, ...query } = useSessionData(websiteId, sessionId); + + 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 new file mode 100644 index 0000000000..7058cd7939 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.module.css @@ -0,0 +1,47 @@ +.page { + display: grid; + grid-template-columns: max-content 1fr max-content; + margin-bottom: 40px; + position: relative; +} + +.sidebar { + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; + gap: 20px; + width: 300px; + padding-right: 20px; + border-right: 1px solid var(--base300); + position: relative; +} + +.content { + display: flex; + flex-direction: column; + gap: 30px; + padding: 0 20px; + position: relative; +} + +.data { + width: 300px; + 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; + width: auto; + } +} 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..d6a07edcf4 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionDetailsPage.tsx @@ -0,0 +1,44 @@ +'use client'; +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 { SessionData } from './SessionData'; +import styles from './SessionDetailsPage.module.css'; +import SessionInfo from './SessionInfo'; +import { SessionStats } from './SessionStats'; + +export default function SessionDetailsPage({ + websiteId, + sessionId, +}: { + websiteId: string; + sessionId: string; +}) { + const { data, ...query } = useWebsiteSession(websiteId, sessionId); + + return ( + + +
+
+ + +
+
+ + +
+
+ +
+
+
+ ); +} 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..de6e796f7b --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.module.css @@ -0,0 +1,21 @@ +.info { + display: grid; + gap: 10px; +} + +.info dl { + width: 100%; +} + +.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 new file mode 100644 index 0000000000..6f9a8f3d57 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionInfo.tsx @@ -0,0 +1,70 @@ +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'; +import styles from './SessionInfo.module.css'; + +export default function SessionInfo({ data }) { + const { locale } = useLocale(); + const { formatTimezoneDate } = useTimezone(); + const { formatMessage, labels } = useMessages(); + const { formatValue } = useFormat(); + const { getRegionName } = useRegionNames(locale); + + return ( +
+
+
ID
+
+ {data?.id} +
+ +
{formatMessage(labels.lastSeen)}
+
{formatTimezoneDate(data?.lastAt, 'EEEE, PPPpp')}
+ +
{formatMessage(labels.firstSeen)}
+
{formatTimezoneDate(data?.firstAt, 'EEEE, 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/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx new file mode 100644 index 0000000000..ea606582f0 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/SessionStats.tsx @@ -0,0 +1,21 @@ +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(); + + return ( + + + + + `${+n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`} + /> + + ); +} 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/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/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/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/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 }) {
- + + +
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/assets/location.svg b/src/assets/location.svg new file mode 100644 index 0000000000..f7f085e416 --- /dev/null +++ b/src/assets/location.svg @@ -0,0 +1 @@ + \ 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/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/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/charts/Chart.tsx b/src/components/charts/Chart.tsx index 6ba601597f..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 === 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/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/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/DataTable.tsx b/src/components/common/DataTable.tsx index ed910def7d..22373f608e 100644 --- a/src/components/common/DataTable.tsx +++ b/src/components/common/DataTable.tsx @@ -1,19 +1,21 @@ import { ReactNode } from 'react'; import classNames from 'classnames'; -import { Banner, Loading, SearchField } from 'react-basics'; -import { useMessages } from 'components/hooks'; +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 { FilterQueryResult } from 'lib/types'; +import { PagedQueryResult } from 'lib/types'; import styles from './DataTable.module.css'; +import { LoadingPanel } from 'components/common/LoadingPanel'; const DEFAULT_SEARCH_DELAY = 600; export interface DataTableProps { - queryResult: FilterQueryResult; + queryResult: PagedQueryResult; 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,12 +32,13 @@ export function DataTable({ result, params, setParams, - query: { error, isLoading }, + query: { error, isLoading, isFetched }, } = queryResult || {}; 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) => { setParams({ ...params, query, page: params.page ? page : 1 }); @@ -42,12 +46,9 @@ export function DataTable({ const handlePageChange = (page: number) => { setParams({ ...params, query, page }); + router.push(renderUrl({ page })); }; - if (error) { - return {formatMessage(messages.error)}; - } - return ( <> {allowSearch && (hasData || query) && ( @@ -60,23 +61,27 @@ export function DataTable({ placeholder={formatMessage(labels.search)} /> )} -
- {hasData ? (typeof children === 'function' ? children(result) : children) : null} - {isLoading && } - {!isLoading && !hasData && !query && } - {noResults && } -
- {allowPaging && hasData && ( - - )} + +
+ {hasData ? (typeof children === 'function' ? children(result) : children) : null} + {isLoading && } + {!isLoading && !hasData && !query && (renderEmpty ? renderEmpty() : )} + {!isLoading && noResults && } +
+ {allowPaging && hasData && ( + + )} +
); } diff --git a/src/components/common/LoadingPanel.module.css b/src/components/common/LoadingPanel.module.css new file mode 100644 index 0000000000..00d6cbb4f4 --- /dev/null +++ b/src/components/common/LoadingPanel.module.css @@ -0,0 +1,16 @@ +.panel { + display: flex; + 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 new file mode 100644 index 0000000000..36de93651a --- /dev/null +++ b/src/components/common/LoadingPanel.tsx @@ -0,0 +1,36 @@ +import { ReactNode } from 'react'; +import classNames from 'classnames'; +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, + 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 = !isLoading && isFetched && data && Array.isArray(data) && data.length === 0; + + return ( +
+ {isLoading && !isFetched && } + {error && } + {!error && isEmpty && } + {!error && !isEmpty && data && children} +
+ ); +} 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; } } diff --git a/src/components/common/TypeIcon.tsx b/src/components/common/TypeIcon.tsx new file mode 100644 index 0000000000..d617e75925 --- /dev/null +++ b/src/components/common/TypeIcon.tsx @@ -0,0 +1,27 @@ +import { ReactNode } from 'react'; + +export function TypeIcon({ + type, + value, + children, +}: { + type: 'browser' | 'country' | 'device' | 'os'; + value: string; + children?: ReactNode; +}) { + return ( + <> + {value} + {children} + + ); +} + +export default TypeIcon; diff --git a/src/components/hooks/index.ts b/src/components/hooks/index.ts index df4fbd88fa..1be99732e0 100644 --- a/src/components/hooks/index.ts +++ b/src/components/hooks/index.ts @@ -1,10 +1,20 @@ export * from './queries/useApi'; export * from './queries/useConfig'; -export * from './queries/useFilterQuery'; +export * from './queries/useEventDataEvents'; +export * from './queries/useEventDataProperties'; +export * from './queries/useEventDataValues'; +export * from './queries/usePagedQuery'; export * from './queries/useLogin'; export * from './queries/useRealtime'; 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/useWebsiteSessionsWeekly'; export * from './queries/useShareToken'; export * from './queries/useTeam'; export * from './queries/useTeams'; @@ -15,6 +25,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'; @@ -30,6 +41,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/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..61aea58e05 --- /dev/null +++ b/src/components/hooks/queries/useEventDataValues.ts @@ -0,0 +1,23 @@ +import useApi from './useApi'; +import { UseQueryOptions } from '@tanstack/react-query'; +import { useFilterParams } from '../useFilterParams'; + +export function useEventDataValues( + websiteId: string, + eventName: 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, eventName, propertyName }), + enabled: !!(websiteId && propertyName), + ...options, + }); +} + +export default useEventDataValues; diff --git a/src/components/hooks/queries/useFilterQuery.ts b/src/components/hooks/queries/usePagedQuery.ts similarity index 55% rename from src/components/hooks/queries/useFilterQuery.ts rename to src/components/hooks/queries/usePagedQuery.ts index 5963d09974..253f809254 100644 --- a/src/components/hooks/queries/useFilterQuery.ts +++ b/src/components/hooks/queries/usePagedQuery.ts @@ -1,16 +1,18 @@ 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 { - const [params, setParams] = useState({ +}: Omit & { queryFn: (params?: object) => any }): PagedQueryResult { + const { query: queryParams } = useNavigation(); + const [params, setParams] = useState({ query: '', - page: 1, + page: +queryParams.page || 1, }); const { useQuery } = useApi(); @@ -21,11 +23,11 @@ export function useFilterQuery({ }); return { - result: data as PageResult, + result: data as PageResult, query, params, setParams, }; } -export default useFilterQuery; +export default usePagedQuery; diff --git a/src/components/hooks/queries/useRealtime.ts b/src/components/hooks/queries/useRealtime.ts index d5186ee0da..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 'components/hooks'; 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/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/useSessionActivity.ts b/src/components/hooks/queries/useSessionActivity.ts new file mode 100644 index 0000000000..16c139ab38 --- /dev/null +++ b/src/components/hooks/queries/useSessionActivity.ts @@ -0,0 +1,21 @@ +import { useApi } from './useApi'; + +export function useSessionActivity( + websiteId: string, + sessionId: string, + startDate: Date, + endDate: Date, +) { + const { get, useQuery } = useApi(); + + return useQuery({ + queryKey: ['session:activity', { websiteId, sessionId, startDate, endDate }], + queryFn: () => { + 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/components/hooks/queries/useSessionData.ts b/src/components/hooks/queries/useSessionData.ts new file mode 100644 index 0000000000..14e046d1d2 --- /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(`/websites/${websiteId}/sessions/${sessionId}/properties`, { websiteId }); + }, + }); +} 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..ce9a67f3ec --- /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:session-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/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 588d4fb568..5d9bd19660 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 { usePagedQuery } from './usePagedQuery'; export function useWebsiteEvents( websiteId: string, options?: Omit, ) { - const { get, useQuery } = useApi(); + const { get } = useApi(); const params = useFilterParams(websiteId); - return useQuery({ + return usePagedQuery({ 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/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/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/hooks/queries/useWebsiteSession.ts b/src/components/hooks/queries/useWebsiteSession.ts new file mode 100644 index 0000000000..64c7be58bf --- /dev/null +++ b/src/components/hooks/queries/useWebsiteSession.ts @@ -0,0 +1,14 @@ +import { useApi } from './useApi'; + +export function useWebsiteSession(websiteId: string, sessionId: string) { + const { get, useQuery } = useApi(); + + return useQuery({ + queryKey: ['session', { websiteId, sessionId }], + queryFn: () => { + return get(`/websites/${websiteId}/sessions/${sessionId}`); + }, + }); +} + +export default useWebsiteSession; 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/components/hooks/queries/useWebsiteSessions.ts b/src/components/hooks/queries/useWebsiteSessions.ts new file mode 100644 index 0000000000..ce65512c2f --- /dev/null +++ b/src/components/hooks/queries/useWebsiteSessions.ts @@ -0,0 +1,24 @@ +import { useApi } from './useApi'; +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 usePagedQuery({ + queryKey: ['sessions', { websiteId, modified, ...params, ...filters }], + queryFn: (data: any) => { + return get(`/websites/${websiteId}/sessions`, { + ...data, + ...params, + ...filters, + pageSize: 20, + }); + }, + }); +} + +export default useWebsiteSessions; 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/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/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/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/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/hooks/useTimezone.ts b/src/components/hooks/useTimezone.ts index 8bd76504bf..c74f513f5f 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, zonedTimeToUtc, utcToZonedTime } from 'date-fns-tz'; import useStore, { setTimezone } from 'store/app'; const selector = (state: { timezone: string }) => state.timezone; @@ -12,7 +13,25 @@ export function useTimezone() { setTimezone(value); }; - return { timezone, saveTimezone }; + 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, + ); + }; + + const toUtc = (date: Date | string | number) => { + return zonedTimeToUtc(date, timezone); + }; + + const fromUtc = (date: Date | string | number) => { + return utcToZonedTime(date, timezone); + }; + + return { timezone, saveTimezone, formatTimezoneDate, toUtc, fromUtc }; } export default useTimezone; 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/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 ( <> - {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..592ade8a0d 100644 --- a/src/components/metrics/CountriesTable.tsx +++ b/src/components/metrics/CountriesTable.tsx @@ -2,22 +2,14 @@ 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, - ...props -}: { - onDataLoad: (data: any) => void; -} & MetricsTableProps) { +export function CountriesTable({ ...props }: MetricsTableProps) { const { locale } = useLocale(); - const countryNames = useCountryNames(locale); + const { countryNames } = useCountryNames(locale); const { formatMessage, labels } = useMessages(); const { formatCountry } = useFormat(); - const handleDataLoad = (data: any) => { - onDataLoad?.(data); - }; - const renderLink = ({ x: code }) => { return ( - {code} + ); }; @@ -41,7 +30,6 @@ export function CountriesTable({ type="country" metric={formatMessage(labels.visitors)} renderLabel={renderLink} - onDataLoad={handleDataLoad} /> ); } 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/EventsChart.tsx b/src/components/metrics/EventsChart.tsx index 842ae60568..ee7f866cb3 100644 --- a/src/components/metrics/EventsChart.tsx +++ b/src/components/metrics/EventsChart.tsx @@ -1,9 +1,7 @@ import { useMemo } from 'react'; -import { Loading } from 'react-basics'; 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'; @@ -17,7 +15,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 []; @@ -32,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]); @@ -51,10 +45,6 @@ export function EventsChart({ websiteId, className }: EventsChartProps) { }; }, [data, startDate, endDate, unit]); - if (isLoading) { - return ; - } - return ( string; showLabel?: boolean; showChange?: boolean; showPrevious?: boolean; diff --git a/src/components/metrics/MetricsBar.module.css b/src/components/metrics/MetricsBar.module.css index 21c9e80238..dadee9ef76 100644 --- a/src/components/metrics/MetricsBar.module.css +++ b/src/components/metrics/MetricsBar.module.css @@ -2,6 +2,8 @@ display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, max-content)); gap: 20px; + width: 100%; + position: relative; } @media screen and (max-width: 768px) { 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/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/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/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..4a16f98f77 100644 --- a/src/components/metrics/WorldMap.tsx +++ b/src/components/metrics/WorldMap.tsx @@ -1,10 +1,10 @@ -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'; 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,36 @@ 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, + ...props +}: { + websiteId?: string; + data?: any[]; + className?: string; +} & HTMLAttributes) { const [tooltip, setTooltipPopup] = useState(); 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]); + 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,22 +52,23 @@ 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 (
... នៃ 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" + "message.visitors-dropped-off": "ចំនួនអ្នកទស្សនាធ្លាក់ចុះ" } 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 f96e643e64..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", @@ -96,13 +97,14 @@ "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", "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 47e08bca3f..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", @@ -96,13 +97,14 @@ "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": "Зөөврийн компьютер", "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 bf28b05cfe..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", @@ -96,13 +97,14 @@ "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", "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 dffbe43e48..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", @@ -96,13 +97,14 @@ "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": "လက်တော့ပ်", "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 ca360d0950..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", @@ -96,13 +97,14 @@ "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", "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 6208147309..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", @@ -96,13 +97,14 @@ "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", "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 8068d9b360..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", @@ -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", @@ -78,15 +78,16 @@ "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": "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,14 +96,15 @@ "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", "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,30 +134,36 @@ "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": "Previous", - "label.previous-period": "Previous period", - "label.previous-year": "Previous year", + "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", "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", "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,11 +173,12 @@ "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", "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", @@ -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", @@ -217,7 +229,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 +237,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}?", diff --git a/src/lang/pt-BR.json b/src/lang/pt-BR.json index c1f9c708ff..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", @@ -96,13 +97,14 @@ "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", "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 d497bb3a8f..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", @@ -96,13 +97,14 @@ "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", "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 107303ae94..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", @@ -96,13 +97,14 @@ "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", "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 535d22921d..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", @@ -96,13 +97,14 @@ "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": "Ноутбук", "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 03b42df782..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", @@ -96,13 +97,14 @@ "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", "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 3d5b8fbb13..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", @@ -96,13 +97,14 @@ "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č", "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 01fc7f30b4..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", @@ -96,13 +97,14 @@ "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", "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 92c9df03c6..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", @@ -96,13 +97,14 @@ "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", "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 99558b11d8..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", @@ -96,13 +97,14 @@ "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": "மடிக்கணினி", "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 253f420c82..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", @@ -96,13 +97,14 @@ "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": "แล็ปท็อป", "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 49e71b9a31..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", @@ -96,13 +97,14 @@ "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ü", "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 ec658b7ef6..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", @@ -93,13 +97,14 @@ "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": "Ноутбук", "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 7d649148ab..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", @@ -96,13 +97,14 @@ "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": "لیپ ٹاپ", "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 1be8684bdb..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", @@ -96,13 +97,14 @@ "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", "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 bcdd3612b6..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": "添加成员", @@ -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": "字段", @@ -78,15 +78,16 @@ "label.filter-combined": "合并", "label.filter-raw": "原始", "label.filters": "筛选", + "label.first-seen": "First seen", "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,14 +96,15 @@ "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": "笔记本", "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": "少于", @@ -110,7 +112,7 @@ "label.login": "登录", "label.logout": "退出", "label.manage": "管理", - "label.manager": "Manager", + "label.manager": "管理者", "label.max": "最大", "label.member": "成员", "label.members": "成员", @@ -123,7 +125,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": "所有者", @@ -132,12 +134,15 @@ "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.previous": "先前", + "label.previous-period": "上一时期", + "label.previous-year": "上一年", "label.profile": "个人资料", - "label.property": "Property", + "label.properties": "Properties", + "label.property": "属性", "label.queries": "查询", "label.query": "查询", "label.query-parameters": "查询参数", @@ -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,17 +173,18 @@ "label.select-date": "选择数据", "label.select-role": "选择角色", "label.select-website": "选择网站", + "label.session": "Session", "label.sessions": "会话", "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": "团队所有者", @@ -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", @@ -225,7 +237,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} ?", diff --git a/src/lang/zh-TW.json b/src/lang/zh-TW.json index 9d8329e9af..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", @@ -96,13 +97,14 @@ "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": "筆記型電腦", "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/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/clickhouse.ts b/src/lib/clickhouse.ts index b6d0fc3f36..332d07275c 100644 --- a/src/lib/clickhouse.ts +++ b/src/lib/clickhouse.ts @@ -1,14 +1,16 @@ import { ClickHouseClient, createClient } from '@clickhouse/client'; -import dateFormat from 'dateformat'; +import { formatInTimeZone } from 'date-fns-tz'; import debug from 'debug'; import { CLICKHOUSE } from 'lib/db'; -import { QueryFilters, QueryOptions } from './types'; -import { 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 = { + 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', day: '%Y-%m-%d', @@ -32,7 +34,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, @@ -47,21 +49,25 @@ function getClient() { return client; } -function getDateStringQuery(data: any, unit: string | number) { +function getUTCString(date?: Date | string | number) { + 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}')`; + } + 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}')`; } 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}}`; @@ -95,6 +101,26 @@ function getFilterQuery(filters: QueryFilters = {}, options: QueryOptions = {}) return query.join('\n'); } +function getDateQuery(filters: QueryFilters = {}) { + 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}`; + } + } + + return ''; +} + function getFilterParams(filters: QueryFilters = {}) { return filtersToArray(filters).reduce((obj, { name, value }) => { if (name && value !== undefined) { @@ -110,6 +136,7 @@ async function parseFilters(websiteId: string, filters: QueryFilters = {}, optio return { filterQuery: getFilterQuery(filters, options), + dateQuery: getDateQuery(filters), params: { ...getFilterParams(filters), websiteId, @@ -119,6 +146,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 = {}, @@ -134,9 +187,19 @@ async function rawQuery( query: query, query_params: params, format: 'JSONEachRow', + clickhouse_settings: { + date_time_output_format: 'iso', + output_format_json_quote_64bit_integers: 0, + }, }); - return resultSet.json(); + return (await 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[]) { @@ -164,12 +227,14 @@ export default { client: clickhouse, log, connect, - getDateStringQuery, - getDateQuery, - getDateFormat, + getDateStringSQL, + getDateSQL, getFilterQuery, + getUTCString, parseFilters, + pagedQuery, findUnique, findFirst, rawQuery, + insert, }; 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); +} diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 35917802d2..b3f89db261 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', @@ -160,6 +151,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 +173,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, @@ -248,7 +244,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})?)?$/; diff --git a/src/lib/date.ts b/src/lib/date.ts index de76f7f327..b731140cf2 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'; @@ -272,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]; @@ -292,7 +280,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 }); } @@ -336,3 +324,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/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/lib/kafka.ts b/src/lib/kafka.ts index da38baa44f..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,13 +55,9 @@ 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( - message: { [key: string]: string | number }, topic: string, + message: { [key: string]: string | number }, ): Promise { await connect(); @@ -77,7 +72,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({ @@ -107,7 +102,6 @@ export default { producer, log, connect, - getDateFormat, sendMessage, sendMessages, }; diff --git a/src/lib/load.ts b/src/lib/load.ts index 7812ea0db2..8cddeaa9e5 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'; @@ -18,13 +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}`, () => getSession(sessionId), 86400); + session = await redis.client.fetch( + `session:${sessionId}`, + () => getWebsiteSession(websiteId, sessionId), + 86400, + ); } else { - session = await getSession(sessionId); + session = await getWebsiteSession(websiteId, sessionId); } if (!session) { diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts index 6250f2e546..110b4750e2 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,20 +9,14 @@ import { maxDate } from './date'; import { QueryFilters, QueryOptions, PageParams } from './types'; import { filtersToArray } from './params'; -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 log = debug('umami:prisma'); -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', +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', }; function getAddIntervalQuery(field: string, interval: string): string { @@ -60,14 +55,14 @@ 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) { 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) { @@ -81,7 +76,31 @@ function getDateQuery(field: string, unit: string, timezone?: string): string { } } -function getTimestampDiffQuery(field1: string, field2: string): string { +function getDateWeeklySQL(field: string) { + const db = getDatabaseType(); + + if (db === POSTGRESQL) { + return `concat(extract(dow from ${field}), ':', to_char(${field}, 'HH24'))`; + } + + if (db === MYSQL) { + return `date_format(${field}, '%w:%H')`; + } +} + +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 +112,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 +156,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 +194,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, @@ -171,6 +205,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 = []; @@ -191,8 +230,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({ @@ -214,6 +253,32 @@ async function pagedQuery(model: string, criteria: T, filters: 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(); @@ -256,13 +321,15 @@ export default { getAddIntervalQuery, getCastColumnQuery, getDayDiffQuery, - getDateQuery, + getDateSQL, + getDateWeeklySQL, getFilterQuery, getSearchParameters, - getTimestampDiffQuery, - getSearchQuery, + getTimestampDiffSQL, + getSearchSQL, getQueryMode, pagedQuery, + pagedRawQuery, parseFilters, rawQuery, }; 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 { sortDescending?: boolean; } -export interface FilterQueryResult { +export interface PagedQueryResult { result: PageResult; query: any; params: PageParams; @@ -125,9 +125,9 @@ export interface WebsiteEventMetric { export interface WebsiteEventData { eventName?: string; - fieldName: string; + propertyName: string; dataType: number; - fieldValue?: string; + propertyValue?: string; total: number; } @@ -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 { 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/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))) { 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/send.ts b/src/pages/api/send.ts index ab565e6e70..23640de9f0 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'; @@ -122,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); @@ -145,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.'); } @@ -166,31 +163,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/src/pages/api/event-data/events.ts b/src/pages/api/websites/[websiteId]/event-data/events.ts similarity index 100% rename from src/pages/api/event-data/events.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/websites/[websiteId]/event-data/values.ts b/src/pages/api/websites/[websiteId]/event-data/values.ts new file mode 100644 index 0000000000..e5bb4ab8de --- /dev/null +++ b/src/pages/api/websites/[websiteId]/event-data/values.ts @@ -0,0 +1,57 @@ +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; + eventName?: 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(), + eventName: yup.string(), + 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, eventName, 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, + eventName, + propertyName, + }); + + return ok(res, data); + } + + return methodNotAllowed(res); +}; 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..13b31fc07a --- /dev/null +++ b/src/pages/api/websites/[websiteId]/events/index.ts @@ -0,0 +1,45 @@ +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, 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 getWebsiteEvents(websiteId, { startDate, endDate }, 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/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/websites/[websiteId]/session-data/properties.ts b/src/pages/api/websites/[websiteId]/session-data/properties.ts new file mode 100644 index 0000000000..92e182d298 --- /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 { getSessionDataProperties } from 'queries'; +import * as yup from 'yup'; + +export interface SessionDataFieldsRequestQuery { + 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 getSessionDataProperties(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]/session-data/values.ts similarity index 80% rename from src/pages/api/event-data/fields.ts rename to src/pages/api/websites/[websiteId]/session-data/values.ts index 4353ee7303..98463f1522 100644 --- a/src/pages/api/event-data/fields.ts +++ b/src/pages/api/websites/[websiteId]/session-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 { getSessionDataValues } 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 getSessionDataValues(websiteId, { startDate, endDate, propertyName }); return ok(res, data); } 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..2b0fc0847e --- /dev/null +++ b/src/pages/api/websites/[websiteId]/sessions/[sessionId]/activity.ts @@ -0,0 +1,49 @@ +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; + 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(), + }), +}; + +export default async ( + req: NextApiRequestQueryBody, + res: NextApiResponse, +) => { + await useCors(req, res); + await useAuth(req, res); + await useValidate(schema, req, res); + + const { websiteId, sessionId, 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 getSessionActivity(websiteId, sessionId, startDate, endDate); + + return ok(res, data); + } + + return methodNotAllowed(res); +}; diff --git a/src/pages/api/websites/[websiteId]/sessions/[sessionId]/index.ts b/src/pages/api/websites/[websiteId]/sessions/[sessionId]/index.ts new file mode 100644 index 0000000000..f627a20858 --- /dev/null +++ b/src/pages/api/websites/[websiteId]/sessions/[sessionId]/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 { getWebsiteSession } from 'queries'; + +export interface WesiteSessionRequestQuery 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 getWebsiteSession(websiteId, sessionId); + + return ok(res, data); + } + + return methodNotAllowed(res); +}; diff --git a/src/pages/api/websites/[websiteId]/sessions/[sessionId]/properties.ts b/src/pages/api/websites/[websiteId]/sessions/[sessionId]/properties.ts new file mode 100644 index 0000000000..c0c200648d --- /dev/null +++ b/src/pages/api/websites/[websiteId]/sessions/[sessionId]/properties.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/pages/api/websites/[websiteId]/sessions/index.ts b/src/pages/api/websites/[websiteId]/sessions/index.ts new file mode 100644 index 0000000000..1809929c19 --- /dev/null +++ b/src/pages/api/websites/[websiteId]/sessions/index.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 { getWebsiteSessions } 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 getWebsiteSessions(websiteId, { startDate, endDate }, req.query); + + return ok(res, data); + } + + return methodNotAllowed(res); +}; 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/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/pages/api/websites/[websiteId]/stats.ts b/src/pages/api/websites/[websiteId]/stats.ts index 0189627a59..9ca84c7440 100644 --- a/src/pages/api/websites/[websiteId]/stats.ts +++ b/src/pages/api/websites/[websiteId]/stats.ts @@ -72,7 +72,11 @@ export default async ( const filters = getRequestFilters(req); - const metrics = await getWebsiteStats(websiteId, { ...filters, startDate, endDate }); + const metrics = await getWebsiteStats(websiteId, { + ...filters, + startDate, + endDate, + }); const prevPeriod = await getWebsiteStats(websiteId, { ...filters, diff --git a/src/queries/analytics/eventData/getEventDataEvents.ts b/src/queries/analytics/events/getEventDataEvents.ts similarity index 77% rename from src/queries/analytics/eventData/getEventDataEvents.ts rename to src/queries/analytics/events/getEventDataEvents.ts index e5647debd0..0b19c5be33 100644 --- a/src/queries/analytics/eventData/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} @@ -85,24 +85,14 @@ async function clickhouseQuery( limit 500 `, params, - ).then(result => { - return Object.values(result).map((a: any) => { - return { - eventName: a.eventName, - fieldName: a.fieldName, - dataType: Number(a.dataType), - fieldValue: a.fieldValue, - total: Number(a.total), - }; - }); - }); + ); } return rawQuery( ` select event_name as eventName, - data_key as fieldName, + data_key as propertyName, data_type as dataType, count(*) as total from event_data @@ -113,14 +103,5 @@ async function clickhouseQuery( limit 500 `, params, - ).then(result => { - return Object.values(result).map((a: any) => { - return { - eventName: a.eventName, - fieldName: a.fieldName, - dataType: Number(a.dataType), - total: Number(a.total), - }; - }); - }); + ); } diff --git a/src/queries/analytics/events/getEventDataProperties.ts b/src/queries/analytics/events/getEventDataProperties.ts new file mode 100644 index 0000000000..e2cf082887 --- /dev/null +++ b/src/queries/analytics/events/getEventDataProperties.ts @@ -0,0 +1,68 @@ +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 getEventDataProperties( + ...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 + website_event.event_name as "eventName", + event_data.data_key as "propertyName", + count(*) as "total" + from event_data + join website_event on website_event.event_id = event_data.website_event_id + where event_data.website_id = {{websiteId::uuid}} + and event_data.created_at between {{startDate}} and {{endDate}} + ${filterQuery} + group by website_event.event_name, event_data.data_key + order by 3 desc + limit 500 + `, + params, + ); +} + +async function clickhouseQuery( + websiteId: string, + filters: QueryFilters & { propertyName?: string }, +): Promise<{ eventName: string; propertyName: string; total: number }[]> { + const { rawQuery, parseFilters } = clickhouse; + const { filterQuery, params } = await parseFilters(websiteId, filters, { + columns: { propertyName: 'data_key' }, + }); + + 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 event_name, data_key + order by 1, 3 desc + limit 500 + `, + params, + ); +} diff --git a/src/queries/analytics/eventData/getEventDataStats.ts b/src/queries/analytics/events/getEventDataStats.ts similarity index 80% rename from src/queries/analytics/eventData/getEventDataStats.ts rename to src/queries/analytics/events/getEventDataStats.ts index 09bef107c6..adeeda46ef 100644 --- a/src/queries/analytics/eventData/getEventDataStats.ts +++ b/src/queries/analytics/events/getEventDataStats.ts @@ -7,13 +7,13 @@ export async function getEventDataStats( ...args: [websiteId: string, filters: QueryFilters] ): Promise<{ events: number; - fields: number; + properties: number; records: number; }> { return runQuery({ [PRISMA]: () => relationalQuery(...args), [CLICKHOUSE]: () => clickhouseQuery(...args), - }).then(results => results[0]); + }).then(results => results?.[0]); } async function relationalQuery(websiteId: string, filters: QueryFilters) { @@ -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 @@ -68,13 +68,5 @@ async function clickhouseQuery( ) as t `, params, - ).then(result => { - return Object.values(result).map((a: any) => { - return { - events: Number(a.events), - fields: Number(a.fields), - records: Number(a.records), - }; - }); - }); + ); } diff --git a/src/queries/analytics/eventData/getEventDataUsage.ts b/src/queries/analytics/events/getEventDataUsage.ts similarity index 84% rename from src/queries/analytics/eventData/getEventDataUsage.ts rename to src/queries/analytics/events/getEventDataUsage.ts index 2a19f33e6d..1d146c9c71 100644 --- a/src/queries/analytics/eventData/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 new file mode 100644 index 0000000000..07e423075b --- /dev/null +++ b/src/queries/analytics/events/getEventDataValues.ts @@ -0,0 +1,75 @@ +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 & { eventName?: string; propertyName?: string }, + ] +): Promise { + return runQuery({ + [PRISMA]: () => relationalQuery(...args), + [CLICKHOUSE]: () => clickhouseQuery(...args), + }); +} + +async function relationalQuery( + websiteId: string, + filters: QueryFilters & { eventName?: string; propertyName?: string }, +) { + const { rawQuery, parseFilters, getDateSQL } = prisma; + const { filterQuery, params } = await parseFilters(websiteId, filters); + + return rawQuery( + ` + select + case + when data_type = 2 then replace(string_value, '.0000', '') + when data_type = 4 then ${getDateSQL('date_value', 'hour')} + else string_value + end as "value", + count(*) as "total" + from event_data + join website_event on website_event.event_id = event_data.website_event_id + where event_data.website_id = {{websiteId::uuid}} + and event_data.created_at between {{startDate}} and {{endDate}} + and event_data.data_key = {{propertyName}} + and website_event.event_name = {{eventName}} + ${filterQuery} + group by value + order by 2 desc + limit 100 + `, + params, + ); +} + +async function clickhouseQuery( + websiteId: 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); + + 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 event_data + 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 event_name, value + order by 2 desc + limit 100 + `, + params, + ); +} diff --git a/src/queries/analytics/events/getEventMetrics.ts b/src/queries/analytics/events/getEventMetrics.ts index 32cccd3e85..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] @@ -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,17 +45,19 @@ 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, }); - return rawQuery( - ` + let sql = ''; + + if (filterQuery) { + sql = ` 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} @@ -64,9 +66,27 @@ async function clickhouseQuery( ${filterQuery} group by x, t order by t - `, - params, - ).then(a => { + `; + } else { + sql = ` + select + event_name x, + ${getDateSQL('created_at', unit, timezone)} t, + count(*) y + from ( + select arrayJoin(event_name) as event_name, + created_at + 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} + ) as g + group by x, t + order by t + `; + } + + 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/events/getEvents.ts b/src/queries/analytics/events/getEvents.ts deleted file mode 100644 index c333242e35..0000000000 --- a/src/queries/analytics/events/getEvents.ts +++ /dev/null @@ -1,63 +0,0 @@ -import clickhouse from 'lib/clickhouse'; -import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; -import prisma from 'lib/prisma'; -import { QueryFilters } from 'lib/types'; - -export function getEvents(...args: [websiteId: string, filters: QueryFilters]) { - return runQuery({ - [PRISMA]: () => relationalQuery(...args), - [CLICKHOUSE]: () => clickhouseQuery(...args), - }); -} - -function relationalQuery(websiteId: string, filters: QueryFilters) { - const { startDate } = filters; - - 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, - }; - }); - }); -} - -function clickhouseQuery(websiteId: string, filters: QueryFilters) { - const { rawQuery } = clickhouse; - const { startDate } = filters; - - return rawQuery( - ` - select - event_id as id, - website_id as websiteId, - session_id as sessionId, - created_at as createdAt, - toUnixTimestamp(created_at) as timestamp, - url_path as urlPath, - referrer_domain as referrerDomain, - event_name as eventName - from website_event - where website_id = {websiteId:UUID} - and created_at >= {startDate:DateTime64} - order by created_at desc - `, - { - websiteId, - startDate, - }, - ); -} diff --git a/src/queries/analytics/events/getWebsiteEvents.ts b/src/queries/analytics/events/getWebsiteEvents.ts new file mode 100644 index 0000000000..21e6270c76 --- /dev/null +++ b/src/queries/analytics/events/getWebsiteEvents.ts @@ -0,0 +1,98 @@ +import clickhouse from 'lib/clickhouse'; +import { CLICKHOUSE, getDatabaseType, POSTGRESQL, PRISMA, runQuery } from 'lib/db'; +import prisma from 'lib/prisma'; +import { PageParams, QueryFilters } from 'lib/types'; + +export function getWebsiteEvents( + ...args: [websiteId: string, filters: QueryFilters, pageParams?: PageParams] +) { + return runQuery({ + [PRISMA]: () => relationalQuery(...args), + [CLICKHOUSE]: () => clickhouseQuery(...args), + }); +} + +async function relationalQuery(websiteId: string, filters: QueryFilters, pageParams?: PageParams) { + const { pagedRawQuery, parseFilters } = prisma; + const { query } = pageParams; + const { filterQuery, params } = await parseFilters(websiteId, { + ...filters, + }); + + const db = getDatabaseType(); + const like = db === POSTGRESQL ? 'ilike' : 'like'; + + 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}} and event_type = 2) + or (url_path ${like} {{query}} and event_type = 1))` + : '' + } + order by created_at desc + limit 1000) + select * from events + `, + { ...params, query: `%${query}%` }, + pageParams, + ); +} + +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( + ` + 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} + ${dateQuery} + ${filterQuery} + ${ + query + ? `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 + limit 1000) + select * from events + `, + { ...params, query }, + pageParams, + ); +} diff --git a/src/queries/analytics/events/saveEvent.ts b/src/queries/analytics/events/saveEvent.ts index 25bcf9e7af..6c0f917baa 100644 --- a/src/queries/analytics/events/saveEvent.ts +++ b/src/queries/analytics/events/saveEvent.ts @@ -1,9 +1,10 @@ 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'; -import { saveEventData } from 'queries/analytics/eventData/saveEventData'; +import { saveEventData } from './saveEventData'; export async function saveEvent(args: { websiteId: string; @@ -134,9 +135,10 @@ async function clickhouseQuery(data: { city, ...args } = data; - const { getDateFormat, sendMessage } = kafka; + const { insert, getUTCString } = clickhouse; + const { sendMessage } = kafka; const eventId = uuid(); - const createdAt = getDateFormat(new Date()); + const createdAt = getUTCString(); const message = { ...args, @@ -164,7 +166,11 @@ async function clickhouseQuery(data: { created_at: createdAt, }; - await sendMessage(message, 'event'); + if (kafka.enabled) { + await sendMessage('event', message); + } else { + await insert('website_event', [message]); + } if (eventData) { await saveEventData({ diff --git a/src/queries/analytics/eventData/saveEventData.ts b/src/queries/analytics/events/saveEventData.ts similarity index 86% rename from src/queries/analytics/eventData/saveEventData.ts rename to src/queries/analytics/events/saveEventData.ts index 0ed3c8b016..7e7db8b465 100644 --- a/src/queries/analytics/eventData/saveEventData.ts +++ b/src/queries/analytics/events/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,7 +60,8 @@ async function clickhouseQuery(data: { }) { const { websiteId, sessionId, eventId, urlPath, eventName, eventData, createdAt } = data; - const { getDateFormat, sendMessages } = kafka; + const { insert, getUTCString } = clickhouse; + const { sendMessages } = kafka; const jsonKeys = flattenJSON(eventData); @@ -74,12 +76,16 @@ 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 ? getUTCString(value) : null, created_at: createdAt, }; }); - await sendMessages(messages, 'event_data'); + if (kafka.enabled) { + await sendMessages('event_data', messages); + } else { + await insert('event_data', messages); + } return data; } 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/getRealtimeActivity.ts b/src/queries/analytics/getRealtimeActivity.ts new file mode 100644 index 0000000000..fba303b2f9 --- /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, + 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_event.website_id = {{websiteId::uuid}} + ${filterQuery} + ${dateQuery} + order by website_event.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 b42fbc50af..63aa9aecb8 100644 --- a/src/queries/analytics/getRealtimeData.ts +++ b/src/queries/analytics/getRealtimeData.ts @@ -1,6 +1,4 @@ -import { getSessions, getEvents, getPageviewStats, getSessionStats } from 'queries/index'; - -const MAX_SIZE = 50; +import { getRealtimeActivity, getPageviewStats, getSessionStats } from 'queries/index'; function increment(data: object, key: string) { if (key) { @@ -12,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([ - getEvents(websiteId, { startDate }), - getSessions(websiteId, { startDate }), +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.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.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: [], @@ -74,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.filter(e => !e.eventName).length, - visitors: uniques.size, - events: events.filter(e => e.eventName).length, - countries: Object.keys(sessionStats.countries).length, + 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, }, 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 6257e166c9..061d487ead 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] @@ -21,7 +22,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 +35,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, @@ -67,12 +68,14 @@ async function clickhouseQuery( eventType: EVENT_TYPE.pageView, }); - return rawQuery( - ` - select + 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", + 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 ( @@ -89,17 +92,30 @@ async function clickhouseQuery( ${filterQuery} group by session_id, visit_id ) as t; - `, - 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), - }; - }); - }); + `; + } else { + sql = ` + select + sum(t.c) as "pageviews", + uniq(session_id) as "visitors", + uniq(visit_id) as "visits", + 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 + ) as t; + `; + } + + return rawQuery(sql, params); } diff --git a/src/queries/analytics/pageviews/getPageviewMetrics.ts b/src/queries/analytics/pageviews/getPageviewMetrics.ts index 67ccb04aab..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'; @@ -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( @@ -87,29 +90,32 @@ async function clickhouseQuery( eventType: column === 'event_name' ? EVENT_TYPE.customEvent : EVENT_TYPE.pageView, }); - let entryExitQuery = ''; let excludeDomain = ''; - if (column === 'referrer_domain') { - excludeDomain = `and referrer_domain != {websiteDomain:String} and referrer_domain != ''`; - } + let sql = ''; - if (type === 'entry' || type === 'exit') { - const aggregrate = type === 'entry' ? 'min' : 'max'; + if (EVENT_COLUMNS.some(item => Object.keys(filters).includes(item))) { + let entryExitQuery = ''; - 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 (column === 'referrer_domain') { + excludeDomain = `and referrer_domain != {websiteDomain:String} and referrer_domain != ''`; + } - return rawQuery( - ` + if (type === 'entry' || type === 'exit') { + 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`; + } + + sql = ` select ${column} x, count(*) y from website_event ${entryExitQuery} @@ -122,9 +128,48 @@ async function clickhouseQuery( order by y desc limit ${limit} offset ${offset} - `, - params, - ).then((result: any) => { + `; + } else { + let groupByQuery = ''; + + if (column === 'referrer_domain') { + excludeDomain = `and t != {websiteDomain:String} and t != ''`; + } + + 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 ( + select ${columnQuery} as t + 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} + ${excludeDomain} + ${filterQuery} + ${groupByQuery}) as g + group by x + order by y desc + limit ${limit} + offset ${offset} + `; + } + + 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 a37a15662f..bf8a0e8f6a 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]) { @@ -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} @@ -40,21 +40,23 @@ async function clickhouseQuery( websiteId: string, filters: QueryFilters, ): Promise<{ x: string; y: number }[]> { - const { timezone = 'UTC', unit = 'day' } = filters; - const { parseFilters, rawQuery, getDateStringQuery, getDateQuery } = clickhouse; + const { unit = 'day' } = filters; + const { parseFilters, rawQuery } = clickhouse; const { filterQuery, params } = await parseFilters(websiteId, { ...filters, eventType: EVENT_TYPE.pageView, }); - return rawQuery( - ` + let sql = ''; + + if (EVENT_COLUMNS.some(item => Object.keys(filters).includes(item)) || unit === 'minute') { + sql = ` select - ${getDateStringQuery('g.t', unit)} as x, + g.t as x, g.y as y from ( - select - ${getDateQuery('created_at', unit, timezone)} as t, + select + date_trunc('${unit}', created_at) as t, count(*) as y from website_event where website_id = {websiteId:UUID} @@ -64,11 +66,26 @@ async function clickhouseQuery( group by t ) as g order by t - `, - params, - ).then(result => { - return Object.values(result).map((a: any) => { - return { x: a.x, y: Number(a.y) }; - }); - }); + `; + } else { + sql = ` + select + g.t as x, + g.y as y + from ( + select + date_trunc('${unit}', created_at) 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); } 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..eb184e1806 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 @@ -93,9 +90,7 @@ async function relationalQuery( startDate, endDate, }, - ).then(results => { - return results.map(i => ({ ...i, percentage: Number(i.percentage) || 0 })); - }); + ); } async function clickhouseQuery( @@ -115,14 +110,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 +127,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 +152,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, @@ -172,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 6b151bb72f..502505f44f 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, @@ -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/getSession.ts b/src/queries/analytics/sessions/getSession.ts deleted file mode 100644 index 256ada4c08..0000000000 --- a/src/queries/analytics/sessions/getSession.ts +++ /dev/null @@ -1,9 +0,0 @@ -import prisma from 'lib/prisma'; - -export async function getSession(id: string) { - return prisma.client.session.findUnique({ - where: { - id, - }, - }); -} diff --git a/src/queries/analytics/sessions/getSessionActivity.ts b/src/queries/analytics/sessions/getSessionActivity.ts new file mode 100644 index 0000000000..3bda0d2917 --- /dev/null +++ b/src/queries/analytics/sessions/getSessionActivity.ts @@ -0,0 +1,58 @@ +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] +) { + return runQuery({ + [PRISMA]: () => relationalQuery(...args), + [CLICKHOUSE]: () => clickhouseQuery(...args), + }); +} + +async function relationalQuery( + websiteId: string, + sessionId: string, + startDate: Date, + endDate: Date, +) { + return prisma.client.websiteEvent.findMany({ + where: { + sessionId, + websiteId, + createdAt: { gte: startDate, lte: endDate }, + }, + take: 500, + }); +} + +async function clickhouseQuery( + websiteId: string, + sessionId: string, + startDate: Date, + endDate: Date, +) { + const { rawQuery } = clickhouse; + + return rawQuery( + ` + select + 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} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} + order by created_at desc + limit 500 + `, + { websiteId, sessionId, startDate, endDate }, + ); +} diff --git a/src/queries/analytics/sessions/getSessionData.ts b/src/queries/analytics/sessions/getSessionData.ts new file mode 100644 index 0000000000..ce80b0351a --- /dev/null +++ b/src/queries/analytics/sessions/getSessionData.ts @@ -0,0 +1,56 @@ +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) { + 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) { + const { rawQuery } = clickhouse; + + 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 final + where website_id = {websiteId:UUID} + and session_id = {sessionId:UUID} + order by data_key asc + `, + { websiteId, sessionId }, + ); +} diff --git a/src/queries/analytics/eventData/getEventDataFields.ts b/src/queries/analytics/sessions/getSessionDataProperties.ts similarity index 51% rename from src/queries/analytics/eventData/getEventDataFields.ts rename to src/queries/analytics/sessions/getSessionDataProperties.ts index f669ad39f6..1d15ea8de1 100644 --- a/src/queries/analytics/eventData/getEventDataFields.ts +++ b/src/queries/analytics/sessions/getSessionDataProperties.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 getSessionDataProperties( + ...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 + from session_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,37 +40,26 @@ 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; 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 + from session_data final 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, - dataType: Number(a.dataType), - fieldValue: a.fieldValue, - 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..c02e4adb06 --- /dev/null +++ b/src/queries/analytics/sessions/getSessionDataValues.ts @@ -0,0 +1,69 @@ +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, getDateSQL } = prisma; + const { filterQuery, params } = await parseFilters(websiteId, filters); + + return rawQuery( + ` + select + case + when data_type = 2 then replace(string_value, '.0000', '') + when data_type = 4 then ${getDateSQL('date_value', 'hour')} + else string_value + end 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 value + order by 2 desc + limit 100 + `, + 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 session_data final + 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 100 + `, + params, + ); +} diff --git a/src/queries/analytics/sessions/getSessionMetrics.ts b/src/queries/analytics/sessions/getSessionMetrics.ts index e28f1fb2e7..bb8bc4c5c4 100644 --- a/src/queries/analytics/sessions/getSessionMetrics.ts +++ b/src/queries/analytics/sessions/getSessionMetrics.ts @@ -1,7 +1,7 @@ -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 { 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'; export async function getSessionMetrics( @@ -71,8 +71,10 @@ async function clickhouseQuery( }); 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 @@ -87,9 +89,27 @@ async function clickhouseQuery( order by y desc limit ${limit} offset ${offset} - `, - params, - ).then(a => { + `; + } else { + sql = ` + select + ${column} x, + uniq(session_id) y + ${includeCountry ? ', country' : ''} + 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 x + ${includeCountry ? ', country' : ''} + order by y desc + limit ${limit} + offset ${offset} + `; + } + + 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 e3af7ba6d1..ee16e42560 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]) { @@ -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} @@ -40,21 +40,23 @@ async function clickhouseQuery( websiteId: string, filters: QueryFilters, ): Promise<{ x: string; y: number }[]> { - const { timezone = 'UTC', unit = 'day' } = filters; - const { parseFilters, rawQuery, getDateStringQuery, getDateQuery } = clickhouse; + const { unit = 'day' } = filters; + const { parseFilters, rawQuery } = clickhouse; const { filterQuery, params } = await parseFilters(websiteId, { ...filters, eventType: EVENT_TYPE.pageView, }); - return rawQuery( - ` + let sql = ''; + + if (EVENT_COLUMNS.some(item => Object.keys(filters).includes(item)) || unit === 'minute') { + sql = ` select - ${getDateStringQuery('g.t', unit)} as x, + g.t as x, g.y as y from ( - select - ${getDateQuery('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} @@ -64,11 +66,26 @@ async function clickhouseQuery( group by t ) as g order by t - `, - params, - ).then(result => { - return Object.values(result).map((a: any) => { - return { x: a.x, y: Number(a.y) }; - }); - }); + `; + } else { + sql = ` + select + g.t as x, + g.y as y + from ( + 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} + 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); } diff --git a/src/queries/analytics/sessions/getSessions.ts b/src/queries/analytics/sessions/getSessions.ts deleted file mode 100644 index a11edd39bd..0000000000 --- a/src/queries/analytics/sessions/getSessions.ts +++ /dev/null @@ -1,69 +0,0 @@ -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 getSessions(...args: [websiteId: string, filters: QueryFilters]) { - return runQuery({ - [PRISMA]: () => relationalQuery(...args), - [CLICKHOUSE]: () => clickhouseQuery(...args), - }); -} - -async function relationalQuery(websiteId: string, filters: QueryFilters) { - const { startDate } = filters; - - 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, - }; - }); - }); -} - -async function clickhouseQuery(websiteId: string, filters: QueryFilters) { - const { rawQuery } = clickhouse; - const { startDate } = filters; - - return rawQuery( - ` - select - session_id as id, - website_id as websiteId, - created_at as createdAt, - toUnixTimestamp(created_at) as timestamp, - hostname, - browser, - os, - device, - screen, - language, - country, - subdivision1, - subdivision2, - city - from website_event - where website_id = {websiteId:UUID} - and created_at >= {startDate:DateTime64} - order by created_at desc - `, - { - websiteId, - startDate, - }, - ); -} diff --git a/src/queries/analytics/sessions/getWebsiteSession.ts b/src/queries/analytics/sessions/getWebsiteSession.ts new file mode 100644 index 0000000000..f9b9f39a08 --- /dev/null +++ b/src/queries/analytics/sessions/getWebsiteSession.ts @@ -0,0 +1,109 @@ +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]) { + return runQuery({ + [PRISMA]: () => relationalQuery(...args), + [CLICKHOUSE]: () => clickhouseQuery(...args), + }); +} + +async function relationalQuery(websiteId: string, sessionId: string) { + 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) { + const { rawQuery, getDateStringSQL } = clickhouse; + + return rawQuery( + ` + select id, + websiteId, + hostname, + browser, + os, + device, + screen, + language, + country, + subdivision1, + city, + ${getDateStringSQL('min(min_time)')} as firstAt, + ${getDateStringSQL('max(max_time)')} as lastAt, + 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]); +} diff --git a/src/queries/analytics/sessions/getWebsiteSessionStats.ts b/src/queries/analytics/sessions/getWebsiteSessionStats.ts new file mode 100644 index 0000000000..648be14000 --- /dev/null +++ b/src/queries/analytics/sessions/getWebsiteSessionStats.ts @@ -0,0 +1,72 @@ +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 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}} + 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, + ); +} diff --git a/src/queries/analytics/sessions/getWebsiteSessions.ts b/src/queries/analytics/sessions/getWebsiteSessions.ts new file mode 100644 index 0000000000..d2a827d0eb --- /dev/null +++ b/src/queries/analytics/sessions/getWebsiteSessions.ts @@ -0,0 +1,102 @@ +import clickhouse from 'lib/clickhouse'; +import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; +import prisma from 'lib/prisma'; +import { PageParams, QueryFilters } from 'lib/types'; + +export async function getWebsiteSessions( + ...args: [websiteId: string, filters?: QueryFilters, pageParams?: PageParams] +) { + return runQuery({ + [PRISMA]: () => relationalQuery(...args), + [CLICKHOUSE]: () => clickhouseQuery(...args), + }); +} + +async function relationalQuery(websiteId: string, filters: QueryFilters, pageParams: PageParams) { + const { pagedRawQuery, parseFilters } = prisma; + const { filterQuery, params } = await parseFilters(websiteId, { + ...filters, + }); + + return pagedRawQuery( + ` + with sessions as ( + select + session.session_id as "id", + session.website_id as "websiteId", + session.hostname, + session.browser, + session.os, + session.device, + session.screen, + session.language, + session.country, + session.subdivision1, + session.city, + min(website_event.created_at) as "firstAt", + max(website_event.created_at) as "lastAt", + count(distinct website_event.visit_id) as "visits", + sum(case when website_event.event_type = 1 then 1 else 0 end) as "views", + max(website_event.created_at) as "createdAt" + from website_event + join session on session.session_id = website_event.session_id + where website_event.website_id = {{websiteId::uuid}} + and website_event.created_at between {{startDate}} and {{endDate}} + ${filterQuery} + group by session.session_id, + session.website_id, + session.hostname, + session.browser, + session.os, + session.device, + session.screen, + session.language, + session.country, + session.subdivision1, + session.city + order by max(website_event.created_at) desc + limit 1000) + select * from sessions + `, + params, + pageParams, + ); +} + +async function clickhouseQuery(websiteId: string, filters: QueryFilters, pageParams?: PageParams) { + const { pagedQuery, parseFilters, getDateStringSQL } = clickhouse; + const { params, dateQuery, filterQuery } = await parseFilters(websiteId, filters); + + return pagedQuery( + ` + with sessions as ( + select + session_id as id, + website_id as websiteId, + hostname, + browser, + os, + device, + screen, + language, + country, + subdivision1, + city, + ${getDateStringSQL('min(min_time)')} as firstAt, + ${getDateStringSQL('max(max_time)')} as lastAt, + uniq(visit_id) as visits, + sumIf(views, event_type = 1) as views, + lastAt as createdAt + from website_event_stats_hourly + where website_id = {websiteId:UUID} + ${dateQuery} + ${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, + ); +} diff --git a/src/queries/analytics/sessions/getWebsiteSessionsWeekly.ts b/src/queries/analytics/sessions/getWebsiteSessionsWeekly.ts new file mode 100644 index 0000000000..c92c2929be --- /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 + 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/analytics/sessions/saveSessionData.ts b/src/queries/analytics/sessions/saveSessionData.ts index 429c6e281b..f9f0276ecc 100644 --- a/src/queries/analytics/sessions/saveSessionData.ts +++ b/src/queries/analytics/sessions/saveSessionData.ts @@ -5,12 +5,12 @@ 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; sessionId: string; sessionData: DynamicData; - createdAt?: string; }) { return runQuery({ [PRISMA]: () => relationalQuery(data), @@ -77,11 +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 { getDateFormat, sendMessages } = kafka; + const { insert, getUTCString } = clickhouse; + const { sendMessages } = kafka; + const createdAt = getUTCString(); const jsonKeys = flattenJSON(sessionData); @@ -93,12 +94,16 @@ 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 ? getUTCString(value) : null, created_at: createdAt, }; }); - await sendMessages(messages, 'session_data'); + if (kafka.enabled) { + await sendMessages('session_data', messages); + } else { + await insert('session_data', messages); + } return data; } diff --git a/src/queries/index.ts b/src/queries/index.ts index 8cef080aa1..a2697ced58 100644 --- a/src/queries/index.ts +++ b/src/queries/index.ts @@ -1,15 +1,16 @@ -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/getEventDataEvents'; +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'; +export * from './analytics/events/getWebsiteEvents'; 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/saveEvent'; export * from './analytics/reports/getFunnel'; export * from './analytics/reports/getJourney'; @@ -19,12 +20,18 @@ 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/getSessionData'; +export * from './analytics/sessions/getSessionDataProperties'; +export * from './analytics/sessions/getSessionDataValues'; export * from './analytics/sessions/getSessionMetrics'; -export * from './analytics/sessions/getSessions'; +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'; export * from './analytics/getActiveVisitors'; +export * from './analytics/getRealtimeActivity'; export * from './analytics/getRealtimeData'; export * from './analytics/getValues'; export * from './analytics/getWebsiteDateRange'; 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) { 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 */ } }; diff --git a/yarn.lock b/yarn.lock index 9b29fa7467..910f0c69f2 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" @@ -1206,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.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.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.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.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.0.2" + "@clickhouse/client-common" "1.4.1" "@colors/colors@1.5.0": version "1.5.0" @@ -1356,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== @@ -1388,6 +1381,204 @@ 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" + 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" @@ -1565,14 +1756,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 +1770,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 +1794,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 +1811,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 +1859,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,66 +2193,66 @@ 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.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.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.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.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.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" -"@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.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" @@ -2089,90 +2280,96 @@ 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.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.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.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.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.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.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.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.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.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.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.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.14.0" - "@prisma/engines-version" "5.14.0-25.e9771e62de70f79a5e1c604a2d7c8e2a0a874b48" - "@prisma/get-platform" "5.14.0" + "@prisma/debug" "5.17.0" + "@prisma/engines-version" "5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053" + "@prisma/get-platform" "5.17.0" -"@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.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.14.0" + "@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" @@ -2216,9 +2413,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 +2444,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 +2470,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.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" @@ -2430,17 +2627,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.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.35.5" - resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.35.5.tgz#d41a087d58f42418824fa04aaca00ba93c99075c" - integrity sha512-sppX7L+PVn5GBV3In6zzj0zcKfnZRKhXbX1MfIfKo1OjIq2GMaopvAFOP0x1bRYTUk2ikrdYcQYOozX7PWkb8A== + 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.35.5" + "@tanstack/query-core" "5.51.21" "@trysound/sax@0.2.0": version "0.2.0" @@ -2525,21 +2722,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" @@ -2612,7 +2804,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== @@ -2652,12 +2844,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.12.11" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.11.tgz#c4ef00d3507000d17690643278a60dc55a9dc9be" - integrity sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw== +"@types/node@*": + version "22.4.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.4.0.tgz#c295fe1d6f5f58916cc61dbef8cf65b5b9b71de9" + integrity sha512-49AbMDwYUz7EXxKU/r7mXOsxwFr4BYbvB7tWYxVuLdb2ibd30ijjXINSMAHiEEZk5PCRBmW1gUeisn2VMKt3cQ== dependencies: - undici-types "~5.26.4" + undici-types "~6.19.2" "@types/node@14": version "14.18.63" @@ -2669,6 +2861,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.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 "~6.13.0" + "@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" @@ -2704,9 +2903,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 +3408,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 +3428,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 +3445,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== @@ -3290,25 +3489,15 @@ 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.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: @@ -3352,7 +3541,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== @@ -3392,21 +3581,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.1" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.1.tgz#bb5f8b8a20739f6ae1caeaf7eea2c7913df8048e" + integrity sha512-u5w79Rd7SU4JaIlA/zFqG+gOiuq25q5VLyZ8E+ijJeILuTxVzZgp2CaGw/UTw6pXYN9XMO9yiqj/nEHmhTG5CA== -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.10.0" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.0.tgz#d9e56ab0147278272739a000880196cdfe113b59" + integrity sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g== -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 +3754,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== @@ -3701,20 +3890,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" @@ -3735,7 +3914,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== @@ -3754,9 +3933,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 +3989,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,11 +4446,11 @@ 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.13.3" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.13.3.tgz#21ee054bb4e00b3858f2e33b4f8f4e69128470a9" + integrity sha512-hUxPrdbJXhUOTzuML+y9Av7CKoYznbD83pt8g3klgpioEha0emfx4WNIuVRx0C76r0xV2MIwAW9WYiXfVJYFQw== 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" @@ -4310,7 +4489,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" @@ -4446,22 +4625,17 @@ 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.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" 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 +4649,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.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" + integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== + 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 +4679,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 +4722,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 +4764,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" @@ -4698,6 +4898,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" @@ -4731,9 +4938,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.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" @@ -4763,7 +4970,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 +5029,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 +5155,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.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.3" + "@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" @@ -5032,26 +5254,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 +5283,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.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" @@ -5071,28 +5293,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.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.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" + hasown "^2.0.2" 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.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" + string.prototype.repeat "^1.0.0" eslint-scope@^5.1.1: version "5.1.1" @@ -5439,6 +5661,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" @@ -5489,9 +5718,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" @@ -5572,7 +5801,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== @@ -5609,7 +5838,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== @@ -5647,9 +5876,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" @@ -6039,7 +6268,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 +6297,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 +6312,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 +6379,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.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" + +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 +6466,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 +6538,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== @@ -6372,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.6" - resolved "https://registry.yarnpkg.com/isbot/-/isbot-5.1.6.tgz#579a48515e92a3e064da63a41709815d8e641a30" - integrity sha512-Phksj1A0dBP/M/5xeOx0zWemKlZRQvrbNzI19/HWso0uodiOcR8YYCXN60IdzwbKsGj5LnxPkMy6FuBtgckMNw== +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" @@ -6460,6 +6704,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" @@ -7033,9 +7287,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 +7544,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.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" @@ -7308,13 +7562,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,12 +7618,12 @@ 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.21" + resolved "https://registry.yarnpkg.com/maxmind/-/maxmind-4.3.21.tgz#cc3af09775e9b96434a56b6ac63c9bf4878d23b8" + integrity sha512-orda4yI01roTa4pP5Jf43u5HPOoEIGaLudTn9cdgCPyZDPipTJdbz1boZQR9QE96hvwNDrJwt1ak9vHIE6iZSQ== dependencies: - mmdb-lib "2.1.0" - tiny-lru "11.2.6" + mmdb-lib "2.1.1" + tiny-lru "11.2.11" md5@^2.3.0: version "2.3.0" @@ -7443,7 +7704,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 +7712,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 +7769,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 +7802,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 +7819,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" @@ -7601,12 +7870,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.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.3" + "@next/env" "14.2.5" "@swc/helpers" "0.5.5" busboy "1.6.0" caniuse-lite "^1.0.30001579" @@ -7614,15 +7883,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.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" @@ -7743,9 +8012,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 +8039,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 +8048,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,16 +8067,7 @@ object.groupby@^1.0.1: define-properties "^1.2.1" es-abstract "^1.23.2" -object.hasown@^1.1.3: - 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.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 +8237,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" @@ -7998,10 +8266,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" @@ -8620,12 +8888,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.41" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.41.tgz#d6104d3ba272d882fe18fc07d15dc2da62fa2681" + integrity sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ== dependencies: nanoid "^3.3.7" - picocolors "^1.0.0" + picocolors "^1.0.1" source-map-js "^1.2.0" prelude-ls@^1.2.1: @@ -8659,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.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.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.14.0" + "@prisma/engines" "5.17.0" process@^0.11.10: version "0.11.10" @@ -8765,10 +9033,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.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" @@ -8810,19 +9078,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 +9286,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 +9409,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" @@ -9340,9 +9608,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" @@ -9567,6 +9835,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 +9892,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== @@ -9644,6 +9927,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" @@ -9770,9 +10061,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" @@ -9972,12 +10263,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== @@ -10037,11 +10328,12 @@ 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.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" @@ -10084,11 +10376,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 +10503,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.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" @@ -10221,10 +10518,15 @@ 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" @@ -10297,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" @@ -10388,12 +10690,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" @@ -10402,10 +10704,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== @@ -10415,7 +10717,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: 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== @@ -10606,9 +10908,9 @@ yup@^0.32.11: property-expr "^2.0.4" 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== +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"