From 9c3205784146e06dc50f69bc2a1c8bf169745800 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Thu, 1 Aug 2024 21:05:43 -0700 Subject: [PATCH] Redo events tab to show all events. --- .../event-data/EventDataParameters.tsx | 2 +- .../[websiteId]/events/EventDataPage.tsx | 12 ------ .../[websiteId]/events/EventDataTable.tsx | 37 ---------------- .../[websiteId]/events/EventsDataTable.tsx | 25 +++++++++++ .../[websiteId]/events/EventsPage.tsx | 12 ++++++ .../[websiteId]/events/EventsTable.tsx | 43 +++++++++++++++++++ .../[websiteId]/events/WebsiteEventData.tsx | 4 +- .../websites/[websiteId]/events/page.tsx | 4 +- src/components/common/DataTable.tsx | 4 +- .../hooks/queries/useFilterQuery.ts | 8 ++-- .../hooks/queries/useWebsiteEvents.ts | 10 +++-- src/components/messages.ts | 2 + .../events.ts => events/[eventId]/data.ts} | 0 .../api/websites/[websiteId]/events/index.ts | 42 ++++++++++++++++++ .../{events.ts => events/series.ts} | 0 .../{getEvents.ts => getWebsiteEvents.ts} | 2 +- src/queries/analytics/getRealtimeData.ts | 9 +++- src/queries/index.ts | 2 +- 18 files changed, 152 insertions(+), 66 deletions(-) delete mode 100644 src/app/(main)/websites/[websiteId]/events/EventDataPage.tsx delete mode 100644 src/app/(main)/websites/[websiteId]/events/EventDataTable.tsx create mode 100644 src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx create mode 100644 src/app/(main)/websites/[websiteId]/events/EventsPage.tsx create mode 100644 src/app/(main)/websites/[websiteId]/events/EventsTable.tsx rename src/pages/api/{event-data/events.ts => events/[eventId]/data.ts} (100%) create mode 100644 src/pages/api/websites/[websiteId]/events/index.ts rename src/pages/api/websites/[websiteId]/{events.ts => events/series.ts} (100%) rename src/queries/analytics/events/{getEvents.ts => getWebsiteEvents.ts} (97%) diff --git a/src/app/(main)/reports/event-data/EventDataParameters.tsx b/src/app/(main)/reports/event-data/EventDataParameters.tsx index e0afda3f..7b61c112 100644 --- a/src/app/(main)/reports/event-data/EventDataParameters.tsx +++ b/src/app/(main)/reports/event-data/EventDataParameters.tsx @@ -48,7 +48,7 @@ export function EventDataParameters() { groups, }; - const handleSubmit = values => { + const handleSubmit = (values: any) => { runReport(values); }; diff --git a/src/app/(main)/websites/[websiteId]/events/EventDataPage.tsx b/src/app/(main)/websites/[websiteId]/events/EventDataPage.tsx deleted file mode 100644 index 77d367e5..00000000 --- a/src/app/(main)/websites/[websiteId]/events/EventDataPage.tsx +++ /dev/null @@ -1,12 +0,0 @@ -'use client'; -import WebsiteHeader from '../WebsiteHeader'; -import WebsiteEventData from './WebsiteEventData'; - -export default function EventDataPage({ websiteId }) { - return ( - <> - - - - ); -} diff --git a/src/app/(main)/websites/[websiteId]/events/EventDataTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventDataTable.tsx deleted file mode 100644 index 71c36992..00000000 --- a/src/app/(main)/websites/[websiteId]/events/EventDataTable.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import Link from 'next/link'; -import { GridTable, GridColumn } from 'react-basics'; -import { useMessages, useNavigation } from 'components/hooks'; -import Empty from 'components/common/Empty'; -import { DATA_TYPES } from 'lib/constants'; - -export function EventDataTable({ data = [] }) { - const { formatMessage, labels } = useMessages(); - const { renderUrl } = useNavigation(); - - if (data.length === 0) { - return ; - } - - return ( - - - {row => ( - - {row.eventName} - - )} - - - {row => row.fieldName} - - - {row => DATA_TYPES[row.dataType]} - - - {({ total }) => total.toLocaleString()} - - - ); -} - -export default EventDataTable; diff --git a/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx new file mode 100644 index 00000000..f2bf7cb9 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx @@ -0,0 +1,25 @@ +import { useWebsiteEvents } from 'components/hooks'; +import EventsTable from './EventsTable'; +import DataTable from 'components/common/DataTable'; +import { ReactNode } from 'react'; + +export default function EventsDataTable({ + websiteId, + children, +}: { + websiteId?: string; + teamId?: string; + children?: ReactNode; +}) { + const queryResult = useWebsiteEvents(websiteId); + + if (queryResult?.result?.data?.length === 0) { + return children; + } + + return ( + + {({ data }) => } + + ); +} diff --git a/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx b/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx new file mode 100644 index 00000000..8f793d81 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx @@ -0,0 +1,12 @@ +'use client'; +import WebsiteHeader from '../WebsiteHeader'; +import EventsDataTable from './EventsDataTable'; + +export default function EventsPage({ websiteId }) { + return ( + <> + + + + ); +} diff --git a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx new file mode 100644 index 00000000..5a625ee9 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx @@ -0,0 +1,43 @@ +import { GridTable, GridColumn } from 'react-basics'; +import { useLocale, useMessages } from 'components/hooks'; +import Empty from 'components/common/Empty'; +import { formatDistance } from 'date-fns'; +import Profile from 'components/common/Profile'; +import Link from 'next/link'; + +export function EventsTable({ data = [] }) { + const { dateLocale } = useLocale(); + const { formatMessage, labels } = useMessages(); + + if (data.length === 0) { + return ; + } + + return ( + + + + {row => ( + + + + )} + + + {row => formatMessage(row.eventName ? labels.triggeredEvent : labels.viewedPage)} + + + + + {row => + formatDistance(new Date(row.createdAt), new Date(), { + addSuffix: true, + locale: dateLocale, + }) + } + + + ); +} + +export default EventsTable; diff --git a/src/app/(main)/websites/[websiteId]/events/WebsiteEventData.tsx b/src/app/(main)/websites/[websiteId]/events/WebsiteEventData.tsx index d7d24cee..296c8a66 100644 --- a/src/app/(main)/websites/[websiteId]/events/WebsiteEventData.tsx +++ b/src/app/(main)/websites/[websiteId]/events/WebsiteEventData.tsx @@ -1,5 +1,5 @@ import { Flexbox, Loading } from 'react-basics'; -import EventDataTable from './EventDataTable'; +import EventsTable from './EventsTable'; import EventDataValueTable from './EventDataValueTable'; import { EventDataMetricsBar } from './EventDataMetricsBar'; import { useDateRange, useApi, useNavigation } from 'components/hooks'; @@ -33,7 +33,7 @@ export default function WebsiteEventData({ websiteId }) { return ( - {!event && } + {!event && } {isLoading && } {event && data && } diff --git a/src/app/(main)/websites/[websiteId]/events/page.tsx b/src/app/(main)/websites/[websiteId]/events/page.tsx index 24cf4fea..b5dc4d62 100644 --- a/src/app/(main)/websites/[websiteId]/events/page.tsx +++ b/src/app/(main)/websites/[websiteId]/events/page.tsx @@ -1,8 +1,8 @@ import { Metadata } from 'next'; -import EventDataPage from './EventDataPage'; +import EventsPage from './EventsPage'; export default async function ({ params: { websiteId } }) { - return ; + return ; } export const metadata: Metadata = { diff --git a/src/components/common/DataTable.tsx b/src/components/common/DataTable.tsx index ed910def..041c2ba5 100644 --- a/src/components/common/DataTable.tsx +++ b/src/components/common/DataTable.tsx @@ -1,7 +1,7 @@ import { ReactNode } from 'react'; import classNames from 'classnames'; import { Banner, Loading, SearchField } from 'react-basics'; -import { useMessages } from 'components/hooks'; +import { useMessages, useNavigation } from 'components/hooks'; import Empty from 'components/common/Empty'; import Pager from 'components/common/Pager'; import { FilterQueryResult } from 'lib/types'; @@ -35,6 +35,7 @@ export function DataTable({ const { query } = params || {}; const hasData = Boolean(!isLoading && data?.length); const noResults = Boolean(!isLoading && query && !hasData); + const { router, renderUrl } = useNavigation(); const handleSearch = (query: string) => { setParams({ ...params, query, page: params.page ? page : 1 }); @@ -42,6 +43,7 @@ export function DataTable({ const handlePageChange = (page: number) => { setParams({ ...params, query, page }); + router.push(renderUrl({ page })); }; if (error) { diff --git a/src/components/hooks/queries/useFilterQuery.ts b/src/components/hooks/queries/useFilterQuery.ts index 5963d099..82fc4d61 100644 --- a/src/components/hooks/queries/useFilterQuery.ts +++ b/src/components/hooks/queries/useFilterQuery.ts @@ -2,15 +2,17 @@ import { UseQueryOptions } from '@tanstack/react-query'; import { useState } from 'react'; import { useApi } from './useApi'; import { PageResult, PageParams, FilterQueryResult } from 'lib/types'; +import { useNavigation } from '../useNavigation'; export function useFilterQuery({ queryKey, queryFn, ...options }: Omit & { queryFn: (params?: object) => any }): FilterQueryResult { - const [params, setParams] = useState({ + const { query: queryParams } = useNavigation(); + const [params, setParams] = useState({ query: '', - page: 1, + page: +queryParams.page || 1, }); const { useQuery } = useApi(); @@ -21,7 +23,7 @@ export function useFilterQuery({ }); return { - result: data as PageResult, + result: data as PageResult, query, params, setParams, diff --git a/src/components/hooks/queries/useWebsiteEvents.ts b/src/components/hooks/queries/useWebsiteEvents.ts index 588d4fb5..f0132470 100644 --- a/src/components/hooks/queries/useWebsiteEvents.ts +++ b/src/components/hooks/queries/useWebsiteEvents.ts @@ -1,17 +1,19 @@ import useApi from './useApi'; -import { useFilterParams } from '../useFilterParams'; import { UseQueryOptions } from '@tanstack/react-query'; +import { useFilterParams } from '../useFilterParams'; +import { useFilterQuery } from 'components/hooks'; export function useWebsiteEvents( websiteId: string, options?: Omit, ) { - const { get, useQuery } = useApi(); + const { get } = useApi(); const params = useFilterParams(websiteId); - return useQuery({ + return useFilterQuery({ queryKey: ['websites:events', { websiteId, ...params }], - queryFn: () => get(`/websites/${websiteId}/events`, params), + queryFn: pageParams => + get(`/websites/${websiteId}/events`, { ...params, ...pageParams, pageSize: 20 }), enabled: !!websiteId, ...options, }); diff --git a/src/components/messages.ts b/src/components/messages.ts index 2dfe61ee..703ccba3 100644 --- a/src/components/messages.ts +++ b/src/components/messages.ts @@ -130,6 +130,7 @@ export const labels = defineMessages({ selectRole: { id: 'label.select-role', defaultMessage: 'Select role' }, selectDate: { id: 'label.select-date', defaultMessage: 'Select date' }, all: { id: 'label.all', defaultMessage: 'All' }, + session: { id: 'label.session', defaultMessage: 'Session' }, sessions: { id: 'label.sessions', defaultMessage: 'Sessions' }, pageNotFound: { id: 'message.page-not-found', defaultMessage: 'Page not found' }, activityLog: { id: 'label.activity-log', defaultMessage: 'Activity log' }, @@ -275,6 +276,7 @@ export const labels = defineMessages({ lastSeen: { id: 'label.last-seen', defaultMessage: 'Last seen' }, firstSeen: { id: 'label.first-seen', defaultMessage: 'First seen' }, properties: { id: 'label.properties', defaultMessage: 'Properties' }, + path: { id: 'label.path', defaultMessage: 'Path' }, }); export const messages = defineMessages({ diff --git a/src/pages/api/event-data/events.ts b/src/pages/api/events/[eventId]/data.ts similarity index 100% rename from src/pages/api/event-data/events.ts rename to src/pages/api/events/[eventId]/data.ts diff --git a/src/pages/api/websites/[websiteId]/events/index.ts b/src/pages/api/websites/[websiteId]/events/index.ts new file mode 100644 index 00000000..2aa791b1 --- /dev/null +++ b/src/pages/api/websites/[websiteId]/events/index.ts @@ -0,0 +1,42 @@ +import * as yup from 'yup'; +import { canViewWebsite } from 'lib/auth'; +import { useAuth, useCors, useValidate } from 'lib/middleware'; +import { NextApiRequestQueryBody, PageParams } from 'lib/types'; +import { NextApiResponse } from 'next'; +import { methodNotAllowed, ok, unauthorized } from 'next-basics'; +import { pageInfo } from 'lib/schema'; +import { getWebsiteEvents } from 'queries'; + +export interface ReportsRequestQuery extends PageParams { + websiteId: string; +} + +const schema = { + GET: yup.object().shape({ + websiteId: yup.string().uuid().required(), + ...pageInfo, + }), +}; + +export default async ( + req: NextApiRequestQueryBody, + res: NextApiResponse, +) => { + await useCors(req, res); + await useAuth(req, res); + await useValidate(schema, req, res); + + const { websiteId } = req.query; + + if (req.method === 'GET') { + if (!(await canViewWebsite(req.auth, websiteId))) { + return unauthorized(res); + } + + const data = await getWebsiteEvents(websiteId, {}, req.query); + + return ok(res, data); + } + + return methodNotAllowed(res); +}; diff --git a/src/pages/api/websites/[websiteId]/events.ts b/src/pages/api/websites/[websiteId]/events/series.ts similarity index 100% rename from src/pages/api/websites/[websiteId]/events.ts rename to src/pages/api/websites/[websiteId]/events/series.ts diff --git a/src/queries/analytics/events/getEvents.ts b/src/queries/analytics/events/getWebsiteEvents.ts similarity index 97% rename from src/queries/analytics/events/getEvents.ts rename to src/queries/analytics/events/getWebsiteEvents.ts index 540c1a05..b76d1ee5 100644 --- a/src/queries/analytics/events/getEvents.ts +++ b/src/queries/analytics/events/getWebsiteEvents.ts @@ -3,7 +3,7 @@ import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; import prisma from 'lib/prisma'; import { PageParams, QueryFilters } from 'lib/types'; -export function getEvents( +export function getWebsiteEvents( ...args: [websiteId: string, filters: QueryFilters, pageParams?: PageParams] ) { return runQuery({ diff --git a/src/queries/analytics/getRealtimeData.ts b/src/queries/analytics/getRealtimeData.ts index e25cc866..3e691b4e 100644 --- a/src/queries/analytics/getRealtimeData.ts +++ b/src/queries/analytics/getRealtimeData.ts @@ -1,4 +1,9 @@ -import { getWebsiteSessions, getEvents, getPageviewStats, getSessionStats } from 'queries/index'; +import { + getWebsiteSessions, + getWebsiteEvents, + getPageviewStats, + getSessionStats, +} from 'queries/index'; const MAX_SIZE = 50; @@ -19,7 +24,7 @@ export async function getRealtimeData( const { startDate, timezone } = criteria; const filters = { startDate, endDate: new Date(), unit: 'minute', timezone }; const [events, sessions, pageviews, sessionviews] = await Promise.all([ - getEvents(websiteId, { startDate, timezone }, { pageSize: 10000 }), + getWebsiteEvents(websiteId, { startDate, timezone }, { pageSize: 10000 }), getWebsiteSessions(websiteId, { startDate, timezone }, { pageSize: 10000 }), getPageviewStats(websiteId, filters), getSessionStats(websiteId, filters), diff --git a/src/queries/index.ts b/src/queries/index.ts index 82d40ee5..51c1e14b 100644 --- a/src/queries/index.ts +++ b/src/queries/index.ts @@ -8,7 +8,7 @@ export * from './analytics/events/getEventDataFields'; export * from './analytics/events/getEventDataStats'; export * from './analytics/events/getEventDataUsage'; export * from './analytics/events/getEventMetrics'; -export * from './analytics/events/getEvents'; +export * from './analytics/events/getWebsiteEvents'; export * from './analytics/events/getEventUsage'; export * from './analytics/events/saveEvent'; export * from './analytics/reports/getFunnel';