diff --git a/db/clickhouse/migrations/02_add_visit_id.sql b/db/clickhouse/migrations/02_add_visit_id.sql
new file mode 100644
index 000000000..202c0fd30
--- /dev/null
+++ b/db/clickhouse/migrations/02_add_visit_id.sql
@@ -0,0 +1,90 @@
+CREATE TABLE umami.website_event_join
+(
+ session_id UUID,
+ visit_id UUID,
+ created_at DateTime('UTC')
+)
+ engine = MergeTree
+ ORDER BY (session_id, created_at)
+ SETTINGS index_granularity = 8192;
+
+INSERT INTO umami.website_event_join
+SELECT DISTINCT
+ s.session_id,
+ generateUUIDv4() visit_id,
+ s.created_at
+FROM (SELECT DISTINCT session_id,
+ date_trunc('hour', created_at) created_at
+ FROM website_event) s;
+
+-- create new table
+CREATE TABLE umami.website_event_new
+(
+ website_id UUID,
+ session_id UUID,
+ visit_id UUID,
+ event_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),
+ subdivision2 LowCardinality(String),
+ city String,
+ url_path String,
+ url_query String,
+ referrer_path String,
+ referrer_query String,
+ referrer_domain String,
+ page_title String,
+ event_type UInt32,
+ event_name String,
+ created_at DateTime('UTC'),
+ job_id UUID
+)
+ engine = MergeTree
+ ORDER BY (website_id, session_id, created_at)
+ SETTINGS index_granularity = 8192;
+
+INSERT INTO umami.website_event_new
+SELECT we.website_id,
+ we.session_id,
+ j.visit_id,
+ we.event_id,
+ we.hostname,
+ we.browser,
+ we.os,
+ we.device,
+ we.screen,
+ we.language,
+ we.country,
+ we.subdivision1,
+ we.subdivision2,
+ we.city,
+ we.url_path,
+ we.url_query,
+ we.referrer_path,
+ we.referrer_query,
+ we.referrer_domain,
+ we.page_title,
+ we.event_type,
+ we.event_name,
+ we.created_at,
+ we.job_id
+FROM umami.website_event we
+JOIN umami.website_event_join j
+ ON we.session_id = j.session_id
+ and date_trunc('hour', we.created_at) = j.created_at
+
+RENAME TABLE umami.website_event TO umami.website_event_old;
+RENAME TABLE umami.website_event_new TO umami.website_event;
+
+/*
+
+ DROP TABLE umami.website_event_old
+ DROP TABLE umami.website_event_join
+
+ */
\ No newline at end of file
diff --git a/db/clickhouse/schema.sql b/db/clickhouse/schema.sql
index 741f06ad2..dad4f4af3 100644
--- a/db/clickhouse/schema.sql
+++ b/db/clickhouse/schema.sql
@@ -3,6 +3,7 @@ CREATE TABLE umami.website_event
(
website_id UUID,
session_id UUID,
+ visit_id UUID,
event_id UUID,
--sessions
hostname LowCardinality(String),
diff --git a/db/mysql/migrations/05_add_visit_id/migration.sql b/db/mysql/migrations/05_add_visit_id/migration.sql
new file mode 100644
index 000000000..7a833a885
--- /dev/null
+++ b/db/mysql/migrations/05_add_visit_id/migration.sql
@@ -0,0 +1,22 @@
+-- AlterTable
+ALTER TABLE `website_event` ADD COLUMN `visit_id` VARCHAR(36) NULL;
+
+UPDATE `website_event` we
+JOIN (SELECT DISTINCT
+ s.session_id,
+ s.visit_time,
+ BIN_TO_UUID(RANDOM_BYTES(16) & 0xffffffffffff0fff3fffffffffffffff | 0x00000000000040008000000000000000) uuid
+ FROM (SELECT DISTINCT session_id,
+ DATE_FORMAT(created_at, '%Y-%m-%d %H:00:00') visit_time
+ FROM `website_event`) s) a
+ ON we.session_id = a.session_id and DATE_FORMAT(we.created_at, '%Y-%m-%d %H:00:00') = a.visit_time
+SET we.visit_id = a.uuid
+WHERE we.visit_id IS NULL;
+
+ALTER TABLE `website_event` MODIFY `visit_id` VARCHAR(36) NOT NULL;
+
+-- CreateIndex
+CREATE INDEX `website_event_visit_id_idx` ON `website_event`(`visit_id`);
+
+-- CreateIndex
+CREATE INDEX `website_event_website_id_visit_id_created_at_idx` ON `website_event`(`website_id`, `visit_id`, `created_at`);
diff --git a/db/mysql/schema.prisma b/db/mysql/schema.prisma
index 8e5cbbc33..152ca265b 100644
--- a/db/mysql/schema.prisma
+++ b/db/mysql/schema.prisma
@@ -92,6 +92,7 @@ model WebsiteEvent {
id String @id() @map("event_id") @db.VarChar(36)
websiteId String @map("website_id") @db.VarChar(36)
sessionId String @map("session_id") @db.VarChar(36)
+ visitId String @map("visit_id") @db.VarChar(36)
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
urlPath String @map("url_path") @db.VarChar(500)
urlQuery String? @map("url_query") @db.VarChar(500)
@@ -107,6 +108,7 @@ model WebsiteEvent {
@@index([createdAt])
@@index([sessionId])
+ @@index([visitId])
@@index([websiteId])
@@index([websiteId, createdAt])
@@index([websiteId, createdAt, urlPath])
@@ -115,6 +117,7 @@ model WebsiteEvent {
@@index([websiteId, createdAt, pageTitle])
@@index([websiteId, createdAt, eventName])
@@index([websiteId, sessionId, createdAt])
+ @@index([websiteId, visitId, createdAt])
@@map("website_event")
}
diff --git a/db/postgresql/migrations/05_add_visit_id/migration.sql b/db/postgresql/migrations/05_add_visit_id/migration.sql
new file mode 100644
index 000000000..fd2f1b905
--- /dev/null
+++ b/db/postgresql/migrations/05_add_visit_id/migration.sql
@@ -0,0 +1,22 @@
+-- AlterTable
+ALTER TABLE "website_event" ADD COLUMN "visit_id" UUID NULL;
+
+UPDATE "website_event" we
+SET visit_id = a.uuid
+FROM (SELECT DISTINCT
+ s.session_id,
+ s.visit_time,
+ gen_random_uuid() uuid
+ FROM (SELECT DISTINCT session_id,
+ date_trunc('hour', created_at) visit_time
+ FROM "website_event") s) a
+WHERE we.session_id = a.session_id
+ and date_trunc('hour', we.created_at) = a.visit_time;
+
+ALTER TABLE "website_event" ALTER COLUMN "visit_id" SET NOT NULL;
+
+-- CreateIndex
+CREATE INDEX "website_event_visit_id_idx" ON "website_event"("visit_id");
+
+-- CreateIndex
+CREATE INDEX "website_event_website_id_visit_id_created_at_idx" ON "website_event"("website_id", "visit_id", "created_at");
\ No newline at end of file
diff --git a/db/postgresql/schema.prisma b/db/postgresql/schema.prisma
index 31cc7616d..0cb8ae8a2 100644
--- a/db/postgresql/schema.prisma
+++ b/db/postgresql/schema.prisma
@@ -92,6 +92,7 @@ model WebsiteEvent {
id String @id() @map("event_id") @db.Uuid
websiteId String @map("website_id") @db.Uuid
sessionId String @map("session_id") @db.Uuid
+ visitId String @map("visit_id") @db.Uuid
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6)
urlPath String @map("url_path") @db.VarChar(500)
urlQuery String? @map("url_query") @db.VarChar(500)
@@ -107,6 +108,7 @@ model WebsiteEvent {
@@index([createdAt])
@@index([sessionId])
+ @@index([visitId])
@@index([websiteId])
@@index([websiteId, createdAt])
@@index([websiteId, createdAt, urlPath])
@@ -115,6 +117,7 @@ model WebsiteEvent {
@@index([websiteId, createdAt, pageTitle])
@@index([websiteId, createdAt, eventName])
@@index([websiteId, sessionId, createdAt])
+ @@index([websiteId, visitId, createdAt])
@@map("website_event")
}
diff --git a/next.config.js b/next.config.js
index dce49100e..f8850c60c 100644
--- a/next.config.js
+++ b/next.config.js
@@ -14,6 +14,7 @@ const frameAncestors = process.env.ALLOWED_FRAME_URLS || '';
const disableLogin = process.env.DISABLE_LOGIN || '';
const disableUI = process.env.DISABLE_UI || '';
const hostURL = process.env.HOST_URL || '';
+const privateMode = process.env.PRIVATE_MODE || '';
const contentSecurityPolicy = [
`default-src 'self'`,
@@ -120,6 +121,7 @@ const config = {
disableLogin,
disableUI,
hostURL,
+ privateMode,
},
basePath,
output: 'standalone',
diff --git a/package.json b/package.json
index 03f0f79aa..bd27a4cfe 100644
--- a/package.json
+++ b/package.json
@@ -66,7 +66,7 @@
"dependencies": {
"@clickhouse/client": "^0.2.2",
"@fontsource/inter": "^4.5.15",
- "@prisma/client": "5.10.2",
+ "@prisma/client": "5.11.0",
"@prisma/extension-read-replicas": "^0.3.0",
"@react-spring/web": "^9.7.3",
"@tanstack/react-query": "^5.28.6",
@@ -98,11 +98,11 @@
"maxmind": "^4.3.6",
"md5": "^2.3.0",
"moment-timezone": "^0.5.35",
- "next": "14.1.3",
+ "next": "14.1.4",
"next-basics": "^0.39.0",
"node-fetch": "^3.2.8",
"npm-run-all": "^4.1.5",
- "prisma": "5.10.2",
+ "prisma": "5.11.0",
"react": "^18.2.0",
"react-basics": "^0.123.0",
"react-beautiful-dnd": "^13.1.0",
@@ -115,7 +115,6 @@
"request-ip": "^3.3.0",
"semver": "^7.5.4",
"thenby": "^1.3.4",
- "timezone-support": "^2.0.2",
"uuid": "^9.0.0",
"yup": "^0.32.11",
"zustand": "^4.3.8"
diff --git a/public/intl/messages/am-ET.json b/public/intl/messages/am-ET.json
index 21a8e79ff..e17f35c77 100644
--- a/public/intl/messages/am-ET.json
+++ b/public/intl/messages/am-ET.json
@@ -41,7 +41,7 @@
"value": "Add website"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Administrator"
diff --git a/public/intl/messages/ar-SA.json b/public/intl/messages/ar-SA.json
index 08b7e51f2..42dea85be 100644
--- a/public/intl/messages/ar-SA.json
+++ b/public/intl/messages/ar-SA.json
@@ -41,7 +41,7 @@
"value": "إضافة موقع"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "مدير"
diff --git a/public/intl/messages/be-BY.json b/public/intl/messages/be-BY.json
index def58386c..4fd681e17 100644
--- a/public/intl/messages/be-BY.json
+++ b/public/intl/messages/be-BY.json
@@ -41,7 +41,7 @@
"value": "Дадаць сайт"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Адміністратар"
diff --git a/public/intl/messages/bn-BD.json b/public/intl/messages/bn-BD.json
index 0f32ba3bc..301ce73b1 100644
--- a/public/intl/messages/bn-BD.json
+++ b/public/intl/messages/bn-BD.json
@@ -41,7 +41,7 @@
"value": "ওয়েবসাইট যুক্ত করুন"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "অ্যাডমিন"
diff --git a/public/intl/messages/ca-ES.json b/public/intl/messages/ca-ES.json
index 0f441e0c9..4de900c3a 100644
--- a/public/intl/messages/ca-ES.json
+++ b/public/intl/messages/ca-ES.json
@@ -41,7 +41,7 @@
"value": "Afegeix lloc web"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Administrador"
diff --git a/public/intl/messages/cs-CZ.json b/public/intl/messages/cs-CZ.json
index 1b91d2472..9037654bf 100644
--- a/public/intl/messages/cs-CZ.json
+++ b/public/intl/messages/cs-CZ.json
@@ -41,7 +41,7 @@
"value": "Přidat web"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Administrátor"
diff --git a/public/intl/messages/da-DK.json b/public/intl/messages/da-DK.json
index a53d93a3f..ecc26e878 100644
--- a/public/intl/messages/da-DK.json
+++ b/public/intl/messages/da-DK.json
@@ -41,7 +41,7 @@
"value": "Tilføj hjemmeside"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Administrator"
diff --git a/public/intl/messages/de-CH.json b/public/intl/messages/de-CH.json
index e7abed6bd..b908b4b28 100644
--- a/public/intl/messages/de-CH.json
+++ b/public/intl/messages/de-CH.json
@@ -41,7 +41,7 @@
"value": "Websiite hinzuefüege"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Administrator"
diff --git a/public/intl/messages/de-DE.json b/public/intl/messages/de-DE.json
index 2477f0e64..0dc62bb61 100644
--- a/public/intl/messages/de-DE.json
+++ b/public/intl/messages/de-DE.json
@@ -41,7 +41,7 @@
"value": "Website hinzufügen"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Administrator"
diff --git a/public/intl/messages/el-GR.json b/public/intl/messages/el-GR.json
index dd78c9714..3ececb2c1 100644
--- a/public/intl/messages/el-GR.json
+++ b/public/intl/messages/el-GR.json
@@ -41,7 +41,7 @@
"value": "Προσθήκη ιστότοπου"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Διαχειριστής"
diff --git a/public/intl/messages/en-GB.json b/public/intl/messages/en-GB.json
index e035770eb..5f37a5400 100644
--- a/public/intl/messages/en-GB.json
+++ b/public/intl/messages/en-GB.json
@@ -41,7 +41,7 @@
"value": "Add website"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Administrator"
diff --git a/public/intl/messages/en-US.json b/public/intl/messages/en-US.json
index 68a3e1f73..8946c90eb 100644
--- a/public/intl/messages/en-US.json
+++ b/public/intl/messages/en-US.json
@@ -41,7 +41,7 @@
"value": "Add website"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Administrator"
diff --git a/public/intl/messages/es-ES.json b/public/intl/messages/es-ES.json
index b1ce8d8a8..9b0ba5bf4 100644
--- a/public/intl/messages/es-ES.json
+++ b/public/intl/messages/es-ES.json
@@ -41,7 +41,7 @@
"value": "Nuevo sitio web"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Administrador"
diff --git a/public/intl/messages/fa-IR.json b/public/intl/messages/fa-IR.json
index a680492cc..9168ff1a9 100644
--- a/public/intl/messages/fa-IR.json
+++ b/public/intl/messages/fa-IR.json
@@ -41,7 +41,7 @@
"value": "افزودن وبسایت"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "مدیر"
diff --git a/public/intl/messages/fi-FI.json b/public/intl/messages/fi-FI.json
index 5c9b76111..16cf8db66 100644
--- a/public/intl/messages/fi-FI.json
+++ b/public/intl/messages/fi-FI.json
@@ -41,7 +41,7 @@
"value": "Lisää verkkosivu"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Järjestelmänvalvoja"
diff --git a/public/intl/messages/fo-FO.json b/public/intl/messages/fo-FO.json
index 29d1eb5fb..b05faf6cf 100644
--- a/public/intl/messages/fo-FO.json
+++ b/public/intl/messages/fo-FO.json
@@ -41,7 +41,7 @@
"value": "Legg heimasíðu afturat"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Fyrisitari"
diff --git a/public/intl/messages/fr-FR.json b/public/intl/messages/fr-FR.json
index c8ba44c3b..c09732497 100644
--- a/public/intl/messages/fr-FR.json
+++ b/public/intl/messages/fr-FR.json
@@ -41,7 +41,7 @@
"value": "Ajouter un site"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Administrateur"
diff --git a/public/intl/messages/ga-ES.json b/public/intl/messages/ga-ES.json
index 926294eab..9e8252288 100644
--- a/public/intl/messages/ga-ES.json
+++ b/public/intl/messages/ga-ES.json
@@ -41,7 +41,7 @@
"value": "Engadir sitio web"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Administradora"
diff --git a/public/intl/messages/he-IL.json b/public/intl/messages/he-IL.json
index b3cdfeebb..596922931 100644
--- a/public/intl/messages/he-IL.json
+++ b/public/intl/messages/he-IL.json
@@ -41,7 +41,7 @@
"value": "הוספת אתר"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "מנהל"
diff --git a/public/intl/messages/hi-IN.json b/public/intl/messages/hi-IN.json
index 985ed5371..ef4deb6f9 100644
--- a/public/intl/messages/hi-IN.json
+++ b/public/intl/messages/hi-IN.json
@@ -41,7 +41,7 @@
"value": "वेबसाइट"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "प्रशासक"
diff --git a/public/intl/messages/hr-HR.json b/public/intl/messages/hr-HR.json
index ff3ca36c2..7259dd029 100644
--- a/public/intl/messages/hr-HR.json
+++ b/public/intl/messages/hr-HR.json
@@ -41,7 +41,7 @@
"value": "Dodaj web stranicu"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Administrator"
diff --git a/public/intl/messages/hu-HU.json b/public/intl/messages/hu-HU.json
index 28450892c..f6342e3c1 100644
--- a/public/intl/messages/hu-HU.json
+++ b/public/intl/messages/hu-HU.json
@@ -41,7 +41,7 @@
"value": "Weboldal hozzáadása"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Adminisztrátor"
diff --git a/public/intl/messages/id-ID.json b/public/intl/messages/id-ID.json
index d2b5a98cf..13385bd8f 100644
--- a/public/intl/messages/id-ID.json
+++ b/public/intl/messages/id-ID.json
@@ -41,7 +41,7 @@
"value": "Tambah situs web"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Pengelola"
diff --git a/public/intl/messages/it-IT.json b/public/intl/messages/it-IT.json
index 10942ffff..4008ef049 100644
--- a/public/intl/messages/it-IT.json
+++ b/public/intl/messages/it-IT.json
@@ -41,7 +41,7 @@
"value": "Aggiungi sito"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Amministratore"
diff --git a/public/intl/messages/ja-JP.json b/public/intl/messages/ja-JP.json
index 06ef769c1..e1882cd3f 100644
--- a/public/intl/messages/ja-JP.json
+++ b/public/intl/messages/ja-JP.json
@@ -41,7 +41,7 @@
"value": "Webサイトの追加"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "管理者"
diff --git a/public/intl/messages/km-KH.json b/public/intl/messages/km-KH.json
index 40a280f99..712bed110 100644
--- a/public/intl/messages/km-KH.json
+++ b/public/intl/messages/km-KH.json
@@ -41,7 +41,7 @@
"value": "បន្ថែមគេហទំព័រ"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "អ្នកគ្រប់គ្រង"
diff --git a/public/intl/messages/ko-KR.json b/public/intl/messages/ko-KR.json
index b95b0e320..4d19a3a26 100644
--- a/public/intl/messages/ko-KR.json
+++ b/public/intl/messages/ko-KR.json
@@ -41,7 +41,7 @@
"value": "웹사이트 추가"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "관리자"
diff --git a/public/intl/messages/lt-LT.json b/public/intl/messages/lt-LT.json
index 0d3817dad..e7d468387 100644
--- a/public/intl/messages/lt-LT.json
+++ b/public/intl/messages/lt-LT.json
@@ -41,7 +41,7 @@
"value": "Pridėti svetainę"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Administratorius"
diff --git a/public/intl/messages/mn-MN.json b/public/intl/messages/mn-MN.json
index 2c8a01e7d..509d8b5bb 100644
--- a/public/intl/messages/mn-MN.json
+++ b/public/intl/messages/mn-MN.json
@@ -41,7 +41,7 @@
"value": "Веб нэмэх"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Админ"
diff --git a/public/intl/messages/ms-MY.json b/public/intl/messages/ms-MY.json
index 4039bea13..997a09511 100644
--- a/public/intl/messages/ms-MY.json
+++ b/public/intl/messages/ms-MY.json
@@ -41,7 +41,7 @@
"value": "Tambah laman web"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Pentadbir"
diff --git a/public/intl/messages/my-MM.json b/public/intl/messages/my-MM.json
index 729dec130..9a586ac2f 100644
--- a/public/intl/messages/my-MM.json
+++ b/public/intl/messages/my-MM.json
@@ -41,7 +41,7 @@
"value": "ဝက်ဘ်ဆိုဒ်ထည့်မည်"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "အက်ဒမင်"
diff --git a/public/intl/messages/nb-NO.json b/public/intl/messages/nb-NO.json
index fadc11f81..b2b8715d4 100644
--- a/public/intl/messages/nb-NO.json
+++ b/public/intl/messages/nb-NO.json
@@ -41,7 +41,7 @@
"value": "Legg til nettsted"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Administrator"
diff --git a/public/intl/messages/nl-NL.json b/public/intl/messages/nl-NL.json
index 34777f935..c992b6c83 100644
--- a/public/intl/messages/nl-NL.json
+++ b/public/intl/messages/nl-NL.json
@@ -41,7 +41,7 @@
"value": "Website koppelen"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Beheerder"
diff --git a/public/intl/messages/pl-PL.json b/public/intl/messages/pl-PL.json
index 89ab2dcdb..923534487 100644
--- a/public/intl/messages/pl-PL.json
+++ b/public/intl/messages/pl-PL.json
@@ -41,7 +41,7 @@
"value": "Dodaj witrynę"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Administrator"
diff --git a/public/intl/messages/pt-BR.json b/public/intl/messages/pt-BR.json
index 3c6bec5bf..e926a9a2d 100644
--- a/public/intl/messages/pt-BR.json
+++ b/public/intl/messages/pt-BR.json
@@ -41,7 +41,7 @@
"value": "Adicionar site"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Administrador"
diff --git a/public/intl/messages/pt-PT.json b/public/intl/messages/pt-PT.json
index bcc7fae4b..ae3fb472c 100644
--- a/public/intl/messages/pt-PT.json
+++ b/public/intl/messages/pt-PT.json
@@ -41,7 +41,7 @@
"value": "Adicionar website"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Administrador"
diff --git a/public/intl/messages/ro-RO.json b/public/intl/messages/ro-RO.json
index 50ae21663..8236eba6b 100644
--- a/public/intl/messages/ro-RO.json
+++ b/public/intl/messages/ro-RO.json
@@ -41,7 +41,7 @@
"value": "Adăugare site web"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Administrator"
diff --git a/public/intl/messages/ru-RU.json b/public/intl/messages/ru-RU.json
index 7d3bd9ea4..e39299021 100644
--- a/public/intl/messages/ru-RU.json
+++ b/public/intl/messages/ru-RU.json
@@ -41,7 +41,7 @@
"value": "Добавить сайт"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Администратор"
diff --git a/public/intl/messages/si-LK.json b/public/intl/messages/si-LK.json
index eab794e6c..04d627fbc 100644
--- a/public/intl/messages/si-LK.json
+++ b/public/intl/messages/si-LK.json
@@ -41,7 +41,7 @@
"value": "වෙබ් අඩවිය එක් කරන්න"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Administrator"
diff --git a/public/intl/messages/sk-SK.json b/public/intl/messages/sk-SK.json
index b1c300eb1..690ee4c5a 100644
--- a/public/intl/messages/sk-SK.json
+++ b/public/intl/messages/sk-SK.json
@@ -41,7 +41,7 @@
"value": "Pridať web"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Administrátor"
diff --git a/public/intl/messages/sl-SI.json b/public/intl/messages/sl-SI.json
index 67524f57d..7773c813c 100644
--- a/public/intl/messages/sl-SI.json
+++ b/public/intl/messages/sl-SI.json
@@ -41,7 +41,7 @@
"value": "Dodaj spletno mesto"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Administrator"
diff --git a/public/intl/messages/sv-SE.json b/public/intl/messages/sv-SE.json
index 5e3b9021e..8b8ccf5a9 100644
--- a/public/intl/messages/sv-SE.json
+++ b/public/intl/messages/sv-SE.json
@@ -41,7 +41,7 @@
"value": "Lägg till webbplats"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Administratör"
diff --git a/public/intl/messages/ta-IN.json b/public/intl/messages/ta-IN.json
index e5f4f36bf..3566ed5cd 100644
--- a/public/intl/messages/ta-IN.json
+++ b/public/intl/messages/ta-IN.json
@@ -41,7 +41,7 @@
"value": "வலைத்தளத்தைச் சேர்க்க"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "நிர்வாகியைச் சேர்க்க"
diff --git a/public/intl/messages/th-TH.json b/public/intl/messages/th-TH.json
index f4356a50e..451e8e7a9 100644
--- a/public/intl/messages/th-TH.json
+++ b/public/intl/messages/th-TH.json
@@ -41,7 +41,7 @@
"value": "เพิ่มเว็บไซต์"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "ผู้ดูแลระบบ"
diff --git a/public/intl/messages/tr-TR.json b/public/intl/messages/tr-TR.json
index 1bd71b562..91d8d8dbe 100644
--- a/public/intl/messages/tr-TR.json
+++ b/public/intl/messages/tr-TR.json
@@ -41,7 +41,7 @@
"value": "Web sitesi ekle"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Yönetici"
diff --git a/public/intl/messages/uk-UA.json b/public/intl/messages/uk-UA.json
index 4dd2e86d5..d9b5bd1e2 100644
--- a/public/intl/messages/uk-UA.json
+++ b/public/intl/messages/uk-UA.json
@@ -41,7 +41,7 @@
"value": "Додати сайт"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Адміністратор"
diff --git a/public/intl/messages/ur-PK.json b/public/intl/messages/ur-PK.json
index 1f9ad9959..883992dc8 100644
--- a/public/intl/messages/ur-PK.json
+++ b/public/intl/messages/ur-PK.json
@@ -41,7 +41,7 @@
"value": "ویب سائٹ کا اضافہ کریں"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "منتظم"
diff --git a/public/intl/messages/vi-VN.json b/public/intl/messages/vi-VN.json
index 254f50705..cc76182d6 100644
--- a/public/intl/messages/vi-VN.json
+++ b/public/intl/messages/vi-VN.json
@@ -41,7 +41,7 @@
"value": "Thêm website"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "Quản trị"
diff --git a/public/intl/messages/zh-CN.json b/public/intl/messages/zh-CN.json
index a66d7db3a..41e748b0d 100644
--- a/public/intl/messages/zh-CN.json
+++ b/public/intl/messages/zh-CN.json
@@ -41,7 +41,7 @@
"value": "添加网站"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "管理员"
diff --git a/public/intl/messages/zh-TW.json b/public/intl/messages/zh-TW.json
index f0455d433..bd9a7d3aa 100644
--- a/public/intl/messages/zh-TW.json
+++ b/public/intl/messages/zh-TW.json
@@ -41,7 +41,7 @@
"value": "新增網站"
}
],
- "label.administrator": [
+ "label.admin": [
{
"type": 0,
"value": "管理員"
diff --git a/src/app/(main)/NavBar.tsx b/src/app/(main)/NavBar.tsx
index 08007b1c4..5e0e3da26 100644
--- a/src/app/(main)/NavBar.tsx
+++ b/src/app/(main)/NavBar.tsx
@@ -14,7 +14,7 @@ import styles from './NavBar.module.css';
export function NavBar() {
const { formatMessage, labels } = useMessages();
const { pathname, router } = useNavigation();
- const { renderTeamUrl } = useTeamUrl();
+ const { teamId, renderTeamUrl } = useTeamUrl();
const cloudMode = !!process.env.cloudMode;
@@ -34,25 +34,38 @@ export function NavBar() {
label: formatMessage(labels.settings),
url: renderTeamUrl('/settings'),
children: [
+ ...(teamId
+ ? [
+ {
+ label: formatMessage(labels.team),
+ url: renderTeamUrl('/settings/team'),
+ },
+ ]
+ : []),
{
label: formatMessage(labels.websites),
- url: '/settings/websites',
- },
- {
- label: formatMessage(labels.teams),
- url: '/settings/teams',
- },
- {
- label: formatMessage(labels.users),
- url: '/settings/users',
- },
- {
- label: formatMessage(labels.profile),
- url: '/profile',
+ url: renderTeamUrl('/settings/websites'),
},
+ ...(!teamId
+ ? [
+ {
+ label: formatMessage(labels.teams),
+ url: renderTeamUrl('/settings/teams'),
+ },
+ {
+ label: formatMessage(labels.users),
+ url: '/settings/users',
+ },
+ ]
+ : [
+ {
+ label: formatMessage(labels.members),
+ url: renderTeamUrl('/settings/members'),
+ },
+ ]),
],
},
- cloudMode && {
+ {
label: formatMessage(labels.profile),
url: '/profile',
},
@@ -94,6 +107,7 @@ export function NavBar() {
+
diff --git a/src/app/(main)/UpdateNotice.module.css b/src/app/(main)/UpdateNotice.module.css
index 261a31698..fec0962cd 100644
--- a/src/app/(main)/UpdateNotice.module.css
+++ b/src/app/(main)/UpdateNotice.module.css
@@ -1,14 +1,17 @@
.notice {
position: absolute;
+ display: flex;
+ justify-content: space-between;
+ width: 100%;
max-width: 800px;
gap: 20px;
- margin: 80px auto;
+ margin: 60px auto;
align-self: center;
background: var(--base50);
padding: 20px;
border: 1px solid var(--base300);
border-radius: var(--border-radius);
- z-index: var(--z-index-popup);
+ z-index: 9999;
box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.1);
}
diff --git a/src/app/(main)/UpdateNotice.tsx b/src/app/(main)/UpdateNotice.tsx
index 54ad05c9a..553e1138b 100644
--- a/src/app/(main)/UpdateNotice.tsx
+++ b/src/app/(main)/UpdateNotice.tsx
@@ -4,9 +4,9 @@ import { Button } from 'react-basics';
import { setItem } from 'next-basics';
import useStore, { checkVersion } from 'store/version';
import { REPO_URL, VERSION_CHECK } from 'lib/constants';
-import styles from './UpdateNotice.module.css';
import { useMessages } from 'components/hooks';
import { usePathname } from 'next/navigation';
+import styles from './UpdateNotice.module.css';
export function UpdateNotice({ user, config }) {
const { formatMessage, labels, messages } = useMessages();
@@ -16,8 +16,9 @@ export function UpdateNotice({ user, config }) {
const allowUpdate =
user?.isAdmin &&
!config?.updatesDisabled &&
- !config?.cloudMode &&
!pathname.includes('/share/') &&
+ !process.env.cloudMode &&
+ !process.env.privateMode &&
!dismissed;
const updateCheck = useCallback(() => {
diff --git a/src/app/(main)/profile/DateRangeSetting.module.css b/src/app/(main)/profile/DateRangeSetting.module.css
new file mode 100644
index 000000000..9de13efe9
--- /dev/null
+++ b/src/app/(main)/profile/DateRangeSetting.module.css
@@ -0,0 +1,3 @@
+.field {
+ width: 200px;
+}
diff --git a/src/app/(main)/profile/DateRangeSetting.tsx b/src/app/(main)/profile/DateRangeSetting.tsx
index a1ae7bc79..c57a209a6 100644
--- a/src/app/(main)/profile/DateRangeSetting.tsx
+++ b/src/app/(main)/profile/DateRangeSetting.tsx
@@ -3,6 +3,7 @@ import { Button, Flexbox } from 'react-basics';
import { useDateRange, useMessages } from 'components/hooks';
import { DEFAULT_DATE_RANGE } from 'lib/constants';
import { DateRange } from 'lib/types';
+import styles from './DateRangeSetting.module.css';
export function DateRangeSetting() {
const { formatMessage, labels } = useMessages();
@@ -13,8 +14,9 @@ export function DateRangeSetting() {
const handleReset = () => setDateRange(DEFAULT_DATE_RANGE);
return (
-
+
n.toLowerCase().includes(search.toLowerCase()))
- : listTimeZones();
+ ? timezones.filter(n => n.toLowerCase().includes(search.toLowerCase()))
+ : timezones;
const handleReset = () => saveTimezone(getTimezone());
return (
saveTimezone(value)}
menuProps={{ className: styles.menu }}
allowSearch={true}
onSearch={setSearch}
diff --git a/src/app/(main)/reports/[reportId]/FieldAddForm.module.css b/src/app/(main)/reports/[reportId]/FieldAddForm.module.css
deleted file mode 100644
index 5c5aaa4f0..000000000
--- a/src/app/(main)/reports/[reportId]/FieldAddForm.module.css
+++ /dev/null
@@ -1,38 +0,0 @@
-.menu {
- width: 360px;
- max-height: 300px;
- overflow: auto;
-}
-
-.item {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- border-radius: var(--border-radius);
-}
-
-.item:hover {
- background: var(--base75);
-}
-
-.type {
- color: var(--font-color300);
-}
-
-.selected {
- font-weight: bold;
-}
-
-.popup {
- display: flex;
-}
-
-.filter {
- display: flex;
- flex-direction: column;
- gap: 20px;
-}
-
-.dropdown {
- min-width: 60px;
-}
diff --git a/src/app/(main)/reports/[reportId]/FieldAddForm.tsx b/src/app/(main)/reports/[reportId]/FieldAddForm.tsx
index 9db472d8d..9217ce4df 100644
--- a/src/app/(main)/reports/[reportId]/FieldAddForm.tsx
+++ b/src/app/(main)/reports/[reportId]/FieldAddForm.tsx
@@ -3,9 +3,6 @@ import { createPortal } from 'react-dom';
import { REPORT_PARAMETERS } from 'lib/constants';
import PopupForm from './PopupForm';
import FieldSelectForm from './FieldSelectForm';
-import FieldAggregateForm from './FieldAggregateForm';
-import FieldFilterForm from './FieldFilterForm';
-import styles from './FieldAddForm.module.css';
export function FieldAddForm({
fields = [],
@@ -18,7 +15,11 @@ export function FieldAddForm({
onAdd: (group: string, value: string) => void;
onClose: () => void;
}) {
- const [selected, setSelected] = useState<{ name: string; type: string; value: string }>();
+ const [selected, setSelected] = useState<{
+ name: string;
+ type: string;
+ value: string;
+ }>();
const handleSelect = (value: any) => {
const { type } = value;
@@ -38,14 +39,8 @@ export function FieldAddForm({
};
return createPortal(
-
+
{!selected && }
- {selected && group === REPORT_PARAMETERS.fields && (
-
- )}
- {selected && group === REPORT_PARAMETERS.filters && (
-
- )}
,
document.body,
);
diff --git a/src/app/(main)/reports/[reportId]/FieldFilterEditForm.module.css b/src/app/(main)/reports/[reportId]/FieldFilterEditForm.module.css
new file mode 100644
index 000000000..43a34438c
--- /dev/null
+++ b/src/app/(main)/reports/[reportId]/FieldFilterEditForm.module.css
@@ -0,0 +1,36 @@
+.menu {
+ position: absolute;
+ max-width: 300px;
+ max-height: 210px;
+}
+
+.filter {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+}
+
+.dropdown {
+ min-width: 200px;
+}
+
+.text {
+ min-width: 200px;
+}
+
+.selected {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 8px 16px;
+ white-space: nowrap;
+ min-width: 200px;
+ font-weight: 900;
+ background: var(--base100);
+ border-radius: var(--border-radius);
+ cursor: pointer;
+}
+
+.search {
+ position: relative;
+}
diff --git a/src/app/(main)/reports/[reportId]/FieldFilterEditForm.tsx b/src/app/(main)/reports/[reportId]/FieldFilterEditForm.tsx
new file mode 100644
index 000000000..dc10b724d
--- /dev/null
+++ b/src/app/(main)/reports/[reportId]/FieldFilterEditForm.tsx
@@ -0,0 +1,224 @@
+import { useState, useMemo } from 'react';
+import {
+ Form,
+ FormRow,
+ Item,
+ Flexbox,
+ Dropdown,
+ Button,
+ SearchField,
+ TextField,
+ Text,
+ Icon,
+ Icons,
+ Menu,
+ Loading,
+} 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 {
+ websiteId?: string;
+ name: string;
+ label?: string;
+ type: string;
+ startDate: Date;
+ endDate: Date;
+ operator?: string;
+ defaultValue?: string;
+ onChange?: (filter: { name: string; type: string; operator: string; value: string }) => void;
+ allowFilterSelect?: boolean;
+ isNew?: boolean;
+}
+
+export default function FieldFilterEditForm({
+ websiteId,
+ name,
+ label,
+ type,
+ startDate,
+ endDate,
+ operator: defaultOperator = 'eq',
+ defaultValue = '',
+ onChange,
+ allowFilterSelect = true,
+ isNew,
+}: FieldFilterFormProps) {
+ const { formatMessage, labels } = useMessages();
+ const [operator, setOperator] = useState(defaultOperator);
+ const [value, setValue] = useState(defaultValue);
+ const [showMenu, setShowMenu] = useState(false);
+ const isEquals = isEqualsOperator(operator);
+ const [search, setSearch] = useState('');
+ const [selected, setSelected] = useState(isEquals ? value : '');
+ const { filters } = useFilters();
+ const { formatValue } = useFormat();
+ const { locale } = useLocale();
+ const isDisabled = !operator || (isEquals && !selected) || (!isEquals && !value);
+ const {
+ data: values = [],
+ isLoading,
+ refetch,
+ } = useWebsiteValues({
+ websiteId,
+ type: name,
+ startDate,
+ endDate,
+ search,
+ });
+
+ const formattedValues = useMemo(() => {
+ if (!values) {
+ return {};
+ }
+ const formatted = {};
+ const format = (val: string) => {
+ formatted[val] = formatValue(val, name);
+ return formatted[val];
+ };
+
+ if (values?.length !== 1) {
+ const { compare } = new Intl.Collator(locale, { numeric: true });
+ values.sort((a, b) => compare(formatted[a] ?? format(a), formatted[b] ?? format(b)));
+ } else {
+ format(values[0]);
+ }
+
+ return formatted;
+ }, [formatValue, locale, name, values]);
+
+ const filteredValues = useMemo(() => {
+ return value
+ ? values.filter((n: string | number) =>
+ formattedValues[n].toLowerCase().includes(value.toLowerCase()),
+ )
+ : values;
+ }, [value, formattedValues]);
+
+ const renderFilterValue = (value: any) => {
+ return filters.find((filter: { value: any }) => filter.value === value)?.label;
+ };
+
+ const handleAdd = () => {
+ onChange({ name, type, operator, value: isEquals ? selected : value });
+ };
+
+ const handleMenuSelect = (value: string) => {
+ setSelected(value);
+ setShowMenu(false);
+ };
+
+ const handleSearch = (value: string) => {
+ setSearch(value);
+ };
+
+ const handleReset = () => {
+ setSelected('');
+ setValue('');
+ setSearch('');
+ refetch();
+ };
+
+ const handleOperatorChange = (value: any) => {
+ setOperator(value);
+
+ if ([OPERATORS.equals, OPERATORS.notEquals].includes(value)) {
+ setValue('');
+ } else {
+ setSelected('');
+ }
+ };
+
+ const handleBlur = () => {
+ window.setTimeout(() => setShowMenu(false), 500);
+ };
+
+ return (
+
+ );
+}
+
+const ResultsMenu = ({ values, type, isLoading, onSelect }) => {
+ const { formatValue } = useFormat();
+ if (isLoading) {
+ return (
+
+ );
+ }
+
+ if (!values?.length) {
+ return null;
+ }
+
+ return (
+
+ );
+};
diff --git a/src/app/(main)/reports/[reportId]/FieldFilterForm.module.css b/src/app/(main)/reports/[reportId]/FieldFilterForm.module.css
deleted file mode 100644
index be7bb9546..000000000
--- a/src/app/(main)/reports/[reportId]/FieldFilterForm.module.css
+++ /dev/null
@@ -1,24 +0,0 @@
-.popup {
- display: flex;
- max-width: 300px;
- max-height: 210px;
- overflow-x: hidden;
-}
-
-.popup > div {
- overflow-y: auto;
-}
-
-.filter {
- display: flex;
- flex-direction: column;
- gap: 20px;
-}
-
-.dropdown {
- min-width: 180px;
-}
-
-.text {
- min-width: 180px;
-}
diff --git a/src/app/(main)/reports/[reportId]/FieldFilterForm.tsx b/src/app/(main)/reports/[reportId]/FieldFilterForm.tsx
deleted file mode 100644
index e38f3d656..000000000
--- a/src/app/(main)/reports/[reportId]/FieldFilterForm.tsx
+++ /dev/null
@@ -1,121 +0,0 @@
-import { useState, useMemo } from 'react';
-import {
- Form,
- FormRow,
- Item,
- Flexbox,
- Dropdown,
- Button,
- TextField,
- Menu,
- Popup,
- PopupTrigger,
-} from 'react-basics';
-import { useMessages, useFilters, useFormat, useLocale } from 'components/hooks';
-import { safeDecodeURIComponent } from 'next-basics';
-import { OPERATORS } from 'lib/constants';
-import styles from './FieldFilterForm.module.css';
-
-export interface FieldFilterFormProps {
- name: string;
- label?: string;
- type: string;
- values?: any[];
- onSelect?: (key: any) => void;
- allowFilterSelect?: boolean;
-}
-
-export default function FieldFilterForm({
- name,
- label,
- type,
- values,
- onSelect,
- allowFilterSelect = true,
-}: FieldFilterFormProps) {
- const { formatMessage, labels } = useMessages();
- const [filter, setFilter] = useState('eq');
- const [value, setValue] = useState('');
- const { getFilters } = useFilters();
- const { formatValue } = useFormat();
- const { locale } = useLocale();
- const filters = getFilters(type);
-
- const formattedValues = useMemo(() => {
- const formatted = {};
- const format = (val: string) => {
- formatted[val] = formatValue(val, name);
- return formatted[val];
- };
- if (values.length !== 1) {
- const { compare } = new Intl.Collator(locale, { numeric: true });
- values.sort((a, b) => compare(formatted[a] ?? format(a), formatted[b] ?? format(b)));
- } else {
- format(values[0]);
- }
- return formatted;
- }, [formatValue, locale, name, values]);
-
- const filteredValues = useMemo(() => {
- return value ? values.filter(n => n.includes(value)) : values;
- }, [value, formattedValues]);
-
- const renderFilterValue = value => {
- return filters.find(f => f.value === value)?.label;
- };
-
- const handleAdd = () => {
- onSelect({ name, type, filter, value });
- };
-
- const handleMenuSelect = value => {
- setValue(value);
- };
-
- const showMenu =
- [OPERATORS.equals, OPERATORS.notEquals].includes(filter as any) &&
- !(filteredValues.length === 1 && filteredValues[0] === value);
-
- return (
-
- );
-}
diff --git a/src/app/(main)/reports/insights/InsightsFieldParameters.tsx b/src/app/(main)/reports/[reportId]/FieldParameters.tsx
similarity index 64%
rename from src/app/(main)/reports/insights/InsightsFieldParameters.tsx
rename to src/app/(main)/reports/[reportId]/FieldParameters.tsx
index 798a828cd..36cfbda9b 100644
--- a/src/app/(main)/reports/insights/InsightsFieldParameters.tsx
+++ b/src/app/(main)/reports/[reportId]/FieldParameters.tsx
@@ -1,30 +1,18 @@
-import { useMessages } from 'components/hooks';
+import { useFields, useMessages } from 'components/hooks';
import Icons from 'components/icons';
import { useContext } from 'react';
import { Button, FormRow, Icon, Popup, PopupTrigger } from 'react-basics';
import FieldSelectForm from '../[reportId]/FieldSelectForm';
import ParameterList from '../[reportId]/ParameterList';
import PopupForm from '../[reportId]/PopupForm';
-import { ReportContext } from '../[reportId]/Report';
+import { ReportContext } from './Report';
-export function InsightsFieldParameters() {
+export function FieldParameters() {
const { report, updateReport } = useContext(ReportContext);
const { formatMessage, labels } = useMessages();
const { parameters } = report || {};
const { fields } = parameters || {};
-
- const fieldOptions = [
- { name: 'url', type: 'string', label: formatMessage(labels.url) },
- { name: 'title', type: 'string', label: formatMessage(labels.pageTitle) },
- { name: 'referrer', type: 'string', label: formatMessage(labels.referrer) },
- { name: 'query', type: 'string', label: formatMessage(labels.query) },
- { name: 'browser', type: 'string', label: formatMessage(labels.browser) },
- { name: 'os', type: 'string', label: formatMessage(labels.os) },
- { name: 'device', type: 'string', label: formatMessage(labels.device) },
- { name: 'country', type: 'string', label: formatMessage(labels.country) },
- { name: 'region', type: 'string', label: formatMessage(labels.region) },
- { name: 'city', type: 'string', label: formatMessage(labels.city) },
- ];
+ const { fields: fieldOptions } = useFields();
const handleAdd = (value: { name: any }) => {
if (!fields.find(({ name }) => name === value.name)) {
@@ -72,4 +60,4 @@ export function InsightsFieldParameters() {
);
}
-export default InsightsFieldParameters;
+export default FieldParameters;
diff --git a/src/app/(main)/reports/insights/InsightsFilterParameters.module.css b/src/app/(main)/reports/[reportId]/FilterParameters.module.css
similarity index 94%
rename from src/app/(main)/reports/insights/InsightsFilterParameters.module.css
rename to src/app/(main)/reports/[reportId]/FilterParameters.module.css
index 8b1795d21..939d0652d 100644
--- a/src/app/(main)/reports/insights/InsightsFilterParameters.module.css
+++ b/src/app/(main)/reports/[reportId]/FilterParameters.module.css
@@ -15,7 +15,7 @@
white-space: nowrap;
}
-.filter {
+.op {
color: var(--blue900);
background-color: var(--blue100);
font-size: 12px;
@@ -34,3 +34,7 @@
border-radius: 5px;
white-space: nowrap;
}
+
+.edit {
+ margin-top: 20px;
+}
diff --git a/src/app/(main)/reports/[reportId]/FilterParameters.tsx b/src/app/(main)/reports/[reportId]/FilterParameters.tsx
new file mode 100644
index 000000000..3118a6f4b
--- /dev/null
+++ b/src/app/(main)/reports/[reportId]/FilterParameters.tsx
@@ -0,0 +1,136 @@
+import { useContext } from 'react';
+import { useMessages, useFormat, useFilters, useFields } from 'components/hooks';
+import Icons from 'components/icons';
+import { Button, FormRow, Icon, Popup, PopupTrigger } from 'react-basics';
+import FilterSelectForm from '../[reportId]/FilterSelectForm';
+import ParameterList from '../[reportId]/ParameterList';
+import PopupForm from '../[reportId]/PopupForm';
+import { ReportContext } from './Report';
+import FieldFilterEditForm from '../[reportId]/FieldFilterEditForm';
+import { isSearchOperator } from 'lib/params';
+import styles from './FilterParameters.module.css';
+
+export function FilterParameters() {
+ const { report, updateReport } = useContext(ReportContext);
+ const { formatMessage, labels } = useMessages();
+ const { formatValue } = useFormat();
+ const { parameters } = report || {};
+ const { websiteId, filters, dateRange } = parameters || {};
+ const { fields } = useFields();
+
+ const handleAdd = (value: { name: any }) => {
+ if (!filters.find(({ name }) => name === value.name)) {
+ updateReport({ parameters: { filters: filters.concat(value) } });
+ }
+ };
+
+ const handleRemove = (name: string) => {
+ updateReport({ parameters: { filters: filters.filter(f => f.name !== name) } });
+ };
+
+ const handleChange = (close: () => void, filter: { name: any }) => {
+ updateReport({
+ parameters: {
+ filters: filters.map(f => {
+ if (filter.name === f.name) {
+ return filter;
+ }
+ return f;
+ }),
+ },
+ });
+ close();
+ };
+
+ const AddButton = () => {
+ return (
+
+
+
+
+ !filters.find(f => f.name === name))}
+ onChange={handleAdd}
+ />
+
+
+
+ );
+ };
+
+ return (
+ }>
+
+ {filters.map(
+ ({ name, operator, value }: { name: string; operator: string; value: string }) => {
+ const label = fields.find(f => f.name === name)?.label;
+ const isSearch = isSearchOperator(operator);
+
+ return (
+ handleRemove(name)}>
+
+
+ );
+ },
+ )}
+
+
+ );
+}
+
+const FilterParameter = ({
+ websiteId,
+ name,
+ label,
+ operator,
+ value,
+ type = 'string',
+ startDate,
+ endDate,
+ onChange,
+}) => {
+ const { operatorLabels } = useFilters();
+
+ return (
+
+
+
{label}
+
{operatorLabels[operator]}
+
{value}
+
+
+ {(close: any) => (
+
+
+
+ )}
+
+
+ );
+};
+
+export default FilterParameters;
diff --git a/src/app/(main)/reports/[reportId]/FilterSelectForm.tsx b/src/app/(main)/reports/[reportId]/FilterSelectForm.tsx
index 3d209fc14..b81c85767 100644
--- a/src/app/(main)/reports/[reportId]/FilterSelectForm.tsx
+++ b/src/app/(main)/reports/[reportId]/FilterSelectForm.tsx
@@ -1,59 +1,41 @@
import { useState } from 'react';
-import { Loading } from 'react-basics';
-import { subDays } from 'date-fns';
import FieldSelectForm from './FieldSelectForm';
-import FieldFilterForm from './FieldFilterForm';
-import { useApi } from 'components/hooks';
-
-function useValues(websiteId: string, type: string) {
- const now = Date.now();
- const { get, useQuery } = useApi();
- const { data, error, isLoading } = useQuery({
- queryKey: ['websites:values', websiteId, type],
- queryFn: () =>
- get(`/websites/${websiteId}/values`, {
- type,
- startAt: +subDays(now, 90),
- endAt: now,
- }),
- enabled: !!(websiteId && type),
- });
-
- return { data, error, isLoading };
-}
+import FieldFilterEditForm from './FieldFilterEditForm';
+import { useDateRange } from 'components/hooks';
export interface FilterSelectFormProps {
- websiteId: string;
+ websiteId?: string;
fields: any[];
- onSelect?: (key: any) => void;
+ onChange?: (filter: { name: string; type: string; operator: string; value: string }) => void;
allowFilterSelect?: boolean;
}
export default function FilterSelectForm({
websiteId,
fields,
- onSelect,
+ onChange,
allowFilterSelect,
}: FilterSelectFormProps) {
const [field, setField] = useState<{ name: string; label: string; type: string }>();
- const { data, isLoading } = useValues(websiteId, field?.name);
+ const [{ startDate, endDate }] = useDateRange(websiteId);
if (!field) {
return ;
}
- if (isLoading) {
- return ;
- }
+ const { name, label, type } = field;
return (
-
);
}
diff --git a/src/app/(main)/reports/[reportId]/ParameterList.tsx b/src/app/(main)/reports/[reportId]/ParameterList.tsx
index 7fe123818..f2ac988fc 100644
--- a/src/app/(main)/reports/[reportId]/ParameterList.tsx
+++ b/src/app/(main)/reports/[reportId]/ParameterList.tsx
@@ -4,6 +4,7 @@ import Icons from 'components/icons';
import Empty from 'components/common/Empty';
import { useMessages } from 'components/hooks';
import styles from './ParameterList.module.css';
+import classNames from 'classnames';
export interface ParameterListProps {
children?: ReactNode;
@@ -20,9 +21,19 @@ export function ParameterList({ children }: ParameterListProps) {
);
}
-const Item = ({ children, onRemove }: { children?: ReactNode; onRemove?: () => void }) => {
+const Item = ({
+ children,
+ className,
+ onClick,
+ onRemove,
+}: {
+ children?: ReactNode;
+ className?: string;
+ onClick?: () => void;
+ onRemove?: () => void;
+}) => {
return (
-
+
{children}
diff --git a/src/app/(main)/reports/[reportId]/PopupForm.module.css b/src/app/(main)/reports/[reportId]/PopupForm.module.css
index 94d98b38c..5d069dd46 100644
--- a/src/app/(main)/reports/[reportId]/PopupForm.module.css
+++ b/src/app/(main)/reports/[reportId]/PopupForm.module.css
@@ -1,5 +1,4 @@
.form {
- position: absolute;
background: var(--base50);
min-width: 300px;
padding: 20px;
diff --git a/src/app/(main)/reports/[reportId]/Report.tsx b/src/app/(main)/reports/[reportId]/Report.tsx
index 76f735952..d6de9d425 100644
--- a/src/app/(main)/reports/[reportId]/Report.tsx
+++ b/src/app/(main)/reports/[reportId]/Report.tsx
@@ -13,7 +13,7 @@ export function Report({
className,
}: {
reportId: string;
- defaultParameters: { [key: string]: any };
+ defaultParameters: { type: string; parameters: { [key: string]: any } };
children: ReactNode;
className?: string;
}) {
diff --git a/src/app/(main)/reports/event-data/EventDataParameters.tsx b/src/app/(main)/reports/event-data/EventDataParameters.tsx
index efa9fb675..adc182748 100644
--- a/src/app/(main)/reports/event-data/EventDataParameters.tsx
+++ b/src/app/(main)/reports/event-data/EventDataParameters.tsx
@@ -60,10 +60,9 @@ export function EventDataParameters() {
}
};
- const handleRemove = (group: string, index: number) => {
+ const handleRemove = (group: string) => {
const data = [...parameterData[group]];
- data.splice(index, 1);
- updateReport({ parameters: { [group]: data } });
+ updateReport({ parameters: { [group]: data.filter(({ name }) => name !== group) } });
};
const AddButton = ({ group, onAdd }) => {
@@ -104,29 +103,28 @@ export function EventDataParameters() {
label={label}
action={}
>
- handleRemove(group, index)}
- >
- {({ name, value }) => {
+
+ {parameterData[group].map(({ name, value }) => {
return (
-
- {group === REPORT_PARAMETERS.fields && (
- <>
-
{name}
-
{value}
- >
- )}
- {group === REPORT_PARAMETERS.filters && (
- <>
-
{name}
-
{value[0]}
-
{value[1]}
- >
- )}
-
+ handleRemove(group)}>
+
+ {group === REPORT_PARAMETERS.fields && (
+ <>
+
{name}
+
{value}
+ >
+ )}
+ {group === REPORT_PARAMETERS.filters && (
+ <>
+
{name}
+
{value[0]}
+
{value[1]}
+ >
+ )}
+
+
);
- }}
+ })}
);
diff --git a/src/app/(main)/reports/funnel/FunnelChart.module.css b/src/app/(main)/reports/funnel/FunnelChart.module.css
index 0279ea038..81b22c787 100644
--- a/src/app/(main)/reports/funnel/FunnelChart.module.css
+++ b/src/app/(main)/reports/funnel/FunnelChart.module.css
@@ -37,12 +37,12 @@
.card {
display: grid;
gap: 20px;
+ margin-top: 14px;
}
.header {
display: flex;
- align-items: center;
- font-weight: 700;
+ flex-direction: column;
gap: 20px;
}
@@ -51,19 +51,16 @@
align-items: center;
justify-content: flex-end;
background: var(--base900);
- height: 50px;
+ height: 30px;
border-radius: 5px;
overflow: hidden;
position: relative;
}
.label {
- color: var(--base700);
-}
-
-.value {
- color: var(--base50);
- margin-inline-end: 20px;
+ color: var(--base600);
+ font-weight: 700;
+ text-transform: uppercase;
}
.track {
@@ -72,13 +69,33 @@
}
.info {
- display: flex;
- justify-content: space-between;
text-transform: lowercase;
}
.item {
- padding: 6px 10px;
- border-radius: 4px;
- border: 1px solid var(--base300);
+ font-size: 20px;
+ color: var(--base900);
+ font-weight: 700;
+}
+
+.metric {
+ color: var(--base700);
+ display: flex;
+ justify-content: space-between;
+ gap: 10px;
+ margin: 10px 0;
+ text-transform: lowercase;
+}
+
+.visitors {
+ color: var(--base900);
+ font-size: 24px;
+ font-weight: 900;
+ margin-right: 10px;
+}
+
+.percent {
+ font-size: 20px;
+ font-weight: 700;
+ align-self: flex-end;
}
diff --git a/src/app/(main)/reports/funnel/FunnelChart.tsx b/src/app/(main)/reports/funnel/FunnelChart.tsx
index 6207a1777..0da71d6f5 100644
--- a/src/app/(main)/reports/funnel/FunnelChart.tsx
+++ b/src/app/(main)/reports/funnel/FunnelChart.tsx
@@ -2,8 +2,8 @@ import { useContext } from 'react';
import classNames from 'classnames';
import { useMessages } from 'components/hooks';
import { ReportContext } from '../[reportId]/Report';
-import styles from './FunnelChart.module.css';
import { formatLongNumber } from 'lib/format';
+import styles from './FunnelChart.module.css';
export interface FunnelChartProps {
className?: string;
@@ -18,35 +18,33 @@ export function FunnelChart({ className }: FunnelChartProps) {
return (
- {data?.map(({ url, visitors, dropped, dropoff, remaining }, index: number) => {
+ {data?.map(({ type, value, visitors, dropped, dropoff, remaining }, index: number) => {
return (
-
+
{index + 1}
- {formatMessage(labels.viewedPage)}:
- {url}
+
+ {formatMessage(type === 'url' ? labels.viewedPage : labels.triggeredEvent)}
+
+ {value}
+
+
+
+ {formatLongNumber(visitors)}
+ {formatMessage(labels.visitors)}
+
+
{(remaining * 100).toFixed(2)}%
-
-
- {remaining > 0.1 && `${(remaining * 100).toFixed(2)}%`}
-
-
+
-
-
-
{formatLongNumber(visitors)}
-
{formatMessage(labels.visitors)}
-
({(remaining * 100).toFixed(2)}%)
+ {dropoff > 0 && (
+
+ {formatLongNumber(dropped)} {formatMessage(labels.visitorsDroppedOff)} (
+ {(dropoff * 100).toFixed(2)}%)
- {dropoff > 0 && (
-
- {formatLongNumber(dropped)} {formatMessage(labels.visitorsDroppedOff)} (
- {(dropoff * 100).toFixed(2)}%)
-
- )}
-
+ )}
);
diff --git a/src/app/(main)/reports/funnel/FunnelParameters.module.css b/src/app/(main)/reports/funnel/FunnelParameters.module.css
new file mode 100644
index 000000000..219b98076
--- /dev/null
+++ b/src/app/(main)/reports/funnel/FunnelParameters.module.css
@@ -0,0 +1,16 @@
+.item {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ width: 100%;
+}
+
+.type {
+ color: var(--base700);
+}
+
+.value {
+ display: flex;
+ align-self: center;
+ gap: 20px;
+}
diff --git a/src/app/(main)/reports/funnel/FunnelParameters.tsx b/src/app/(main)/reports/funnel/FunnelParameters.tsx
index 6eefbaae4..7b1fb0c81 100644
--- a/src/app/(main)/reports/funnel/FunnelParameters.tsx
+++ b/src/app/(main)/reports/funnel/FunnelParameters.tsx
@@ -10,50 +10,65 @@ import {
Popup,
SubmitButton,
TextField,
+ Button,
} from 'react-basics';
import Icons from 'components/icons';
-import UrlAddForm from './UrlAddForm';
+import FunnelStepAddForm from './FunnelStepAddForm';
import { ReportContext } from '../[reportId]/Report';
import BaseParameters from '../[reportId]/BaseParameters';
import ParameterList from '../[reportId]/ParameterList';
import PopupForm from '../[reportId]/PopupForm';
+import styles from './FunnelParameters.module.css';
export function FunnelParameters() {
const { report, runReport, updateReport, isRunning } = useContext(ReportContext);
const { formatMessage, labels } = useMessages();
const { id, parameters } = report || {};
- const { websiteId, dateRange, urls } = parameters || {};
- const queryDisabled = !websiteId || !dateRange || urls?.length < 2;
+ const { websiteId, dateRange, steps } = parameters || {};
+ const queryDisabled = !websiteId || !dateRange || steps?.length < 2;
const handleSubmit = (data: any, e: any) => {
e.stopPropagation();
e.preventDefault();
+
if (!queryDisabled) {
runReport(data);
}
};
- const handleAddUrl = (url: string) => {
- updateReport({ parameters: { urls: parameters.urls.concat(url) } });
+ const handleAddStep = (step: { type: string; value: string }) => {
+ updateReport({ parameters: { steps: parameters.steps.concat(step) } });
};
- const handleRemoveUrl = (index: number, e: any) => {
- e.stopPropagation();
- const urls = [...parameters.urls];
- urls.splice(index, 1);
- updateReport({ parameters: { urls } });
+ const handleUpdateStep = (
+ close: () => void,
+ index: number,
+ step: { type: string; value: string },
+ ) => {
+ const steps = [...parameters.steps];
+ steps[index] = step;
+ updateReport({ parameters: { steps } });
+ close();
};
- const AddUrlButton = () => {
+ const handleRemoveStep = (index: number) => {
+ const steps = [...parameters.steps];
+ delete steps[index];
+ updateReport({ parameters: { steps: steps.filter(n => n) } });
+ };
+
+ const AddStepButton = () => {
return (
-
-
-
-
+
+
-
+
@@ -71,11 +86,37 @@ export function FunnelParameters() {
-
}>
-
handleRemoveUrl(index, e)}
- />
+ }>
+
+ {steps.map((step: { type: string; value: string }, index: number) => {
+ return (
+
+ handleRemoveStep(index)}
+ >
+
+
+ {step.type === 'url' ? : }
+
+
{step.value}
+
+
+
+ {(close: () => void) => (
+
+
+
+ )}
+
+
+ );
+ })}
+
diff --git a/src/app/(main)/reports/funnel/FunnelReport.tsx b/src/app/(main)/reports/funnel/FunnelReport.tsx
index 7b9a6677a..850bbd906 100644
--- a/src/app/(main)/reports/funnel/FunnelReport.tsx
+++ b/src/app/(main)/reports/funnel/FunnelReport.tsx
@@ -9,7 +9,7 @@ import { REPORT_TYPES } from 'lib/constants';
const defaultParameters = {
type: REPORT_TYPES.funnel,
- parameters: { window: 60, urls: [] },
+ parameters: { window: 60, steps: [] },
};
export default function FunnelReport({ reportId }: { reportId?: string }) {
diff --git a/src/app/(main)/reports/funnel/FunnelStepAddForm.module.css b/src/app/(main)/reports/funnel/FunnelStepAddForm.module.css
new file mode 100644
index 000000000..a254ff088
--- /dev/null
+++ b/src/app/(main)/reports/funnel/FunnelStepAddForm.module.css
@@ -0,0 +1,7 @@
+.dropdown {
+ width: 140px;
+}
+
+.input {
+ width: 200px;
+}
diff --git a/src/app/(main)/reports/funnel/FunnelStepAddForm.tsx b/src/app/(main)/reports/funnel/FunnelStepAddForm.tsx
new file mode 100644
index 000000000..7d77b0c72
--- /dev/null
+++ b/src/app/(main)/reports/funnel/FunnelStepAddForm.tsx
@@ -0,0 +1,80 @@
+import { useState } from 'react';
+import { useMessages } from 'components/hooks';
+import { Button, FormRow, TextField, Flexbox, Dropdown, Item } from 'react-basics';
+import styles from './FunnelStepAddForm.module.css';
+
+export interface FunnelStepAddFormProps {
+ type?: string;
+ value?: string;
+ onChange?: (step: { type: string; value: string }) => void;
+}
+
+export function FunnelStepAddForm({
+ type: defaultType = 'url',
+ value: defaultValue = '',
+ onChange,
+}: FunnelStepAddFormProps) {
+ const [type, setType] = useState(defaultType);
+ const [value, setValue] = useState(defaultValue);
+ const { formatMessage, labels } = useMessages();
+ const items = [
+ { label: formatMessage(labels.url), value: 'url' },
+ { label: formatMessage(labels.event), value: 'event' },
+ ];
+ const isDisabled = !type || !value;
+
+ const handleSave = () => {
+ onChange({ type, value });
+ setValue('');
+ };
+
+ const handleChange = e => {
+ setValue(e.target.value);
+ };
+
+ const handleKeyDown = e => {
+ if (e.key === 'Enter') {
+ e.stopPropagation();
+ handleSave();
+ }
+ };
+
+ const renderTypeValue = (value: any) => {
+ return items.find(item => item.value === value)?.label;
+ };
+
+ return (
+
+
+
+ setType(value)}
+ >
+ {({ value, label }) => {
+ return - {label}
;
+ }}
+
+
+
+
+
+
+
+
+ );
+}
+
+export default FunnelStepAddForm;
diff --git a/src/app/(main)/reports/funnel/UrlAddForm.module.css b/src/app/(main)/reports/funnel/UrlAddForm.module.css
deleted file mode 100644
index 6a3e03b5e..000000000
--- a/src/app/(main)/reports/funnel/UrlAddForm.module.css
+++ /dev/null
@@ -1,14 +0,0 @@
-.form {
- position: absolute;
- background: var(--base50);
- width: 300px;
- padding: 30px;
- margin-top: 10px;
- border: 1px solid var(--base400);
- border-radius: var(--border-radius);
- box-shadow: 0 0 0 5px rgba(0, 0, 0, 0.1);
-}
-
-.input {
- width: 100%;
-}
diff --git a/src/app/(main)/reports/funnel/UrlAddForm.tsx b/src/app/(main)/reports/funnel/UrlAddForm.tsx
deleted file mode 100644
index 88c27ae91..000000000
--- a/src/app/(main)/reports/funnel/UrlAddForm.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import { useState } from 'react';
-import { useMessages } from 'components/hooks';
-import { Button, Form, FormRow, TextField, Flexbox } from 'react-basics';
-import styles from './UrlAddForm.module.css';
-
-export interface UrlAddFormProps {
- defaultValue?: string;
- onAdd?: (url: string) => void;
-}
-
-export function UrlAddForm({ defaultValue = '', onAdd }: UrlAddFormProps) {
- const [url, setUrl] = useState(defaultValue);
- const { formatMessage, labels } = useMessages();
-
- const handleSave = () => {
- onAdd(url);
- setUrl('');
- };
-
- const handleChange = e => {
- setUrl(e.target.value);
- };
-
- const handleKeyDown = e => {
- if (e.key === 'Enter') {
- e.stopPropagation();
- handleSave();
- }
- };
-
- return (
-
- );
-}
-
-export default UrlAddForm;
diff --git a/src/app/(main)/reports/insights/InsightsFilterParameters.tsx b/src/app/(main)/reports/insights/InsightsFilterParameters.tsx
deleted file mode 100644
index 47554469c..000000000
--- a/src/app/(main)/reports/insights/InsightsFilterParameters.tsx
+++ /dev/null
@@ -1,88 +0,0 @@
-import { useMessages, useFormat, useFilters } from 'components/hooks';
-import Icons from 'components/icons';
-import { useContext } from 'react';
-import { Button, FormRow, Icon, Popup, PopupTrigger } from 'react-basics';
-import FilterSelectForm from '../[reportId]/FilterSelectForm';
-import ParameterList from '../[reportId]/ParameterList';
-import PopupForm from '../[reportId]/PopupForm';
-import { ReportContext } from '../[reportId]/Report';
-import styles from './InsightsFilterParameters.module.css';
-import { safeDecodeURIComponent } from 'next-basics';
-import { OPERATORS } from 'lib/constants';
-
-export function InsightsFilterParameters() {
- const { report, updateReport } = useContext(ReportContext);
- const { formatMessage, labels } = useMessages();
- const { formatValue } = useFormat();
- const { filterLabels } = useFilters();
- const { parameters } = report || {};
- const { websiteId, filters } = parameters || {};
-
- const fieldOptions = [
- { name: 'url', type: 'string', label: formatMessage(labels.url) },
- { name: 'title', type: 'string', label: formatMessage(labels.pageTitle) },
- { name: 'referrer', type: 'string', label: formatMessage(labels.referrer) },
- { name: 'query', type: 'string', label: formatMessage(labels.query) },
- { name: 'browser', type: 'string', label: formatMessage(labels.browser) },
- { name: 'os', type: 'string', label: formatMessage(labels.os) },
- { name: 'device', type: 'string', label: formatMessage(labels.device) },
- { name: 'country', type: 'string', label: formatMessage(labels.country) },
- { name: 'region', type: 'string', label: formatMessage(labels.region) },
- { name: 'city', type: 'string', label: formatMessage(labels.city) },
- ];
-
- const handleAdd = (value: { name: any }) => {
- if (!filters.find(({ name }) => name === value.name)) {
- updateReport({ parameters: { filters: filters.concat(value) } });
- }
- };
-
- const handleRemove = (name: string) => {
- updateReport({ parameters: { filters: filters.filter(f => f.name !== name) } });
- };
-
- const AddButton = () => {
- return (
-
-
-
-
- !filters.find(f => f.name === name))}
- onSelect={handleAdd}
- />
-
-
-
- );
- };
-
- return (
- }>
-
- {filters.map(({ name, filter, value }) => {
- const label = fieldOptions.find(f => f.name === name)?.label;
- const isEquals = [OPERATORS.equals, OPERATORS.notEquals].includes(filter);
- return (
- handleRemove(name)}>
-
-
{label}
-
{filterLabels[filter]}
-
- {safeDecodeURIComponent(isEquals ? formatValue(value, name) : value)}
-
-
-
- );
- })}
-
-
- );
-}
-
-export default InsightsFilterParameters;
diff --git a/src/app/(main)/reports/insights/InsightsParameters.tsx b/src/app/(main)/reports/insights/InsightsParameters.tsx
index 22c57ff0c..7f58de6a4 100644
--- a/src/app/(main)/reports/insights/InsightsParameters.tsx
+++ b/src/app/(main)/reports/insights/InsightsParameters.tsx
@@ -3,8 +3,8 @@ import { useContext } from 'react';
import { Form, FormButtons, SubmitButton } from 'react-basics';
import BaseParameters from '../[reportId]/BaseParameters';
import { ReportContext } from '../[reportId]/Report';
-import InsightsFieldParameters from './InsightsFieldParameters';
-import InsightsFilterParameters from './InsightsFilterParameters';
+import FieldParameters from '../[reportId]/FieldParameters';
+import FilterParameters from '../[reportId]/FilterParameters';
export function InsightsParameters() {
const { report, runReport, isRunning } = useContext(ReportContext);
@@ -22,8 +22,8 @@ export function InsightsParameters() {
return (