diff --git a/db/mysql/migrations/11_add_segment/migration.sql b/db/mysql/migrations/11_add_segment/migration.sql
new file mode 100644
index 00000000..c79e916d
--- /dev/null
+++ b/db/mysql/migrations/11_add_segment/migration.sql
@@ -0,0 +1,14 @@
+-- CreateTable
+CREATE TABLE `segment` (
+ `segment_id` VARCHAR(36) NOT NULL,
+ `website_id` VARCHAR(36) NOT NULL,
+ `type` VARCHAR(200) NOT NULL,
+ `name` VARCHAR(200) NOT NULL,
+ `parameters` JSON NOT NULL,
+ `created_at` TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP(0),
+ `updated_at` TIMESTAMP(0) NULL,
+
+ UNIQUE INDEX `segment_segment_id_key`(`segment_id`),
+ INDEX `segment_website_id_idx`(`website_id`),
+ PRIMARY KEY (`segment_id`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
diff --git a/db/mysql/migrations/12_update_report_parameter/migration.sql b/db/mysql/migrations/12_update_report_parameter/migration.sql
new file mode 100644
index 00000000..f6a99c3f
--- /dev/null
+++ b/db/mysql/migrations/12_update_report_parameter/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE `report` MODIFY `parameters` JSON NOT NULL;
diff --git a/db/mysql/migrations/13_add_revenue/migration.sql b/db/mysql/migrations/13_add_revenue/migration.sql
new file mode 100644
index 00000000..96115a33
--- /dev/null
+++ b/db/mysql/migrations/13_add_revenue/migration.sql
@@ -0,0 +1,18 @@
+-- CreateTable
+CREATE TABLE `revenue` (
+ `revenue_id` VARCHAR(36) NOT NULL,
+ `website_id` VARCHAR(36) NOT NULL,
+ `session_id` VARCHAR(36) NOT NULL,
+ `event_id` VARCHAR(36) NOT NULL,
+ `event_name` VARCHAR(50) NOT NULL,
+ `currency` VARCHAR(100) NOT NULL,
+ `revenue` DECIMAL(19, 4) NULL,
+ `created_at` TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP(0),
+
+ UNIQUE INDEX `revenue_revenue_id_key`(`revenue_id`),
+ INDEX `revenue_website_id_idx`(`website_id`),
+ INDEX `revenue_session_id_idx`(`session_id`),
+ INDEX `revenue_website_id_created_at_idx`(`website_id`, `created_at`),
+ INDEX `revenue_website_id_session_id_created_at_idx`(`website_id`, `session_id`, `created_at`),
+ PRIMARY KEY (`revenue_id`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
diff --git a/db/mysql/schema.prisma b/db/mysql/schema.prisma
index 2a5513ab..67bd24d2 100644
--- a/db/mysql/schema.prisma
+++ b/db/mysql/schema.prisma
@@ -43,6 +43,7 @@ model Session {
websiteEvent WebsiteEvent[]
sessionData SessionData[]
+ revenue Revenue[]
@@index([createdAt])
@@index([websiteId])
@@ -76,7 +77,9 @@ model Website {
team Team? @relation(fields: [teamId], references: [id])
eventData EventData[]
report Report[]
+ revenue Revenue[]
sessionData SessionData[]
+ segment Segment[]
@@index([userId])
@@index([teamId])
@@ -215,10 +218,10 @@ model Report {
id String @id() @unique() @map("report_id") @db.VarChar(36)
userId String @map("user_id") @db.VarChar(36)
websiteId String @map("website_id") @db.VarChar(36)
- type String @map("type") @db.VarChar(200)
- name String @map("name") @db.VarChar(200)
- description String @map("description") @db.VarChar(500)
- parameters String @map("parameters") @db.VarChar(6000)
+ type String @db.VarChar(200)
+ name String @db.VarChar(200)
+ description String @db.VarChar(500)
+ parameters Json
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamp(0)
@@ -231,3 +234,38 @@ model Report {
@@index([name])
@@map("report")
}
+
+model Segment {
+ id String @id() @unique() @map("segment_id") @db.VarChar(36)
+ websiteId String @map("website_id") @db.VarChar(36)
+ type String @db.VarChar(200)
+ name String @db.VarChar(200)
+ parameters Json
+ createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
+ updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamp(0)
+
+ website Website @relation(fields: [websiteId], references: [id])
+
+ @@index([websiteId])
+ @@map("segment")
+}
+
+model Revenue {
+ id String @id() @unique() @map("revenue_id") @db.VarChar(36)
+ websiteId String @map("website_id") @db.VarChar(36)
+ sessionId String @map("session_id") @db.VarChar(36)
+ eventId String @map("event_id") @db.VarChar(36)
+ eventName String @map("event_name") @db.VarChar(50)
+ currency String @db.VarChar(100)
+ revenue Decimal? @db.Decimal(19, 4)
+ createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
+
+ website Website @relation(fields: [websiteId], references: [id])
+ session Session @relation(fields: [sessionId], references: [id])
+
+ @@index([websiteId])
+ @@index([sessionId])
+ @@index([websiteId, createdAt])
+ @@index([websiteId, sessionId, createdAt])
+ @@map("revenue")
+}
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a25a6594..bd8c46f0 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -248,7 +248,7 @@ importers:
version: 14.2.30(eslint@8.57.1)(typescript@5.8.3)
eslint-config-prettier:
specifier: ^8.5.0
- version: 8.10.0(eslint@8.57.1)
+ version: 8.10.1(eslint@8.57.1)
eslint-import-resolver-alias:
specifier: ^1.1.2
version: 1.1.2(eslint-plugin-import@2.32.0)
@@ -266,7 +266,7 @@ importers:
version: 27.9.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(jest@29.7.0(@types/node@22.16.4)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3)))(typescript@5.8.3)
eslint-plugin-prettier:
specifier: ^4.0.0
- version: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.1))(eslint@8.57.1)(prettier@2.8.8)
+ version: 4.2.3(eslint-config-prettier@8.10.1(eslint@8.57.1))(eslint@8.57.1)(prettier@2.8.8)
extract-react-intl-messages:
specifier: ^4.1.1
version: 4.1.1(ts-jest@29.4.0(@babel/core@7.28.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.0))(esbuild@0.25.6)(jest-util@29.7.0)(jest@29.7.0(@types/node@22.16.4)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3)))(typescript@5.8.3))
@@ -1308,14 +1308,14 @@ packages:
peerDependencies:
'@dicebear/core': ^9.0.0
- '@emnapi/core@1.4.4':
- resolution: {integrity: sha512-A9CnAbC6ARNMKcIcrQwq6HeHCjpcBZ5wSx4U01WXCqEKlrzB9F9315WDNHkrs2xbx7YjjSxbUYxuN6EQzpcY2g==}
+ '@emnapi/core@1.4.5':
+ resolution: {integrity: sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==}
- '@emnapi/runtime@1.4.4':
- resolution: {integrity: sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==}
+ '@emnapi/runtime@1.4.5':
+ resolution: {integrity: sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==}
- '@emnapi/wasi-threads@1.0.3':
- resolution: {integrity: sha512-8K5IFFsQqF9wQNJptGbS6FNKgUTsSRYnTqNCG1vPP8jFdjSv18n2mQfJpkt2Oibo9iBEzcDnDxNwKTzC7svlJw==}
+ '@emnapi/wasi-threads@1.0.4':
+ resolution: {integrity: sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==}
'@esbuild/aix-ppc64@0.25.6':
resolution: {integrity: sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==}
@@ -3377,8 +3377,8 @@ packages:
engines: {node: '>=0.10.0'}
hasBin: true
- electron-to-chromium@1.5.186:
- resolution: {integrity: sha512-lur7L4BFklgepaJxj4DqPk7vKbTEl0pajNlg2QjE5shefmlmBLm2HvQ7PMf1R/GvlevT/581cop33/quQcfX3A==}
+ electron-to-chromium@1.5.187:
+ resolution: {integrity: sha512-cl5Jc9I0KGUoOoSbxvTywTa40uspGJt/BDBoDLoxJRSBpWh4FFXBsjNRHfQrONsV/OoEjDfHUmZQa2d6Ze4YgA==}
emittery@0.13.1:
resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==}
@@ -3477,8 +3477,8 @@ packages:
typescript:
optional: true
- eslint-config-prettier@8.10.0:
- resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==}
+ eslint-config-prettier@8.10.1:
+ resolution: {integrity: sha512-mXi3I2ghYZv02pKsUS5C2IRcYlOs3WFNYzYtLKX5s9mFju7BIAjxJqA4UG2qN2DAC0yUECdnfs5iGnyUdgOWzA==}
hasBin: true
peerDependencies:
eslint: '>=7.0.0'
@@ -3566,8 +3566,8 @@ packages:
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9
- eslint-plugin-prettier@4.2.1:
- resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==}
+ eslint-plugin-prettier@4.2.3:
+ resolution: {integrity: sha512-HOT5QrFj0ioo/USxnMvj9+E1kwJHg7HDpY9sf6mxNqisHbSegMnmdan/wfUtIPVZ8hwcfGGEJpyG1/TpeR5R1g==}
engines: {node: '>=12.0.0'}
peerDependencies:
eslint: '>=7.28.0'
@@ -4796,8 +4796,8 @@ packages:
mdn-data@2.12.2:
resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==}
- mdn-data@2.22.1:
- resolution: {integrity: sha512-u9Xnc9zLuF/CL2IHPow7HcXPpb8okQyzYpwL5wFsY//JRedSWYglYRg3PYWoQCu1zO+tBTmWOJN/iM0mPC5CRQ==}
+ mdn-data@2.23.0:
+ resolution: {integrity: sha512-786vq1+4079JSeu2XdcDjrhi/Ry7BWtjDl9WtGPWLiIHb2T66GvIVflZTBoSNZ5JqTtJGYEVMuFA/lbQlMOyDQ==}
memoize-one@5.2.1:
resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==}
@@ -4914,8 +4914,8 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
- napi-postinstall@0.3.0:
- resolution: {integrity: sha512-M7NqKyhODKV1gRLdkwE7pDsZP2/SC2a2vHkOYh9MCpKMbWVfyVfUw5MaH83Fv6XMjxr5jryUp3IDDL9rlxsTeA==}
+ napi-postinstall@0.3.1:
+ resolution: {integrity: sha512-is9eGpjKpRg+Z7ECny6NSOekea7+1eTs9+jTt5jJx43ez0o1BYRUKEBIf+ZZn15oQf5wvPnRf0Uat6MAtvVz+A==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
hasBin: true
@@ -7840,18 +7840,18 @@ snapshots:
dependencies:
'@dicebear/core': 9.2.3
- '@emnapi/core@1.4.4':
+ '@emnapi/core@1.4.5':
dependencies:
- '@emnapi/wasi-threads': 1.0.3
+ '@emnapi/wasi-threads': 1.0.4
tslib: 2.8.1
optional: true
- '@emnapi/runtime@1.4.4':
+ '@emnapi/runtime@1.4.5':
dependencies:
tslib: 2.8.1
optional: true
- '@emnapi/wasi-threads@1.0.3':
+ '@emnapi/wasi-threads@1.0.4':
dependencies:
tslib: 2.8.1
optional: true
@@ -8180,7 +8180,7 @@ snapshots:
'@img/sharp-wasm32@0.34.3':
dependencies:
- '@emnapi/runtime': 1.4.4
+ '@emnapi/runtime': 1.4.5
optional: true
'@img/sharp-win32-arm64@0.34.3':
@@ -8407,8 +8407,8 @@ snapshots:
'@napi-rs/wasm-runtime@0.2.12':
dependencies:
- '@emnapi/core': 1.4.4
- '@emnapi/runtime': 1.4.4
+ '@emnapi/core': 1.4.5
+ '@emnapi/runtime': 1.4.5
'@tybys/wasm-util': 0.10.0
optional: true
@@ -9458,7 +9458,7 @@ snapshots:
browserslist@4.25.1:
dependencies:
caniuse-lite: 1.0.30001727
- electron-to-chromium: 1.5.186
+ electron-to-chromium: 1.5.187
node-releases: 2.0.19
update-browserslist-db: 1.1.3(browserslist@4.25.1)
@@ -10119,7 +10119,7 @@ snapshots:
dependencies:
jake: 10.9.2
- electron-to-chromium@1.5.186: {}
+ electron-to-chromium@1.5.187: {}
emittery@0.13.1: {}
@@ -10311,7 +10311,7 @@ snapshots:
- eslint-plugin-import-x
- supports-color
- eslint-config-prettier@8.10.0(eslint@8.57.1):
+ eslint-config-prettier@8.10.1(eslint@8.57.1):
dependencies:
eslint: 8.57.1
@@ -10423,13 +10423,13 @@ snapshots:
safe-regex-test: 1.1.0
string.prototype.includes: 2.0.1
- eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.1))(eslint@8.57.1)(prettier@2.8.8):
+ eslint-plugin-prettier@4.2.3(eslint-config-prettier@8.10.1(eslint@8.57.1))(eslint@8.57.1)(prettier@2.8.8):
dependencies:
eslint: 8.57.1
prettier: 2.8.8
prettier-linter-helpers: 1.0.0
optionalDependencies:
- eslint-config-prettier: 8.10.0(eslint@8.57.1)
+ eslint-config-prettier: 8.10.1(eslint@8.57.1)
eslint-plugin-promise@6.6.0(eslint@8.57.1):
dependencies:
@@ -11914,7 +11914,7 @@ snapshots:
mdn-data@2.12.2:
optional: true
- mdn-data@2.22.1:
+ mdn-data@2.23.0:
optional: true
memoize-one@5.2.1: {}
@@ -12029,7 +12029,7 @@ snapshots:
nanoid@3.3.11: {}
- napi-postinstall@0.3.0: {}
+ napi-postinstall@0.3.1: {}
natural-compare@1.4.0: {}
@@ -13465,7 +13465,7 @@ snapshots:
css-tree: 3.1.0
is-plain-object: 5.0.0
known-css-properties: 0.36.0
- mdn-data: 2.22.1
+ mdn-data: 2.23.0
postcss-media-query-parser: 0.2.3
postcss-resolve-nested-selector: 0.1.6
postcss-selector-parser: 7.1.0
@@ -13789,7 +13789,7 @@ snapshots:
unrs-resolver@1.11.1:
dependencies:
- napi-postinstall: 0.3.0
+ napi-postinstall: 0.3.1
optionalDependencies:
'@unrs/resolver-binding-android-arm-eabi': 1.11.1
'@unrs/resolver-binding-android-arm64': 1.11.1
diff --git a/scripts/data-migrations/populate-revenue-table.sql b/scripts/data-migrations/populate-revenue-table.sql
new file mode 100644
index 00000000..9df75189
--- /dev/null
+++ b/scripts/data-migrations/populate-revenue-table.sql
@@ -0,0 +1,41 @@
+-----------------------------------------------------
+-- PostgreSQL
+-----------------------------------------------------
+INSERT INTO "revenue"
+SELECT gen_random_uuid() revenue_id,
+ ed.website_id,
+ we.session_id,
+ we.event_id,
+ we.event_name,
+ currency.string_value currency,
+ coalesce(ed.number_value, cast(ed.string_value as numeric(19,4))) revenue,
+ ed.created_at
+FROM event_data ed
+JOIN website_event we
+ON we.event_id = ed.website_event_id
+JOIN (SELECT website_event_id, string_value
+ FROM event_data
+ WHERE data_key ilike '%currency%') currency
+ON currency.website_event_id = ed.website_event_id
+WHERE ed.data_key ilike '%revenue%';
+
+-----------------------------------------------------
+-- MySQL
+-----------------------------------------------------
+INSERT INTO `revenue`
+SELECT UUID() revenue_id,
+ ed.website_id,
+ we.session_id,
+ we.event_id,
+ we.event_name,
+ currency.string_value currency,
+ coalesce(ed.number_value, cast(ed.string_value as decimal(19,4))) revenue,
+ ed.created_at
+FROM event_data ed
+JOIN website_event we
+ON we.event_id = ed.website_event_id
+JOIN (SELECT website_event_id, string_value
+ FROM event_data
+ WHERE data_key like '%currency%') currency
+ON currency.website_event_id = ed.website_event_id
+WHERE ed.data_key like '%revenue%';
\ No newline at end of file
diff --git a/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx b/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx
index 285a230e..46970b51 100644
--- a/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx
+++ b/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx
@@ -9,16 +9,22 @@ import { useMessages } from '@/components/hooks';
import { Item, Tabs } from 'react-basics';
import { useState } from 'react';
import EventProperties from './EventProperties';
+import { getItem, setItem } from '@/lib/storage';
export default function EventsPage({ websiteId }) {
const [label, setLabel] = useState(null);
- const [tab, setTab] = useState('activity');
+ const [tab, setTab] = useState(getItem('eventTab') || 'activity');
const { formatMessage, labels } = useMessages();
const handleLabelClick = (value: string) => {
setLabel(value !== label ? value : '');
};
+ const onSelect = (value: 'activity' | 'properties') => {
+ setItem('eventTab', value);
+ setTab(value);
+ };
+
return (
<>