diff --git a/db/postgresql/schema.prisma b/db/postgresql/schema.prisma
index 6733bca6..d39620ca 100644
--- a/db/postgresql/schema.prisma
+++ b/db/postgresql/schema.prisma
@@ -1,6 +1,6 @@
generator client {
provider = "prisma-client"
- previewFeatures = ["driverAdapters"]
+ previewFeatures = ["queryCompiler", "driverAdapters"]
output = "../src/generated/prisma"
moduleFormat = "esm"
}
diff --git a/package.json b/package.json
index 70fd1f73..df26f460 100644
--- a/package.json
+++ b/package.json
@@ -76,7 +76,7 @@
"@fontsource/inter": "^4.5.15",
"@hello-pangea/dnd": "^17.0.0",
"@prisma/adapter-pg": "^6.8.2",
- "@prisma/client": "^6.8.2",
+ "@prisma/client": "^6.9.0",
"@prisma/extension-read-replicas": "^0.4.1",
"@react-spring/web": "^9.7.3",
"@svgr/cli": "^8.1.0",
@@ -114,7 +114,7 @@
"node-fetch": "^3.2.8",
"npm-run-all": "^4.1.5",
"pg": "^8.16.0",
- "prisma": "6.8.2",
+ "prisma": "6.9.0",
"pure-rand": "^6.1.0",
"react": "^19.0.0",
"react-basics": "^0.126.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 1fac552a..571361ea 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -30,11 +30,11 @@ importers:
specifier: ^6.8.2
version: 6.8.2(pg@8.16.0)
'@prisma/client':
- specifier: ^6.8.2
- version: 6.8.2(prisma@6.8.2(typescript@5.8.3))(typescript@5.8.3)
+ specifier: ^6.9.0
+ version: 6.9.0(prisma@6.9.0(typescript@5.8.3))(typescript@5.8.3)
'@prisma/extension-read-replicas':
specifier: ^0.4.1
- version: 0.4.1(@prisma/client@6.8.2(prisma@6.8.2(typescript@5.8.3))(typescript@5.8.3))
+ version: 0.4.1(@prisma/client@6.9.0(prisma@6.9.0(typescript@5.8.3))(typescript@5.8.3))
'@react-spring/web':
specifier: ^9.7.3
version: 9.7.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -144,8 +144,8 @@ importers:
specifier: ^8.16.0
version: 8.16.0
prisma:
- specifier: 6.8.2
- version: 6.8.2(typescript@5.8.3)
+ specifier: 6.9.0
+ version: 6.9.0(typescript@5.8.3)
pure-rand:
specifier: ^6.1.0
version: 6.1.0
@@ -1508,8 +1508,8 @@ packages:
peerDependencies:
pg: ^8.11.3
- '@prisma/client@6.8.2':
- resolution: {integrity: sha512-5II+vbyzv4si6Yunwgkj0qT/iY0zyspttoDrL3R4BYgLdp42/d2C8xdi9vqkrYtKt9H32oFIukvyw3Koz5JoDg==}
+ '@prisma/client@6.9.0':
+ resolution: {integrity: sha512-Gg7j1hwy3SgF1KHrh0PZsYvAaykeR0PaxusnLXydehS96voYCGt1U5zVR31NIouYc63hWzidcrir1a7AIyCsNQ==}
engines: {node: '>=18.18'}
peerDependencies:
prisma: '*'
@@ -1520,31 +1520,34 @@ packages:
typescript:
optional: true
- '@prisma/config@6.8.2':
- resolution: {integrity: sha512-ZJY1fF4qRBPdLQ/60wxNtX+eu89c3AkYEcP7L3jkp0IPXCNphCYxikTg55kPJLDOG6P0X+QG5tCv6CmsBRZWFQ==}
+ '@prisma/config@6.9.0':
+ resolution: {integrity: sha512-Wcfk8/lN3WRJd5w4jmNQkUwhUw0eksaU/+BlAJwPQKW10k0h0LC9PD/6TQFmqKVbHQL0vG2z266r0S1MPzzhbA==}
'@prisma/debug@6.8.2':
resolution: {integrity: sha512-4muBSSUwJJ9BYth5N8tqts8JtiLT8QI/RSAzEogwEfpbYGFo9mYsInsVo8dqXdPO2+Rm5OG5q0qWDDE3nyUbVg==}
+ '@prisma/debug@6.9.0':
+ resolution: {integrity: sha512-bFeur/qi/Q+Mqk4JdQ3R38upSYPebv5aOyD1RKywVD+rAMLtRkmTFn28ZuTtVOnZHEdtxnNOCH+bPIeSGz1+Fg==}
+
'@prisma/driver-adapter-utils@6.8.2':
resolution: {integrity: sha512-5+CzN/41gBsRmA3ekbVy1TXnSImSPBtMlxWAttVH6tg94bv4zGGRmyk5tUCdT83nl0hG1Sq2oMXR7ml6aqILvw==}
- '@prisma/engines-version@6.8.0-43.2060c79ba17c6bb9f5823312b6f6b7f4a845738e':
- resolution: {integrity: sha512-Rkik9lMyHpFNGaLpPF3H5q5TQTkm/aE7DsGM5m92FZTvWQsvmi6Va8On3pWvqLHOt5aPUvFb/FeZTmphI4CPiQ==}
+ '@prisma/engines-version@6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e':
+ resolution: {integrity: sha512-Qp9gMoBHgqhKlrvumZWujmuD7q4DV/gooEyPCLtbkc13EZdSz2RsGUJ5mHb3RJgAbk+dm6XenqG7obJEhXcJ6Q==}
- '@prisma/engines@6.8.2':
- resolution: {integrity: sha512-XqAJ//LXjqYRQ1RRabs79KOY4+v6gZOGzbcwDQl0D6n9WBKjV7qdrbd042CwSK0v0lM9MSHsbcFnU2Yn7z8Zlw==}
+ '@prisma/engines@6.9.0':
+ resolution: {integrity: sha512-im0X0bwDLA0244CDf8fuvnLuCQcBBdAGgr+ByvGfQY9wWl6EA+kRGwVk8ZIpG65rnlOwtaWIr/ZcEU5pNVvq9g==}
'@prisma/extension-read-replicas@0.4.1':
resolution: {integrity: sha512-mCMDloqUKUwx2o5uedTs1FHX3Nxdt1GdRMoeyp1JggjiwOALmIYWhxfIN08M2BZ0w8SKwvJqicJZMjkQYkkijw==}
peerDependencies:
'@prisma/client': ^6.5.0
- '@prisma/fetch-engine@6.8.2':
- resolution: {integrity: sha512-lCvikWOgaLOfqXGacEKSNeenvj0n3qR5QvZUOmPE2e1Eh8cMYSobxonCg9rqM6FSdTfbpqp9xwhSAOYfNqSW0g==}
+ '@prisma/fetch-engine@6.9.0':
+ resolution: {integrity: sha512-PMKhJdl4fOdeE3J3NkcWZ+tf3W6rx3ht/rLU8w4SXFRcLhd5+3VcqY4Kslpdm8osca4ej3gTfB3+cSk5pGxgFg==}
- '@prisma/get-platform@6.8.2':
- resolution: {integrity: sha512-vXSxyUgX3vm1Q70QwzwkjeYfRryIvKno1SXbIqwSptKwqKzskINnDUcx85oX+ys6ooN2ATGSD0xN2UTfg6Zcow==}
+ '@prisma/get-platform@6.9.0':
+ resolution: {integrity: sha512-/B4n+5V1LI/1JQcHp+sUpyRT1bBgZVPHbsC4lt4/19Xp4jvNIVcq5KYNtQDk5e/ukTSjo9PZVAxxy9ieFtlpTQ==}
'@react-aria/autocomplete@3.0.0-beta.3':
resolution: {integrity: sha512-8haBygHNMqVt4Ge90VOk+iVlLW+zhiOGHYz2IKCE6+Sy1dTE6mzhHjxrtwWYnSez/OQLbxjHlwLch4CDd5JkLA==}
@@ -5906,8 +5909,8 @@ packages:
resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
- prisma@6.8.2:
- resolution: {integrity: sha512-JNricTXQxzDtRS7lCGGOB4g5DJ91eg3nozdubXze3LpcMl1oWwcFddrj++Up3jnRE6X/3gB/xz3V+ecBk/eEGA==}
+ prisma@6.9.0:
+ resolution: {integrity: sha512-resJAwMyZREC/I40LF6FZ6rZTnlrlrYrb63oW37Gq+U+9xHwbyMSPJjKtM7VZf3gTO86t/Oyz+YeSXr3CmAY1Q==}
engines: {node: '>=18.18'}
hasBin: true
peerDependencies:
@@ -8209,43 +8212,45 @@ snapshots:
pg: 8.16.0
postgres-array: 3.0.4
- '@prisma/client@6.8.2(prisma@6.8.2(typescript@5.8.3))(typescript@5.8.3)':
+ '@prisma/client@6.9.0(prisma@6.9.0(typescript@5.8.3))(typescript@5.8.3)':
optionalDependencies:
- prisma: 6.8.2(typescript@5.8.3)
+ prisma: 6.9.0(typescript@5.8.3)
typescript: 5.8.3
- '@prisma/config@6.8.2':
+ '@prisma/config@6.9.0':
dependencies:
jiti: 2.4.2
'@prisma/debug@6.8.2': {}
+ '@prisma/debug@6.9.0': {}
+
'@prisma/driver-adapter-utils@6.8.2':
dependencies:
'@prisma/debug': 6.8.2
- '@prisma/engines-version@6.8.0-43.2060c79ba17c6bb9f5823312b6f6b7f4a845738e': {}
+ '@prisma/engines-version@6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e': {}
- '@prisma/engines@6.8.2':
+ '@prisma/engines@6.9.0':
dependencies:
- '@prisma/debug': 6.8.2
- '@prisma/engines-version': 6.8.0-43.2060c79ba17c6bb9f5823312b6f6b7f4a845738e
- '@prisma/fetch-engine': 6.8.2
- '@prisma/get-platform': 6.8.2
+ '@prisma/debug': 6.9.0
+ '@prisma/engines-version': 6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e
+ '@prisma/fetch-engine': 6.9.0
+ '@prisma/get-platform': 6.9.0
- '@prisma/extension-read-replicas@0.4.1(@prisma/client@6.8.2(prisma@6.8.2(typescript@5.8.3))(typescript@5.8.3))':
+ '@prisma/extension-read-replicas@0.4.1(@prisma/client@6.9.0(prisma@6.9.0(typescript@5.8.3))(typescript@5.8.3))':
dependencies:
- '@prisma/client': 6.8.2(prisma@6.8.2(typescript@5.8.3))(typescript@5.8.3)
+ '@prisma/client': 6.9.0(prisma@6.9.0(typescript@5.8.3))(typescript@5.8.3)
- '@prisma/fetch-engine@6.8.2':
+ '@prisma/fetch-engine@6.9.0':
dependencies:
- '@prisma/debug': 6.8.2
- '@prisma/engines-version': 6.8.0-43.2060c79ba17c6bb9f5823312b6f6b7f4a845738e
- '@prisma/get-platform': 6.8.2
+ '@prisma/debug': 6.9.0
+ '@prisma/engines-version': 6.9.0-10.81e4af48011447c3cc503a190e86995b66d2a28e
+ '@prisma/get-platform': 6.9.0
- '@prisma/get-platform@6.8.2':
+ '@prisma/get-platform@6.9.0':
dependencies:
- '@prisma/debug': 6.8.2
+ '@prisma/debug': 6.9.0
'@react-aria/autocomplete@3.0.0-beta.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
dependencies:
@@ -13603,10 +13608,10 @@ snapshots:
ansi-styles: 5.2.0
react-is: 18.3.1
- prisma@6.8.2(typescript@5.8.3):
+ prisma@6.9.0(typescript@5.8.3):
dependencies:
- '@prisma/config': 6.8.2
- '@prisma/engines': 6.8.2
+ '@prisma/config': 6.9.0
+ '@prisma/engines': 6.9.0
optionalDependencies:
typescript: 5.8.3
diff --git a/src/app/(main)/reports/ReportDeleteButton.tsx b/src/app/(main)/reports/ReportDeleteButton.tsx
deleted file mode 100644
index d64e2b56..00000000
--- a/src/app/(main)/reports/ReportDeleteButton.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import { Button, Icon, Modal, DialogTrigger, Dialog, Text } from '@umami/react-zen';
-import { useMessages } from '@/components/hooks';
-import { Trash } from '@/components/icons';
-import { ConfirmationForm } from '@/components/common/ConfirmationForm';
-import { useDeleteQuery } from '@/components/hooks/queries/useDeleteQuery';
-
-export function ReportDeleteButton({
- reportId,
- reportName,
- onDelete,
-}: {
- reportId: string;
- reportName: string;
- onDelete?: () => void;
-}) {
- const { formatMessage, labels, messages } = useMessages();
- const { mutate, isPending, error, touch } = useDeleteQuery(`/reports/${reportId}`);
-
- const handleConfirm = (close: () => void) => {
- mutate(reportId as any, {
- onSuccess: () => {
- touch('reports');
- onDelete?.();
- close();
- },
- });
- };
-
- return (
-
-
-
-
-
-
- );
-}
diff --git a/src/app/(main)/reports/ReportsDataTable.tsx b/src/app/(main)/reports/ReportsDataTable.tsx
deleted file mode 100644
index ca0109e8..00000000
--- a/src/app/(main)/reports/ReportsDataTable.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { ReactNode } from 'react';
-import { useReportsQuery } from '@/components/hooks';
-import { DataGrid } from '@/components/common/DataGrid';
-import { ReportsTable } from './ReportsTable';
-
-export function ReportsDataTable({
- websiteId,
- teamId,
- children,
-}: {
- websiteId?: string;
- teamId?: string;
- children?: ReactNode;
-}) {
- const queryResult = useReportsQuery({ websiteId, teamId });
-
- return (
- children}>
- {({ data }) => }
-
- );
-}
diff --git a/src/app/(main)/reports/ReportsHeader.tsx b/src/app/(main)/reports/ReportsHeader.tsx
deleted file mode 100644
index b477f386..00000000
--- a/src/app/(main)/reports/ReportsHeader.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { SectionHeader } from '@/components/common/SectionHeader';
-import { Icon, Text } from '@umami/react-zen';
-import { useLoginQuery, useMessages, useNavigation } from '@/components/hooks';
-import { Plus } from '@/components/icons';
-import { LinkButton } from '@/components/common/LinkButton';
-import { ROLES } from '@/lib/constants';
-
-export function ReportsHeader() {
- const { formatMessage, labels } = useMessages();
- const { renderTeamUrl } = useNavigation();
- const { user } = useLoginQuery();
- const canEdit = user.role !== ROLES.viewOnly;
-
- return (
-
- {canEdit && (
-
-
-
-
- {formatMessage(labels.createReport)}
-
- )}
-
- );
-}
diff --git a/src/app/(main)/reports/ReportsPage.tsx b/src/app/(main)/reports/ReportsPage.tsx
deleted file mode 100644
index 3041fced..00000000
--- a/src/app/(main)/reports/ReportsPage.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-'use client';
-import { Metadata } from 'next';
-import { ReportsHeader } from './ReportsHeader';
-import { ReportsDataTable } from './ReportsDataTable';
-import { useNavigation } from '@/components/hooks';
-import { PageBody } from '@/components/common/PageBody';
-
-export function ReportsPage() {
- const { teamId } = useNavigation();
-
- return (
-
-
-
-
- );
-}
-
-export const metadata: Metadata = {
- title: 'Reports',
-};
diff --git a/src/app/(main)/reports/ReportsTable.tsx b/src/app/(main)/reports/ReportsTable.tsx
deleted file mode 100644
index 7e288384..00000000
--- a/src/app/(main)/reports/ReportsTable.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import { Icon, Text, DataTable, DataColumn, Row } from '@umami/react-zen';
-import { LinkButton } from '@/components/common/LinkButton';
-import { useMessages, useLoginQuery, useNavigation } from '@/components/hooks';
-import { REPORT_TYPES } from '@/lib/constants';
-import { Arrow } from '@/components/icons';
-import { ReportDeleteButton } from './ReportDeleteButton';
-
-export function ReportsTable({ data = [] }: { data: any[]; showDomain?: boolean }) {
- const { formatMessage, labels } = useMessages();
- const { user } = useLoginQuery();
- const { renderTeamUrl } = useNavigation();
-
- return (
-
-
-
-
- {(row: any) => {
- return formatMessage(
- labels[Object.keys(REPORT_TYPES).find(key => REPORT_TYPES[key] === row.type)],
- );
- }}
-
-
- {(row: any) => {
- const { id, name, userId, website } = row;
- return (
-
- {(user.id === userId || user.id === website?.userId) && (
-
- )}
-
-
-
-
- {formatMessage(labels.view)}
-
-
- );
- }}
-
-
- );
-}
diff --git a/src/app/(main)/reports/[reportId]/BaseParameters.tsx b/src/app/(main)/reports/[reportId]/BaseParameters.tsx
deleted file mode 100644
index 1e49d378..00000000
--- a/src/app/(main)/reports/[reportId]/BaseParameters.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import { Column, Label } from '@umami/react-zen';
-import { useReport } from '@/components/hooks';
-import { parseDateRange } from '@/lib/date';
-import { DateFilter } from '@/components/input/DateFilter';
-import { WebsiteSelect } from '@/components/input/WebsiteSelect';
-import { useMessages, useNavigation, useWebsiteQuery } from '@/components/hooks';
-
-export interface BaseParametersProps {
- showWebsiteSelect?: boolean;
- allowWebsiteSelect?: boolean;
- showDateSelect?: boolean;
- allowDateSelect?: boolean;
-}
-
-export function BaseParameters({
- showWebsiteSelect = true,
- allowWebsiteSelect = true,
- showDateSelect = true,
- allowDateSelect = true,
-}: BaseParametersProps) {
- const { report, updateReport } = useReport();
- const { formatMessage, labels } = useMessages();
- const { teamId } = useNavigation();
- const { parameters } = report || {};
- const { websiteId, dateRange } = parameters || {};
- const { value, startDate, endDate } = dateRange || {};
- const { data: website } = useWebsiteQuery(websiteId);
- const { name } = website || {};
-
- const handleWebsiteSelect = (websiteId: string) => {
- updateReport({ websiteId, parameters: { websiteId } });
- };
-
- const handleDateChange = (value: string) => {
- updateReport({ parameters: { dateRange: { ...parseDateRange(value) } } });
- };
-
- return (
- <>
- {showWebsiteSelect && (
-
-
- {allowWebsiteSelect ? (
-
- ) : (
- name
- )}
-
- )}
- {showDateSelect && (
-
-
- {allowDateSelect && (
-
- )}
-
- )}
- >
- );
-}
diff --git a/src/app/(main)/reports/[reportId]/FieldAddForm.tsx b/src/app/(main)/reports/[reportId]/FieldAddForm.tsx
deleted file mode 100644
index 8034724e..00000000
--- a/src/app/(main)/reports/[reportId]/FieldAddForm.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import { useState } from 'react';
-import { createPortal } from 'react-dom';
-import { REPORT_PARAMETERS } from '@/lib/constants';
-import { FieldSelectForm } from './FieldSelectForm';
-
-export function FieldAddForm({
- fields = [],
- group,
- onAdd,
- onClose,
-}: {
- fields?: any[];
- group: string;
- onAdd: (group: string, value: string) => void;
- onClose: () => void;
-}) {
- const [selected, setSelected] = useState<{
- name: string;
- type: string;
- value: string;
- }>();
-
- const handleSelect = (value: any) => {
- const { type } = value;
-
- if (group === REPORT_PARAMETERS.groups || type === 'array' || type === 'boolean') {
- value.value = group === REPORT_PARAMETERS.groups ? '' : 'total';
- handleSave(value);
- return;
- }
-
- setSelected(value);
- };
-
- const handleSave = (value: any) => {
- onAdd(group, value);
- onClose();
- };
-
- return createPortal(
- !selected && ,
- document.body,
- );
-}
diff --git a/src/app/(main)/reports/[reportId]/FieldAggregateForm.tsx b/src/app/(main)/reports/[reportId]/FieldAggregateForm.tsx
deleted file mode 100644
index fd895c7e..00000000
--- a/src/app/(main)/reports/[reportId]/FieldAggregateForm.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import { Form, FormRow, Menu, Item } from '@umami/react-zen';
-import { useMessages } from '@/components/hooks';
-
-export function FieldAggregateForm({
- name,
- type,
- onSelect,
-}: {
- name: string;
- type: string;
- onSelect: (key: any) => void;
-}) {
- const { formatMessage, labels } = useMessages();
-
- const options = {
- number: [
- { label: formatMessage(labels.sum), value: 'sum' },
- { label: formatMessage(labels.average), value: 'average' },
- { label: formatMessage(labels.min), value: 'min' },
- { label: formatMessage(labels.max), value: 'max' },
- ],
- date: [
- { label: formatMessage(labels.min), value: 'min' },
- { label: formatMessage(labels.max), value: 'max' },
- ],
- string: [
- { label: formatMessage(labels.total), value: 'total' },
- { label: formatMessage(labels.unique), value: 'unique' },
- ],
- uuid: [
- { label: formatMessage(labels.total), value: 'total' },
- { label: formatMessage(labels.unique), value: 'unique' },
- ],
- };
-
- const items = options[type];
-
- const handleSelect = (value: any) => {
- onSelect({ name, type, value });
- };
-
- return (
-
- );
-}
diff --git a/src/app/(main)/reports/[reportId]/FieldFilterEditForm.module.css b/src/app/(main)/reports/[reportId]/FieldFilterEditForm.module.css
deleted file mode 100644
index 43a34438..00000000
--- a/src/app/(main)/reports/[reportId]/FieldFilterEditForm.module.css
+++ /dev/null
@@ -1,36 +0,0 @@
-.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
deleted file mode 100644
index 4e5e5847..00000000
--- a/src/app/(main)/reports/[reportId]/FieldFilterEditForm.tsx
+++ /dev/null
@@ -1,219 +0,0 @@
-import { useMemo, useState } from 'react';
-import { useFilters, useFormat, useMessages, useWebsiteValuesQuery } from '@/components/hooks';
-import { OPERATORS } from '@/lib/constants';
-import { isEqualsOperator } from '@/lib/params';
-import {
- Button,
- Column,
- Row,
- Select,
- Icon,
- Loading,
- Menu,
- MenuItem,
- ListItem,
- SearchField,
- Text,
- TextField,
- Label,
-} from '@umami/react-zen';
-import { Close } from '@/components/icons';
-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 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 isDisabled = !operator || (isEquals && !selected) || (!isEquals && !value);
- const {
- data: values = [],
- isLoading,
- refetch,
- } = useWebsiteValuesQuery({
- websiteId,
- type: name,
- startDate,
- endDate,
- 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(() => {
- return values.reduce((obj: { [x: string]: string }, { value }: { value: string }) => {
- obj[value] = formatValue(value, name);
-
- return obj;
- }, {});
- }, [formatValue, name, values]);
-
- const filteredValues = useMemo(() => {
- return value
- ? values.filter((n: string | number) =>
- formattedValues[n]?.toLowerCase()?.includes(value.toLowerCase()),
- )
- : values;
- }, [value, formattedValues]);
-
- 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);
- };
-
- const items = filterDropdownItems(name);
-
- return (
-
-
-
-
- {allowFilterSelect && (
-
- )}
- {selected && isEquals && (
-
- {formatValue(selected, name)}
-
-
-
-
- )}
- {!selected && isEquals && (
-
- setShowMenu(true)}
- onBlur={handleBlur}
- />
- {showMenu && (
-
- )}
-
- )}
- {!selected && !isEquals && (
- setValue(e.target.value)}
- />
- )}
-
-
-
-
- );
-}
-
-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]/FieldParameters.tsx b/src/app/(main)/reports/[reportId]/FieldParameters.tsx
deleted file mode 100644
index 7bf62ece..00000000
--- a/src/app/(main)/reports/[reportId]/FieldParameters.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import { useFields, useMessages, useReport } from '@/components/hooks';
-import { Plus } from '@/components/icons';
-import { Button, Row, Label, Icon, Popover, MenuTrigger, Column } from '@umami/react-zen';
-import { FieldSelectForm } from '../[reportId]/FieldSelectForm';
-import { ParameterList } from '../[reportId]/ParameterList';
-
-export function FieldParameters() {
- const { report, updateReport } = useReport();
- const { formatMessage, labels } = useMessages();
- const { parameters } = report || {};
- const { fields } = parameters || {};
- const { fields: fieldOptions } = useFields();
-
- const handleAdd = (value: string) => {
- if (!fields.find(({ name }) => name === value)) {
- const field = fieldOptions.find(({ name }) => name === value);
- updateReport({ parameters: { fields: fields.concat(field) } });
- }
- };
-
- const handleRemove = (name: string) => {
- updateReport({ parameters: { fields: fields.filter(f => f.name !== name) } });
- };
-
- return (
-
-
-
-
-
-
- !fields.find(f => f.name === name))}
- onSelect={handleAdd}
- showType={false}
- />
-
-
-
-
- {fields.map(({ name }) => {
- return (
- handleRemove(name)}>
- {fieldOptions.find(f => f.name === name)?.label}
-
- );
- })}
-
-
- );
-}
diff --git a/src/app/(main)/reports/[reportId]/FieldSelectForm.module.css b/src/app/(main)/reports/[reportId]/FieldSelectForm.module.css
deleted file mode 100644
index 3a5ed9b8..00000000
--- a/src/app/(main)/reports/[reportId]/FieldSelectForm.module.css
+++ /dev/null
@@ -1,20 +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);
-}
diff --git a/src/app/(main)/reports/[reportId]/FieldSelectForm.tsx b/src/app/(main)/reports/[reportId]/FieldSelectForm.tsx
deleted file mode 100644
index 038183bc..00000000
--- a/src/app/(main)/reports/[reportId]/FieldSelectForm.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import { Menu, MenuItem, Text, MenuSection, Row } from '@umami/react-zen';
-import { useMessages } from '@/components/hooks';
-
-export interface FieldSelectFormProps {
- fields?: any[];
- onSelect?: (value: any) => void;
- showType?: boolean;
-}
-
-export function FieldSelectForm({ fields = [], onSelect, showType = true }: FieldSelectFormProps) {
- const { formatMessage, labels } = useMessages();
-
- return (
-
- );
-}
diff --git a/src/app/(main)/reports/[reportId]/FilterParameters.module.css b/src/app/(main)/reports/[reportId]/FilterParameters.module.css
deleted file mode 100644
index 939d0652..00000000
--- a/src/app/(main)/reports/[reportId]/FilterParameters.module.css
+++ /dev/null
@@ -1,40 +0,0 @@
-.item {
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- gap: 10px;
- overflow: hidden;
-}
-
-.label {
- color: var(--base800);
- border: 1px solid var(--base300);
- font-weight: 900;
- padding: 2px 8px;
- border-radius: 5px;
- white-space: nowrap;
-}
-
-.op {
- color: var(--blue900);
- background-color: var(--blue100);
- font-size: 12px;
- font-weight: 900;
- padding: 2px 8px;
- border-radius: 5px;
- text-transform: uppercase;
- white-space: nowrap;
-}
-
-.value {
- color: var(--base900);
- background-color: var(--base100);
- font-weight: 900;
- padding: 2px 8px;
- 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
deleted file mode 100644
index 9d157411..00000000
--- a/src/app/(main)/reports/[reportId]/FilterParameters.tsx
+++ /dev/null
@@ -1,140 +0,0 @@
-import { useMessages, useFormat, useFilters, useFields, useReport } from '@/components/hooks';
-import { Plus } from '@/components/icons';
-import {
- Button,
- Row,
- Column,
- Label,
- Icon,
- Popover,
- MenuTrigger,
- Text,
- Pressable,
-} from '@umami/react-zen';
-import { FilterSelectForm } from '../[reportId]/FilterSelectForm';
-import { ParameterList } from '../[reportId]/ParameterList';
-import { FieldFilterEditForm } from '../[reportId]/FieldFilterEditForm';
-import { isSearchOperator } from '@/lib/params';
-
-export function FilterParameters() {
- const { report, updateReport } = useReport();
- 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();
- };
-
- return (
-
-
-
-
-
-
- !filters.find(f => f.name === name))}
- startDate={dateRange?.startDate}
- endDate={dateRange?.endDate}
- onChange={handleAdd}
- />
-
-
-
-
- {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) => (
-
- )}
-
-
- );
-};
diff --git a/src/app/(main)/reports/[reportId]/FilterSelectForm.tsx b/src/app/(main)/reports/[reportId]/FilterSelectForm.tsx
deleted file mode 100644
index beb3350f..00000000
--- a/src/app/(main)/reports/[reportId]/FilterSelectForm.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import { useState } from 'react';
-import { FieldSelectForm } from './FieldSelectForm';
-import { FieldFilterEditForm } from './FieldFilterEditForm';
-
-export interface FilterSelectFormProps {
- websiteId?: string;
- fields: any[];
- startDate?: Date;
- endDate?: Date;
- onChange?: (filter: { name: string; type: string; operator: string; value: string }) => void;
- allowFilterSelect?: boolean;
-}
-
-export function FilterSelectForm({
- websiteId,
- fields,
- startDate,
- endDate,
- onChange,
- allowFilterSelect,
-}: FilterSelectFormProps) {
- const [field, setField] = useState<{ name: string; label: string; type: string }>();
-
- if (!field) {
- return ;
- }
-
- const { name, label, type } = field;
-
- return (
-
- );
-}
diff --git a/src/app/(main)/reports/[reportId]/ParameterList.module.css b/src/app/(main)/reports/[reportId]/ParameterList.module.css
deleted file mode 100644
index 75860b25..00000000
--- a/src/app/(main)/reports/[reportId]/ParameterList.module.css
+++ /dev/null
@@ -1,29 +0,0 @@
-.list {
- display: flex;
- flex-direction: column;
- gap: 16px;
-}
-
-.item {
- display: flex;
- gap: 12px;
- width: 100%;
- flex-wrap: nowrap;
- padding: 12px;
- border: 1px solid var(--base400);
- border-radius: var(--border-radius);
- box-shadow: 1px 1px 1px var(--base400);
-}
-
-.value {
- display: flex;
- flex-direction: row;
- align-items: center;
- flex-wrap: wrap;
- flex: 1;
-}
-
-.icon,
-.close {
- height: 1.5rem;
-}
diff --git a/src/app/(main)/reports/[reportId]/ParameterList.tsx b/src/app/(main)/reports/[reportId]/ParameterList.tsx
deleted file mode 100644
index 663c12ea..00000000
--- a/src/app/(main)/reports/[reportId]/ParameterList.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import { ReactNode } from 'react';
-import { Icon, Row, Text, Button, Column } from '@umami/react-zen';
-import { Close } from '@/components/icons';
-import { Empty } from '@/components/common/Empty';
-import { useMessages } from '@/components/hooks';
-
-export interface ParameterListProps {
- children?: ReactNode;
-}
-
-export function ParameterList({ children }: ParameterListProps) {
- const { formatMessage, labels } = useMessages();
-
- return (
-
- {!children && }
- {children}
-
- );
-}
-
-const Item = ({
- icon,
- onClick,
- onRemove,
- children,
-}: {
- icon?: ReactNode;
- onClick?: () => void;
- onRemove?: () => void;
- children?: ReactNode;
-}) => {
- return (
-
- {icon && {icon}}
- {children}
-
-
- );
-};
-
-ParameterList.Item = Item;
diff --git a/src/app/(main)/reports/[reportId]/Report.module.css b/src/app/(main)/reports/[reportId]/Report.module.css
deleted file mode 100644
index 6aa6a9b3..00000000
--- a/src/app/(main)/reports/[reportId]/Report.module.css
+++ /dev/null
@@ -1,7 +0,0 @@
-.container {
- display: grid;
- grid-template-rows: max-content 1fr;
- grid-template-columns: max-content 1fr;
- margin-bottom: 60px;
- height: 90vh;
-}
diff --git a/src/app/(main)/reports/[reportId]/Report.tsx b/src/app/(main)/reports/[reportId]/Report.tsx
deleted file mode 100644
index 788191b3..00000000
--- a/src/app/(main)/reports/[reportId]/Report.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import { createContext, ReactNode } from 'react';
-import { Loading, Grid } from '@umami/react-zen';
-import { useReportQuery } from '@/components/hooks';
-
-export const ReportContext = createContext(null);
-
-export function Report({
- reportId,
- defaultParameters,
- children,
-}: {
- reportId: string;
- defaultParameters: { type: string; parameters: { [key: string]: any } };
- children: ReactNode;
-}) {
- const report = useReportQuery(reportId, defaultParameters);
-
- if (!report) {
- return reportId ? : null;
- }
-
- return (
-
-
- {children}
-
-
- );
-}
diff --git a/src/app/(main)/reports/[reportId]/ReportBody.module.css b/src/app/(main)/reports/[reportId]/ReportBody.module.css
deleted file mode 100644
index 9af1070a..00000000
--- a/src/app/(main)/reports/[reportId]/ReportBody.module.css
+++ /dev/null
@@ -1,5 +0,0 @@
-.body {
- padding-inline-start: 20px;
- grid-row: 2 / 3;
- grid-column: 2 / 3;
-}
diff --git a/src/app/(main)/reports/[reportId]/ReportBody.tsx b/src/app/(main)/reports/[reportId]/ReportBody.tsx
deleted file mode 100644
index bbf5323b..00000000
--- a/src/app/(main)/reports/[reportId]/ReportBody.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import { Panel } from '@/components/common/Panel';
-import { useReport } from '@/components/hooks';
-
-export function ReportBody({ children }) {
- const { report } = useReport();
-
- if (!report) {
- return null;
- }
-
- return {children};
-}
diff --git a/src/app/(main)/reports/[reportId]/ReportHeader.tsx b/src/app/(main)/reports/[reportId]/ReportHeader.tsx
deleted file mode 100644
index d6a7fc05..00000000
--- a/src/app/(main)/reports/[reportId]/ReportHeader.tsx
+++ /dev/null
@@ -1,100 +0,0 @@
-import {
- Row,
- Column,
- Text,
- Heading,
- Icon,
- LoadingButton,
- InlineEditField,
- useToast,
-} from '@umami/react-zen';
-import { useMessages, useApi, useNavigation, useReport } from '@/components/hooks';
-import { REPORT_TYPES } from '@/lib/constants';
-
-export function ReportHeader({ icon }) {
- const { report, updateReport } = useReport();
- const { formatMessage, labels, messages } = useMessages();
- const { toast } = useToast();
- const { router, renderTeamUrl } = useNavigation();
-
- const { post, useMutation } = useApi();
- const { mutate: create, isPending: isCreating } = useMutation({
- mutationFn: (data: any) => post(`/reports`, data),
- });
- const { mutate: update, isPending: isUpdating } = useMutation({
- mutationFn: (data: any) => post(`/reports/${data.id}`, data),
- });
-
- const { name, description, parameters } = report || {};
- const { websiteId, dateRange } = parameters || {};
- const defaultName = formatMessage(labels.untitled);
-
- const handleSave = async () => {
- if (!report.id) {
- create(report, {
- onSuccess: async ({ id }) => {
- toast(formatMessage(messages.saved));
- router.push(renderTeamUrl(`/reports/${id}`));
- },
- });
- } else {
- update(report, {
- onSuccess: async () => {
- toast(formatMessage(messages.saved));
- },
- });
- }
- };
-
- const handleNameChange = (name: string) => {
- updateReport({ name: name || defaultName });
- };
-
- const handleDescriptionChange = (description: string) => {
- updateReport({ description });
- };
-
- if (!report) {
- return null;
- }
-
- return (
-
-
- {icon}
-
- {formatMessage(
- labels[Object.keys(REPORT_TYPES).find(key => REPORT_TYPES[key] === report?.type)],
- )}
-
-
-
-
-
-
-
- {name}
-
-
-
- {description || `+ ${formatMessage(labels.addDescription)}`}
-
-
-
-
- {formatMessage(labels.save)}
-
-
-
- );
-}
diff --git a/src/app/(main)/reports/[reportId]/ReportMenu.module.css b/src/app/(main)/reports/[reportId]/ReportMenu.module.css
deleted file mode 100644
index 21368411..00000000
--- a/src/app/(main)/reports/[reportId]/ReportMenu.module.css
+++ /dev/null
@@ -1,38 +0,0 @@
-.menu {
- position: relative;
- width: 300px;
- padding-top: 20px;
- padding-inline-end: 20px;
- border-inline-end: 1px solid var(--base300);
- grid-row: 2 / 3;
- grid-column: 1 / 2;
-}
-
-.button {
- position: absolute;
- top: 0;
- right: 0;
- display: flex;
- place-content: center;
- border: 1px solid var(--base400);
- border-right: 0;
- width: 30px;
- padding: 5px;
- cursor: pointer;
- border-radius: 4px 0 0 4px;
- z-index: 1;
-}
-
-.button:hover {
- background: var(--base75);
-}
-
-.menu.collapsed {
- width: 0;
- padding: 0;
-}
-
-.menu.collapsed .button {
- right: 0;
- border-radius: 4px 0 0 4px;
-}
diff --git a/src/app/(main)/reports/[reportId]/ReportMenu.tsx b/src/app/(main)/reports/[reportId]/ReportMenu.tsx
deleted file mode 100644
index 23175fc2..00000000
--- a/src/app/(main)/reports/[reportId]/ReportMenu.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { useState } from 'react';
-import { Button, Column, Icon, Row } from '@umami/react-zen';
-import { PanelLeft } from '@/components/icons';
-import { useReport } from '@/components/hooks';
-
-export function ReportMenu({ children }) {
- const [collapsed, setCollapsed] = useState(false);
- const { report } = useReport();
-
- if (!report) {
- return null;
- }
-
- return (
-
-
-
-
- {!collapsed && children}
-
- );
-}
diff --git a/src/app/(main)/reports/[reportId]/ReportPage.tsx b/src/app/(main)/reports/[reportId]/ReportPage.tsx
deleted file mode 100644
index 11c80f5f..00000000
--- a/src/app/(main)/reports/[reportId]/ReportPage.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-'use client';
-import { useReportQuery } from '@/components/hooks';
-import { EventDataReport } from '../event-data/EventDataReport';
-import { FunnelReport } from '../funnel/FunnelReport';
-import { GoalsReport } from '../goals/GoalsReport';
-import { InsightsReport } from '../insights/InsightsReport';
-import { JourneyReport } from '../journey/JourneyReport';
-import { RetentionReport } from '../retention/RetentionReport';
-import { UTMReport } from '../utm/UTMReport';
-import { RevenueReport } from '../revenue/RevenueReport';
-import AttributionReport from '../attribution/AttributionReport';
-
-const reports = {
- funnel: FunnelReport,
- 'event-data': EventDataReport,
- insights: InsightsReport,
- retention: RetentionReport,
- utm: UTMReport,
- goals: GoalsReport,
- journey: JourneyReport,
- revenue: RevenueReport,
- attribution: AttributionReport,
-};
-
-export function ReportPage({ reportId }: { reportId: string }) {
- const { report } = useReportQuery(reportId);
-
- if (!report) {
- return null;
- }
-
- const ReportComponent = reports[report.type];
-
- return ;
-}
diff --git a/src/app/(main)/reports/[reportId]/page.tsx b/src/app/(main)/reports/[reportId]/page.tsx
deleted file mode 100644
index b228896a..00000000
--- a/src/app/(main)/reports/[reportId]/page.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import { Metadata } from 'next';
-import { ReportPage } from './ReportPage';
-
-export default async function ({ params }: { params: Promise<{ reportId: string }> }) {
- const { reportId } = await params;
-
- return ;
-}
-
-export const metadata: Metadata = {
- title: 'Reports',
-};
diff --git a/src/app/(main)/reports/attribution/AttributionParameters.tsx b/src/app/(main)/reports/attribution/AttributionParameters.tsx
deleted file mode 100644
index 15d737c9..00000000
--- a/src/app/(main)/reports/attribution/AttributionParameters.tsx
+++ /dev/null
@@ -1,174 +0,0 @@
-import { useMessages } from '@/components/hooks';
-import { Eye, Bolt, Plus } from '@/components/icons';
-import { useContext, useState } from 'react';
-import {
- Button,
- Select,
- Form,
- FormButtons,
- FormField,
- Icon,
- ListItem,
- Popover,
- DialogTrigger,
- Toggle,
- FormSubmitButton,
-} from '@umami/react-zen';
-import { BaseParameters } from '../[reportId]/BaseParameters';
-import { ParameterList } from '../[reportId]/ParameterList';
-import { ReportContext } from '../[reportId]/Report';
-import { FunnelStepAddForm } from '../funnel/FunnelStepAddForm';
-import { AttributionStepAddForm } from './AttributionStepAddForm';
-import { useRevenueValuesQuery } from '@/components/hooks/queries/useRevenueValuesQuery';
-
-export function AttributionParameters() {
- const { report, runReport, updateReport, isRunning } = useContext(ReportContext);
- const { formatMessage, labels } = useMessages();
- const { id, parameters } = report || {};
- const { websiteId, dateRange, steps } = parameters || {};
- const queryEnabled = websiteId && dateRange && steps.length > 0;
- const [model, setModel] = useState('');
- const [revenueMode, setRevenueMode] = useState(false);
-
- const { data: currencyValues = [] } = useRevenueValuesQuery(
- websiteId,
- dateRange?.startDate,
- dateRange?.endDate,
- );
-
- const handleSubmit = (data: any, e: any) => {
- if (revenueMode === false) {
- delete data.currency;
- }
-
- e.stopPropagation();
- e.preventDefault();
- runReport(data);
- };
-
- const handleCheck = () => {
- setRevenueMode(!revenueMode);
- };
-
- const handleAddStep = (step: { type: string; value: string }) => {
- if (step.type === 'url') {
- setRevenueMode(false);
- }
- updateReport({ parameters: { steps: parameters.steps.concat(step) } });
- };
-
- const handleUpdateStep = (
- close: () => void,
- index: number,
- step: { type: string; value: string },
- ) => {
- if (step.type === 'url') {
- setRevenueMode(false);
- }
- const steps = [...parameters.steps];
- steps[index] = step;
- updateReport({ parameters: { steps } });
- close();
- };
-
- const handleRemoveStep = (index: number) => {
- const steps = [...parameters.steps];
- delete steps[index];
- updateReport({ parameters: { steps: steps.filter(n => n) } });
- };
-
- const AddStepButton = () => {
- return (
-
-
-
-
-
-
- );
- };
-
- const items = [
- { id: 'first-click', label: 'First-Click', value: 'firstClick' },
- { id: 'last-click', label: 'Last-Click', value: 'lastClick' },
- ];
-
- const onModelChange = (value: any) => {
- setModel(value);
- updateReport({ parameters: { model } });
- };
-
- return (
-
- );
-}
diff --git a/src/app/(main)/reports/attribution/AttributionReport.tsx b/src/app/(main)/reports/attribution/AttributionReport.tsx
deleted file mode 100644
index e7981592..00000000
--- a/src/app/(main)/reports/attribution/AttributionReport.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import { Network } from '@/components/icons';
-import { REPORT_TYPES } from '@/lib/constants';
-import { Report } from '../[reportId]/Report';
-import { ReportBody } from '../[reportId]/ReportBody';
-import { ReportHeader } from '../[reportId]/ReportHeader';
-import { ReportMenu } from '../[reportId]/ReportMenu';
-import { AttributionParameters } from './AttributionParameters';
-import { AttributionView } from './AttributionView';
-
-const defaultParameters = {
- type: REPORT_TYPES.attribution,
- parameters: { model: 'firstClick', steps: [] },
-};
-
-export default function AttributionReport({ reportId }: { reportId?: string }) {
- return (
-
- } />
-
-
-
-
-
-
-
- );
-}
diff --git a/src/app/(main)/reports/attribution/AttributionReportPage.tsx b/src/app/(main)/reports/attribution/AttributionReportPage.tsx
deleted file mode 100644
index ed730704..00000000
--- a/src/app/(main)/reports/attribution/AttributionReportPage.tsx
+++ /dev/null
@@ -1,6 +0,0 @@
-'use client';
-import AttributionReport from './AttributionReport';
-
-export default function AttributionReportPage() {
- return ;
-}
diff --git a/src/app/(main)/reports/attribution/AttributionStepAddForm.tsx b/src/app/(main)/reports/attribution/AttributionStepAddForm.tsx
deleted file mode 100644
index 8a81c06f..00000000
--- a/src/app/(main)/reports/attribution/AttributionStepAddForm.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import { useState } from 'react';
-import { useMessages } from '@/components/hooks';
-import {
- Button,
- FormButtons,
- FormField,
- TextField,
- Row,
- Column,
- Select,
- ListItem,
-} from '@umami/react-zen';
-
-export interface AttributionStepAddFormProps {
- type?: string;
- value?: string;
- onChange?: (step: { type: string; value: string }) => void;
-}
-
-export function AttributionStepAddForm({
- type: defaultType = 'url',
- value: defaultValue = '',
- onChange,
-}: AttributionStepAddFormProps) {
- const [type, setType] = useState(defaultType);
- const [value, setValue] = useState(defaultValue);
- const { formatMessage, labels } = useMessages();
- const items = [
- { id: 'url', label: formatMessage(labels.url), value: 'url' },
- { id: 'event', 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();
- }
- };
-
- return (
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-export default AttributionStepAddForm;
diff --git a/src/app/(main)/reports/attribution/AttributionView.tsx b/src/app/(main)/reports/attribution/AttributionView.tsx
deleted file mode 100644
index 3e560f36..00000000
--- a/src/app/(main)/reports/attribution/AttributionView.tsx
+++ /dev/null
@@ -1,133 +0,0 @@
-import { useContext } from 'react';
-import { PieChart } from '@/components/charts/PieChart';
-import { useMessages } from '@/components/hooks';
-import { GridRow } from '@/components/common/GridRow';
-import { ListTable } from '@/components/metrics/ListTable';
-import { MetricCard } from '@/components/metrics/MetricCard';
-import { MetricsBar } from '@/components/metrics/MetricsBar';
-import { CHART_COLORS } from '@/lib/constants';
-import { formatLongNumber } from '@/lib/format';
-import { ReportContext } from '../[reportId]/Report';
-
-export interface AttributionViewProps {
- isLoading?: boolean;
-}
-
-export function AttributionView({ isLoading }: AttributionViewProps) {
- const { formatMessage, labels } = useMessages();
- const { report } = useContext(ReportContext);
- const {
- data,
- parameters: { currency },
- } = report || {};
- const ATTRIBUTION_PARAMS = [
- { value: 'referrer', label: formatMessage(labels.referrers) },
- { value: 'paidAds', label: formatMessage(labels.paidAds) },
- ];
-
- if (!data) {
- return null;
- }
-
- const { pageviews, visitors, visits } = data.total;
-
- const metrics = data
- ? [
- {
- value: pageviews,
- label: formatMessage(labels.views),
- formatValue: formatLongNumber,
- },
- {
- value: visits,
- label: formatMessage(labels.visits),
- formatValue: formatLongNumber,
- },
- {
- value: visitors,
- label: formatMessage(labels.visitors),
- formatValue: formatLongNumber,
- },
- ]
- : [];
-
- function UTMTable(UTMTableProps: { data: any; title: string; utm: string }) {
- const { data, title, utm } = UTMTableProps;
- const total = data[utm].reduce((sum, { value }) => {
- return +sum + +value;
- }, 0);
-
- return (
- ({
- x: name,
- y: Number(value),
- z: (value / total) * 100,
- }))}
- />
- );
- }
-
- return (
-
-
- {metrics?.map(({ label, value, formatValue }) => {
- return ;
- })}
-
- {ATTRIBUTION_PARAMS.map(({ value, label }) => {
- const items = data[value];
- const total = items.reduce((sum, { value }) => {
- return +sum + +value;
- }, 0);
-
- const chartData = {
- labels: items.map(({ name }) => name),
- datasets: [
- {
- data: items.map(({ value }) => value),
- backgroundColor: CHART_COLORS,
- borderWidth: 0,
- },
- ],
- };
-
- return (
-
-
-
{label}
-
({
- x: name,
- y: Number(value),
- z: (value / total) * 100,
- }))}
- />
-
-
-
- );
- })}
- <>
-
-
-
-
-
-
-
-
-
- >
-
- );
-}
-
-export default AttributionView;
diff --git a/src/app/(main)/reports/attribution/page.tsx b/src/app/(main)/reports/attribution/page.tsx
deleted file mode 100644
index 9efd6220..00000000
--- a/src/app/(main)/reports/attribution/page.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import AttributionReportPage from './AttributionReportPage';
-import { Metadata } from 'next';
-
-export default function () {
- return ;
-}
-
-export const metadata: Metadata = {
- title: 'Attribution Report',
-};
diff --git a/src/app/(main)/reports/create/ReportCreatePage.tsx b/src/app/(main)/reports/create/ReportCreatePage.tsx
deleted file mode 100644
index 4a04d026..00000000
--- a/src/app/(main)/reports/create/ReportCreatePage.tsx
+++ /dev/null
@@ -1,6 +0,0 @@
-'use client';
-import { ReportTemplates } from './ReportTemplates';
-
-export function ReportCreatePage() {
- return ;
-}
diff --git a/src/app/(main)/reports/create/ReportTemplates.tsx b/src/app/(main)/reports/create/ReportTemplates.tsx
deleted file mode 100644
index 68ea0213..00000000
--- a/src/app/(main)/reports/create/ReportTemplates.tsx
+++ /dev/null
@@ -1,108 +0,0 @@
-import { Icon, Text, Row, Column, Grid } from '@umami/react-zen';
-import { useMessages, useNavigation } from '@/components/hooks';
-import {
- Lightbulb,
- Funnel,
- Magnet,
- Tag,
- Target,
- Path,
- Money,
- Network,
- Plus,
-} from '@/components/icons';
-import { SectionHeader } from '@/components/common/SectionHeader';
-import { LinkButton } from '@/components/common/LinkButton';
-
-export function ReportTemplates({ showHeader = true }: { showHeader?: boolean }) {
- const { formatMessage, labels } = useMessages();
- const { renderTeamUrl } = useNavigation();
-
- const reports = [
- {
- title: formatMessage(labels.insights),
- description: formatMessage(labels.insightsDescription),
- url: renderTeamUrl('/reports/insights'),
- icon: ,
- },
- {
- title: formatMessage(labels.funnel),
- description: formatMessage(labels.funnelDescription),
- url: renderTeamUrl('/reports/funnel'),
- icon: ,
- },
- {
- title: formatMessage(labels.retention),
- description: formatMessage(labels.retentionDescription),
- url: renderTeamUrl('/reports/retention'),
- icon: ,
- },
- {
- title: formatMessage(labels.utm),
- description: formatMessage(labels.utmDescription),
- url: renderTeamUrl('/reports/utm'),
- icon: ,
- },
- {
- title: formatMessage(labels.goals),
- description: formatMessage(labels.goalsDescription),
- url: renderTeamUrl('/reports/goals'),
- icon: ,
- },
- {
- title: formatMessage(labels.journey),
- description: formatMessage(labels.journeyDescription),
- url: renderTeamUrl('/reports/journey'),
- icon: ,
- },
- {
- title: formatMessage(labels.revenue),
- description: formatMessage(labels.revenueDescription),
- url: renderTeamUrl('/reports/revenue'),
- icon: ,
- },
- {
- title: formatMessage(labels.attribution),
- description: formatMessage(labels.attributionDescription),
- url: renderTeamUrl('/reports/attribution'),
- icon: ,
- },
- ];
-
- return (
- <>
- {showHeader && }
-
- {reports.map(({ title, description, url, icon }) => {
- return (
-
- );
- })}
-
- >
- );
-}
-
-function ReportItem({ title, description, url, icon }) {
- const { formatMessage, labels } = useMessages();
-
- return (
-
-
- {icon}
-
- {title}
-
-
- {description}
-
-
-
-
-
- {formatMessage(labels.create)}
-
-
-
- );
-}
diff --git a/src/app/(main)/reports/create/page.tsx b/src/app/(main)/reports/create/page.tsx
deleted file mode 100644
index 608367fc..00000000
--- a/src/app/(main)/reports/create/page.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import { ReportCreatePage } from './ReportCreatePage';
-import { Metadata } from 'next';
-
-export default function () {
- return ;
-}
-
-export const metadata: Metadata = {
- title: 'Create Report',
-};
diff --git a/src/app/(main)/reports/event-data/EventDataParameters.module.css b/src/app/(main)/reports/event-data/EventDataParameters.module.css
deleted file mode 100644
index 06b62414..00000000
--- a/src/app/(main)/reports/event-data/EventDataParameters.module.css
+++ /dev/null
@@ -1,12 +0,0 @@
-.parameter {
- display: flex;
- gap: 10px;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- min-width: 0;
-}
-
-.op {
- font-weight: bold;
-}
diff --git a/src/app/(main)/reports/event-data/EventDataParameters.tsx b/src/app/(main)/reports/event-data/EventDataParameters.tsx
deleted file mode 100644
index e93b5d23..00000000
--- a/src/app/(main)/reports/event-data/EventDataParameters.tsx
+++ /dev/null
@@ -1,142 +0,0 @@
-import {
- Form,
- FormField,
- FormButtons,
- FormSubmitButton,
- DialogTrigger,
- Icon,
- Popover,
-} from '@umami/react-zen';
-import { Empty } from '@/components/common/Empty';
-import { Plus } from '@/components/icons';
-import { useApi, useMessages, useReport } from '@/components/hooks';
-import { DATA_TYPES, REPORT_PARAMETERS } from '@/lib/constants';
-import { FieldAddForm } from '../[reportId]/FieldAddForm';
-import { ParameterList } from '../[reportId]/ParameterList';
-import { BaseParameters } from '../[reportId]/BaseParameters';
-import styles from './EventDataParameters.module.css';
-
-function useFields(websiteId, startDate, endDate) {
- const { get, useQuery } = useApi();
- const { data, error, isLoading } = useQuery({
- queryKey: ['fields', websiteId, startDate, endDate],
- queryFn: () =>
- get('/reports/event-data', {
- websiteId,
- startAt: +startDate,
- endAt: +endDate,
- }),
- enabled: !!(websiteId && startDate && endDate),
- });
-
- return { data, error, isLoading };
-}
-
-export function EventDataParameters() {
- const { report, runReport, updateReport, isRunning } = useReport();
- const { formatMessage, labels, messages } = useMessages();
- const { id, parameters } = report || {};
- const { websiteId, dateRange, fields, filters, groups } = parameters || {};
- const { startDate, endDate } = dateRange || {};
- const queryEnabled = websiteId && dateRange && fields?.length;
- const { data, error } = useFields(websiteId, startDate, endDate);
- const parametersSelected = websiteId && startDate && endDate;
- const hasData = data?.length !== 0;
-
- const parameterGroups = [
- { label: formatMessage(labels.fields), group: REPORT_PARAMETERS.fields },
- { label: formatMessage(labels.filters), group: REPORT_PARAMETERS.filters },
- ];
-
- const parameterData = {
- fields,
- filters,
- groups,
- };
-
- const handleSubmit = (values: any) => {
- runReport(values);
- };
-
- const handleAdd = (group: string, value: any) => {
- const data = parameterData[group];
-
- if (!data.find(({ name }) => name === value?.name)) {
- updateReport({ parameters: { [group]: data.concat(value) } });
- }
- };
-
- const handleRemove = (group: string) => {
- const data = [...parameterData[group]];
- updateReport({ parameters: { [group]: data.filter(({ name }) => name !== group) } });
- };
-
- const AddButton = ({ group, onAdd }) => {
- return (
-
-
-
-
-
- {({ close }: any) => {
- return (
- ({
- name: dataKey,
- type: DATA_TYPES[eventDataType],
- }))}
- group={group}
- onAdd={onAdd}
- onClose={close}
- />
- );
- }}
-
-
- );
- };
-
- return (
-
- );
-}
diff --git a/src/app/(main)/reports/event-data/EventDataReport.tsx b/src/app/(main)/reports/event-data/EventDataReport.tsx
deleted file mode 100644
index 6c6afef1..00000000
--- a/src/app/(main)/reports/event-data/EventDataReport.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Report } from '../[reportId]/Report';
-import { ReportHeader } from '../[reportId]/ReportHeader';
-import { ReportMenu } from '../[reportId]/ReportMenu';
-import { ReportBody } from '../[reportId]/ReportBody';
-import { EventDataParameters } from './EventDataParameters';
-import { EventDataTable } from './EventDataTable';
-import { Nodes } from '@/components/icons';
-
-const defaultParameters = {
- type: 'event-data',
- parameters: { fields: [], filters: [] },
-};
-
-export function EventDataReport({ reportId }: { reportId?: string }) {
- return (
-
- } />
-
-
-
-
-
-
-
- );
-}
diff --git a/src/app/(main)/reports/event-data/EventDataReportPage.tsx b/src/app/(main)/reports/event-data/EventDataReportPage.tsx
deleted file mode 100644
index 92bd0a05..00000000
--- a/src/app/(main)/reports/event-data/EventDataReportPage.tsx
+++ /dev/null
@@ -1,6 +0,0 @@
-'use client';
-import { EventDataReport } from './EventDataReport';
-
-export function EventDataReportPage() {
- return ;
-}
diff --git a/src/app/(main)/reports/event-data/EventDataTable.tsx b/src/app/(main)/reports/event-data/EventDataTable.tsx
deleted file mode 100644
index 7aec1b77..00000000
--- a/src/app/(main)/reports/event-data/EventDataTable.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import { DataTable, DataColumn } from '@umami/react-zen';
-import { useMessages, useReport } from '@/components/hooks';
-
-export function EventDataTable() {
- const { report } = useReport();
- const { formatMessage, labels } = useMessages();
-
- return (
-
-
-
-
-
- );
-}
diff --git a/src/app/(main)/reports/event-data/page.tsx b/src/app/(main)/reports/event-data/page.tsx
deleted file mode 100644
index 88875408..00000000
--- a/src/app/(main)/reports/event-data/page.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import { Metadata } from 'next';
-import { EventDataReportPage } from './EventDataReportPage';
-
-export default function () {
- return ;
-}
-
-export const metadata: Metadata = {
- title: 'Event Data Report',
-};
diff --git a/src/app/(main)/reports/insights/InsightsParameters.tsx b/src/app/(main)/reports/insights/InsightsParameters.tsx
deleted file mode 100644
index 944cfe58..00000000
--- a/src/app/(main)/reports/insights/InsightsParameters.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import { useMessages, useReport } from '@/components/hooks';
-import { Form, FormButtons, FormSubmitButton } from '@umami/react-zen';
-import { BaseParameters } from '../[reportId]/BaseParameters';
-import { FieldParameters } from '../[reportId]/FieldParameters';
-import { FilterParameters } from '../[reportId]/FilterParameters';
-
-export function InsightsParameters() {
- const { report, runReport, isRunning } = useReport();
- const { formatMessage, labels } = useMessages();
- const { id, parameters } = report || {};
- const { websiteId, dateRange, fields, filters } = parameters || {};
- const { startDate, endDate } = dateRange || {};
- const parametersSelected = websiteId && startDate && endDate;
- const queryEnabled = websiteId && dateRange && (fields?.length || filters?.length);
-
- const handleSubmit = (values: any) => {
- runReport(values);
- };
-
- return (
-
- );
-}
diff --git a/src/app/(main)/reports/insights/InsightsReport.tsx b/src/app/(main)/reports/insights/InsightsReport.tsx
deleted file mode 100644
index 6f0f6a71..00000000
--- a/src/app/(main)/reports/insights/InsightsReport.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import { Report } from '../[reportId]/Report';
-import { ReportHeader } from '../[reportId]/ReportHeader';
-import { ReportMenu } from '../[reportId]/ReportMenu';
-import { ReportBody } from '../[reportId]/ReportBody';
-import { InsightsParameters } from './InsightsParameters';
-import { InsightsTable } from './InsightsTable';
-import { Lightbulb } from '@/components/icons';
-import { REPORT_TYPES } from '@/lib/constants';
-
-const defaultParameters = {
- type: REPORT_TYPES.insights,
- parameters: { fields: [], filters: [] },
-};
-
-export function InsightsReport({ reportId }: { reportId?: string }) {
- return (
-
- } />
-
-
-
-
-
-
-
- );
-}
diff --git a/src/app/(main)/reports/insights/InsightsReportPage.tsx b/src/app/(main)/reports/insights/InsightsReportPage.tsx
deleted file mode 100644
index 80cb42ff..00000000
--- a/src/app/(main)/reports/insights/InsightsReportPage.tsx
+++ /dev/null
@@ -1,6 +0,0 @@
-'use client';
-import { InsightsReport } from './InsightsReport';
-
-export function InsightsReportPage() {
- return ;
-}
diff --git a/src/app/(main)/reports/insights/InsightsTable.tsx b/src/app/(main)/reports/insights/InsightsTable.tsx
deleted file mode 100644
index e1bd59df..00000000
--- a/src/app/(main)/reports/insights/InsightsTable.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import { useEffect, useState } from 'react';
-import { DataTable, DataColumn } from '@umami/react-zen';
-import { useFormat, useMessages, useReport } from '@/components/hooks';
-import { EmptyPlaceholder } from '@/components/common/EmptyPlaceholder';
-import { formatShortTime } from '@/lib/format';
-
-export function InsightsTable() {
- const [fields, setFields] = useState([]);
- const { report } = useReport();
- const { formatMessage, labels } = useMessages();
- const { formatValue } = useFormat();
-
- useEffect(
- () => {
- setFields(report?.parameters?.fields);
- },
- // eslint-disable-next-line react-hooks/exhaustive-deps
- [report?.data],
- );
-
- if (!fields || !report?.parameters) {
- return ;
- }
-
- return (
-
- {fields.map(({ name, label }) => {
- return (
-
- {row => formatValue(row[name], name)}
-
- );
- })}
-
- {(row: any) => row?.views?.toLocaleString()}
-
-
- {(row: any) => row?.visits?.toLocaleString()}
-
-
- {(row: any) => row?.visitors?.toLocaleString()}
-
-
- {(row: any) => {
- const n = (Math.min(row?.visits, row?.bounces) / row?.visits) * 100;
- return Math.round(+n) + '%';
- }}
-
-
- {(row: any) => {
- const n = row?.totaltime / row?.visits;
- return `${+n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`;
- }}
-
-
- );
-}
diff --git a/src/app/(main)/reports/insights/page.tsx b/src/app/(main)/reports/insights/page.tsx
deleted file mode 100644
index 7b9b8722..00000000
--- a/src/app/(main)/reports/insights/page.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import { InsightsReportPage } from './InsightsReportPage';
-import { Metadata } from 'next';
-
-export default function () {
- return ;
-}
-
-export const metadata: Metadata = {
- title: 'Insights Report',
-};
diff --git a/src/app/(main)/reports/page.tsx b/src/app/(main)/reports/page.tsx
deleted file mode 100644
index 621281c4..00000000
--- a/src/app/(main)/reports/page.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import { ReportsPage } from './ReportsPage';
-import { Metadata } from 'next';
-
-export default function () {
- return ;
-}
-
-export const metadata: Metadata = {
- title: 'Reports',
-};
diff --git a/src/app/(main)/websites/[websiteId]/funnels/Funnel.tsx b/src/app/(main)/websites/[websiteId]/funnels/Funnel.tsx
index 6281b5a2..2ca2e41b 100644
--- a/src/app/(main)/websites/[websiteId]/funnels/Funnel.tsx
+++ b/src/app/(main)/websites/[websiteId]/funnels/Funnel.tsx
@@ -43,7 +43,11 @@ export function Funnel({ id, name, type, parameters, websiteId, startDate, endDa
{({ close }) => {
return (
-