diff --git a/src/app/(main)/admin/users/[userId]/UserEditForm.tsx b/src/app/(main)/admin/users/[userId]/UserEditForm.tsx index 68aa7f6e..28bf030f 100644 --- a/src/app/(main)/admin/users/[userId]/UserEditForm.tsx +++ b/src/app/(main)/admin/users/[userId]/UserEditForm.tsx @@ -30,11 +30,7 @@ export function UserEditForm({ userId, onSave }: { userId: string; onSave?: () = }; return ( -
+ diff --git a/src/app/(main)/links/LinksDataTable.tsx b/src/app/(main)/links/LinksDataTable.tsx index 87da5984..0b3d660b 100644 --- a/src/app/(main)/links/LinksDataTable.tsx +++ b/src/app/(main)/links/LinksDataTable.tsx @@ -2,13 +2,13 @@ import { DataGrid } from '@/components/common/DataGrid'; import { useLinksQuery, useNavigation } from '@/components/hooks'; import { LinksTable } from './LinksTable'; -export function LinksDataTable({ showActions = false }: { showActions?: boolean }) { +export function LinksDataTable() { const { teamId } = useNavigation(); const query = useLinksQuery({ teamId }); return ( - {({ data }) => } + {({ data }) => } ); } diff --git a/src/app/(main)/links/LinksPage.tsx b/src/app/(main)/links/LinksPage.tsx index cdaf8fce..a6e4c7c4 100644 --- a/src/app/(main)/links/LinksPage.tsx +++ b/src/app/(main)/links/LinksPage.tsx @@ -4,30 +4,21 @@ import { LinksDataTable } from '@/app/(main)/links/LinksDataTable'; import { PageBody } from '@/components/common/PageBody'; import { PageHeader } from '@/components/common/PageHeader'; import { Panel } from '@/components/common/Panel'; -import { useLoginQuery, useMessages, useNavigation, useTeamMembersQuery } from '@/components/hooks'; -import { ROLES } from '@/lib/constants'; +import { useMessages, useNavigation } from '@/components/hooks'; import { LinkAddButton } from './LinkAddButton'; export function LinksPage() { - const { user } = useLoginQuery(); const { formatMessage, labels } = useMessages(); const { teamId } = useNavigation(); - const { data } = useTeamMembersQuery(teamId); - - const showActions = - (teamId && - data?.data.filter(team => team.userId === user.id && team.role !== ROLES.teamViewOnly) - .length > 0) || - (!teamId && user.role !== ROLES.viewOnly); return ( - {showActions && } + - + diff --git a/src/app/(main)/links/LinksTable.tsx b/src/app/(main)/links/LinksTable.tsx index 62eb0fb8..a3b4a86a 100644 --- a/src/app/(main)/links/LinksTable.tsx +++ b/src/app/(main)/links/LinksTable.tsx @@ -6,11 +6,7 @@ import { useMessages, useNavigation, useSlug } from '@/components/hooks'; import { LinkDeleteButton } from './LinkDeleteButton'; import { LinkEditButton } from './LinkEditButton'; -export interface LinksTableProps extends DataTableProps { - showActions?: boolean; -} - -export function LinksTable({ showActions, ...props }: LinksTableProps) { +export function LinksTable(props: DataTableProps) { const { formatMessage, labels } = useMessages(); const { websiteId, renderUrl } = useNavigation(); const { getSlugUrl } = useSlug('link'); @@ -40,18 +36,16 @@ export function LinksTable({ showActions, ...props }: LinksTableProps) { {(row: any) => } - {showActions && ( - - {({ id, name }: any) => { - return ( - - - - - ); - }} - - )} + + {({ id, name }: any) => { + return ( + + + + + ); + }} + ); } diff --git a/src/app/(main)/pixels/PixelsDataTable.tsx b/src/app/(main)/pixels/PixelsDataTable.tsx index 6a9a9162..51b8c5a0 100644 --- a/src/app/(main)/pixels/PixelsDataTable.tsx +++ b/src/app/(main)/pixels/PixelsDataTable.tsx @@ -2,13 +2,13 @@ import { DataGrid } from '@/components/common/DataGrid'; import { useNavigation, usePixelsQuery } from '@/components/hooks'; import { PixelsTable } from './PixelsTable'; -export function PixelsDataTable({ showActions = false }: { showActions?: boolean }) { +export function PixelsDataTable() { const { teamId } = useNavigation(); const query = usePixelsQuery({ teamId }); return ( - {({ data }) => } + {({ data }) => } ); } diff --git a/src/app/(main)/pixels/PixelsPage.tsx b/src/app/(main)/pixels/PixelsPage.tsx index 91ddcdcd..4f6acefe 100644 --- a/src/app/(main)/pixels/PixelsPage.tsx +++ b/src/app/(main)/pixels/PixelsPage.tsx @@ -3,31 +3,22 @@ import { Column } from '@umami/react-zen'; import { PageBody } from '@/components/common/PageBody'; import { PageHeader } from '@/components/common/PageHeader'; import { Panel } from '@/components/common/Panel'; -import { useLoginQuery, useMessages, useNavigation, useTeamMembersQuery } from '@/components/hooks'; -import { ROLES } from '@/lib/constants'; +import { useMessages, useNavigation } from '@/components/hooks'; import { PixelAddButton } from './PixelAddButton'; import { PixelsDataTable } from './PixelsDataTable'; export function PixelsPage() { - const { user } = useLoginQuery(); const { formatMessage, labels } = useMessages(); const { teamId } = useNavigation(); - const { data } = useTeamMembersQuery(teamId); - - const showActions = - (teamId && - data?.data.filter(team => team.userId === user.id && team.role !== ROLES.teamViewOnly) - .length > 0) || - (!teamId && user.role !== ROLES.viewOnly); return ( - {showActions && } + - + diff --git a/src/app/(main)/pixels/PixelsTable.tsx b/src/app/(main)/pixels/PixelsTable.tsx index 57aff4db..48a84589 100644 --- a/src/app/(main)/pixels/PixelsTable.tsx +++ b/src/app/(main)/pixels/PixelsTable.tsx @@ -6,15 +6,10 @@ import { useMessages, useNavigation, useSlug } from '@/components/hooks'; import { PixelDeleteButton } from './PixelDeleteButton'; import { PixelEditButton } from './PixelEditButton'; -export interface PixelsTableProps extends DataTableProps { - showActions?: boolean; -} - -export function PixelsTable({ showActions, ...props }: PixelsTableProps) { +export function PixelsTable(props: DataTableProps) { const { formatMessage, labels } = useMessages(); const { renderUrl } = useNavigation(); const { getSlugUrl } = useSlug('pixel'); - console.log(showActions); return ( @@ -36,20 +31,18 @@ export function PixelsTable({ showActions, ...props }: PixelsTableProps) { {(row: any) => } - {showActions && ( - - {(row: any) => { - const { id, name } = row; + + {(row: any) => { + const { id, name } = row; - return ( - - - - - ); - }} - - )} + return ( + + + + + ); + }} + ); } diff --git a/src/app/(main)/websites/WebsitesPage.tsx b/src/app/(main)/websites/WebsitesPage.tsx index 6f3548a9..31de7047 100644 --- a/src/app/(main)/websites/WebsitesPage.tsx +++ b/src/app/(main)/websites/WebsitesPage.tsx @@ -3,31 +3,22 @@ import { Column } from '@umami/react-zen'; import { PageBody } from '@/components/common/PageBody'; import { PageHeader } from '@/components/common/PageHeader'; import { Panel } from '@/components/common/Panel'; -import { useLoginQuery, useMessages, useNavigation, useTeamMembersQuery } from '@/components/hooks'; -import { ROLES } from '@/lib/constants'; +import { useMessages, useNavigation } from '@/components/hooks'; import { WebsiteAddButton } from './WebsiteAddButton'; import { WebsitesDataTable } from './WebsitesDataTable'; export function WebsitesPage() { - const { user } = useLoginQuery(); const { teamId } = useNavigation(); const { formatMessage, labels } = useMessages(); - const { data } = useTeamMembersQuery(teamId); - - const showActions = - (teamId && - data?.data.filter(team => team.userId === user.id && team.role !== ROLES.teamViewOnly) - .length > 0) || - (!teamId && user.role !== ROLES.viewOnly); return ( - {showActions && } + - + diff --git a/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx b/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx index f62d8a4c..55ec0403 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx @@ -1,18 +1,12 @@ 'use client'; import { Column, Tab, TabList, TabPanel, Tabs } from '@umami/react-zen'; -import locale from 'date-fns/locale/af'; -import { type Key, useMemo, useState } from 'react'; +import { type Key, useState } from 'react'; import { SessionModal } from '@/app/(main)/websites/[websiteId]/sessions/SessionModal'; import { WebsiteControls } from '@/app/(main)/websites/[websiteId]/WebsiteControls'; -import { LoadingPanel } from '@/components/common/LoadingPanel'; import { Panel } from '@/components/common/Panel'; import { useMessages } from '@/components/hooks'; -import { useEventStatsQuery } from '@/components/hooks/queries/useEventStatsQuery'; import { EventsChart } from '@/components/metrics/EventsChart'; -import { MetricCard } from '@/components/metrics/MetricCard'; -import { MetricsBar } from '@/components/metrics/MetricsBar'; import { MetricsTable } from '@/components/metrics/MetricsTable'; -import { formatLongNumber } from '@/lib/format'; import { getItem, setItem } from '@/lib/storage'; import { EventProperties } from './EventProperties'; import { EventsDataTable } from './EventsDataTable'; @@ -21,61 +15,16 @@ const KEY_NAME = 'umami.events.tab'; export function EventsPage({ websiteId }) { const [tab, setTab] = useState(getItem(KEY_NAME) || 'chart'); - const { formatMessage, labels, getErrorMessage } = useMessages(); - const { data, isLoading, isFetching, error } = useEventStatsQuery({ - websiteId, - }); + const { formatMessage, labels } = useMessages(); const handleSelect = (value: Key) => { setItem(KEY_NAME, value); setTab(value); }; - const metrics = useMemo(() => { - if (!data) return []; - - const { events, visitors, visits, uniqueEvents } = data || {}; - - return [ - { - value: visitors, - label: formatMessage(labels.visitors), - formatValue: formatLongNumber, - }, - { - value: visits, - label: formatMessage(labels.visits), - formatValue: formatLongNumber, - }, - { - value: events, - label: formatMessage(labels.events), - formatValue: formatLongNumber, - }, - { - value: uniqueEvents, - label: formatMessage(labels.uniqueEvents), - formatValue: formatLongNumber, - }, - ] as any; - }, [data, locale]); - return ( - - - {metrics?.map(({ label, value, formatValue }) => { - return ; - })} - - handleSelect(key)}> diff --git a/src/app/api/websites/[websiteId]/events/stats/route.ts b/src/app/api/websites/[websiteId]/events/stats/route.ts deleted file mode 100644 index 61e151d4..00000000 --- a/src/app/api/websites/[websiteId]/events/stats/route.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { z } from 'zod'; -import { getQueryFilters, parseRequest } from '@/lib/request'; -import { json, unauthorized } from '@/lib/response'; -import { dateRangeParams, filterParams } from '@/lib/schema'; -import { canViewWebsite } from '@/permissions'; -import { getWebsiteEventStats } from '@/queries/sql/events/getWebsiteEventStats'; - -export async function GET( - request: Request, - { params }: { params: Promise<{ websiteId: string }> }, -) { - const schema = z.object({ - ...dateRangeParams, - ...filterParams, - }); - - const { auth, query, error } = await parseRequest(request, schema); - - if (error) { - return error(); - } - - const { websiteId } = await params; - - if (!(await canViewWebsite(auth, websiteId))) { - return unauthorized(); - } - - const filters = await getQueryFilters(query, websiteId); - - const data = await getWebsiteEventStats(websiteId, filters); - - return json({ data }); -} diff --git a/src/components/hooks/queries/useEventStatsQuery.ts b/src/components/hooks/queries/useEventStatsQuery.ts deleted file mode 100644 index 44316ca5..00000000 --- a/src/components/hooks/queries/useEventStatsQuery.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { UseQueryOptions } from '@tanstack/react-query'; -import { useDateParameters } from '@/components/hooks/useDateParameters'; -import { useApi } from '../useApi'; -import { useFilterParameters } from '../useFilterParameters'; - -export interface EventStatsData { - events: number; - visitors: number; - visits: number; - uniqueEvents: number; -} - -type EventStatsApiResponse = { - data: EventStatsData; -}; - -export function useEventStatsQuery( - { websiteId }: { websiteId: string }, - options?: UseQueryOptions, -) { - const { get, useQuery } = useApi(); - const { startAt, endAt } = useDateParameters(); - const filters = useFilterParameters(); - - return useQuery({ - queryKey: ['websites:events:stats', { websiteId, startAt, endAt, ...filters }], - queryFn: () => - get(`/websites/${websiteId}/events/stats`, { - startAt, - endAt, - ...filters, - }), - select: response => response.data, - enabled: !!websiteId, - ...options, - }); -} diff --git a/src/components/input/FilterEditForm.tsx b/src/components/input/FilterEditForm.tsx index 87acc515..9221e3a2 100644 --- a/src/components/input/FilterEditForm.tsx +++ b/src/components/input/FilterEditForm.tsx @@ -22,7 +22,6 @@ export function FilterEditForm({ websiteId, onChange, onClose }: FilterEditFormP const [currentCohort, setCurrentCohort] = useState(cohort); const { isMobile } = useMobile(); const excludeFilters = pathname.includes('/pixels') || pathname.includes('/links'); - const excludeEvent = !pathname.endsWith('/events'); const handleReset = () => { setCurrentFilters([]); @@ -63,11 +62,7 @@ export function FilterEditForm({ websiteId, onChange, onClose }: FilterEditFormP value={currentFilters} onChange={setCurrentFilters} exclude={ - excludeFilters - ? ['path', 'title', 'hostname', 'distinctId', 'tag', 'event'] - : excludeEvent - ? ['event'] - : [] + excludeFilters ? ['path', 'title', 'hostname', 'distinctId', 'tag', 'event'] : [] } /> diff --git a/src/components/messages.ts b/src/components/messages.ts index de29c306..3d7388cd 100644 --- a/src/components/messages.ts +++ b/src/components/messages.ts @@ -146,7 +146,6 @@ export const labels = defineMessages({ poweredBy: { id: 'label.powered-by', defaultMessage: 'Powered by {name}' }, pageViews: { id: 'label.page-views', defaultMessage: 'Page views' }, uniqueVisitors: { id: 'label.unique-visitors', defaultMessage: 'Unique visitors' }, - uniqueEvents: { id: 'label.unique-events', defaultMessage: 'Unique Events' }, bounceRate: { id: 'label.bounce-rate', defaultMessage: 'Bounce rate' }, viewsPerVisit: { id: 'label.views-per-visit', defaultMessage: 'Views per visit' }, visitDuration: { id: 'label.visit-duration', defaultMessage: 'Visit duration' }, diff --git a/src/queries/sql/events/getWebsiteEventStats.ts b/src/queries/sql/events/getWebsiteEventStats.ts deleted file mode 100644 index 27179d10..00000000 --- a/src/queries/sql/events/getWebsiteEventStats.ts +++ /dev/null @@ -1,97 +0,0 @@ -import clickhouse from '@/lib/clickhouse'; -import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db'; -import prisma from '@/lib/prisma'; -import type { QueryFilters } from '@/lib/types'; - -const FUNCTION_NAME = 'getWebsiteEventStats'; - -export interface WebsiteEventStatsData { - events: number; - visitors: number; - visits: number; - uniqueEvents: number; -} - -export async function getWebsiteEventStats( - ...args: [websiteId: string, filters: QueryFilters] -): Promise { - return runQuery({ - [PRISMA]: () => relationalQuery(...args), - [CLICKHOUSE]: () => clickhouseQuery(...args), - }); -} - -async function relationalQuery( - websiteId: string, - filters: QueryFilters, -): Promise { - const { parseFilters, rawQuery } = prisma; - const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters({ - ...filters, - websiteId, - }); - - return rawQuery( - ` - select - cast(coalesce(sum(t.c), 0) as bigint) as "events", - count(distinct t.session_id) as "visitors", - count(distinct t.visit_id) as "visits", - count(distinct t.event_name) as "uniqueEvents" - from ( - select - website_event.session_id, - website_event.visit_id, - website_event.event_name, - count(*) as "c" - from website_event - ${cohortQuery} - ${joinSessionQuery} - where website_event.website_id = {{websiteId::uuid}} - and website_event.created_at between {{startDate}} and {{endDate}} - and website_event.event_type = 2 - ${filterQuery} - group by 1, 2, 3 - ) as t - `, - queryParams, - FUNCTION_NAME, - ).then(result => result?.[0]); -} - -async function clickhouseQuery( - websiteId: string, - filters: QueryFilters, -): Promise { - const { rawQuery, parseFilters } = clickhouse; - const { filterQuery, cohortQuery, queryParams } = parseFilters({ - ...filters, - websiteId, - }); - - return rawQuery( - ` - select - sum(t.c) as "events", - uniq(t.session_id) as "visitors", - uniq(t.visit_id) as "visits", - count(distinct t.event_name) as "uniqueEvents" - from ( - select - session_id, - visit_id, - event_name, - count(*) c - from website_event - ${cohortQuery} - where website_id = {websiteId:UUID} - and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type = 2 - ${filterQuery} - group by session_id, visit_id, event_name - ) as t; - `, - queryParams, - FUNCTION_NAME, - ).then(result => result?.[0]); -}