diff --git a/src/app/(main)/websites/[websiteId]/WebsiteDetails.tsx b/src/app/(main)/websites/[websiteId]/WebsiteDetails.tsx index d917c6d71..6ccde2812 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteDetails.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteDetails.tsx @@ -10,7 +10,13 @@ import WebsiteHeader from './WebsiteHeader'; import WebsiteMetricsBar from './WebsiteMetricsBar'; import WebsiteTableView from './WebsiteTableView'; -export default function WebsiteDetails({ websiteId }: { websiteId: string }) { +export default function WebsiteDetails({ + websiteId, + customDataFields, +}: { + websiteId: string; + customDataFields: string[]; +}) { const { data: website, isLoading, error } = useWebsite(websiteId); const pathname = usePathname(); const showLinks = !pathname.includes('/share/'); @@ -32,7 +38,13 @@ export default function WebsiteDetails({ websiteId }: { websiteId: string }) { {!website && } {website && ( <> - {!view && } + {!view && ( + + )} {view && } )} diff --git a/src/app/(main)/websites/[websiteId]/WebsiteTableView.tsx b/src/app/(main)/websites/[websiteId]/WebsiteTableView.tsx index 7cc415e5c..9cabcf7de 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteTableView.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteTableView.tsx @@ -9,13 +9,17 @@ import WorldMap from 'components/metrics/WorldMap'; import CountriesTable from 'components/metrics/CountriesTable'; import EventsTable from 'components/metrics/EventsTable'; import EventsChart from 'components/metrics/EventsChart'; +import PageviewCustomDataTable from 'components/metrics/PageviewCustomDataTable'; +import { chunkArray } from 'next-basics'; export default function WebsiteTableView({ websiteId, domainName, + customDataFields, }: { websiteId: string; domainName: string; + customDataFields: string[]; }) { const [countryData, setCountryData] = useState(); const tableProps = { @@ -35,6 +39,16 @@ export default function WebsiteTableView({ + {chunkArray(customDataFields, 3).map((fields, index) => ( + + {fields.map(field => ( + + ))} + + ))} diff --git a/src/components/metrics/PageviewCustomDataTable.tsx b/src/components/metrics/PageviewCustomDataTable.tsx new file mode 100644 index 000000000..c7cffa76a --- /dev/null +++ b/src/components/metrics/PageviewCustomDataTable.tsx @@ -0,0 +1,29 @@ +import useMessages from 'components/hooks/useMessages'; +import MetricsTable, { MetricsTableProps } from 'components/metrics/MetricsTable'; + +export function PageviewCustomDataTable(props: MetricsTableProps) { + const { formatMessage, labels } = useMessages(); + + function renderLink({ x: field }) { + return <>{field}; + } + + function titleize(fieldName: string) { + return fieldName + .split(/[_-]/) + .map((word, i) => (i === 0 ? word[0].toUpperCase() + word.slice(1) : word)) + .join(' '); + } + + return ( + + ); +} + +export default PageviewCustomDataTable; diff --git a/src/pages/api/websites/[websiteId]/metrics.ts b/src/pages/api/websites/[websiteId]/metrics.ts index eb6361707..abc63cd5f 100644 --- a/src/pages/api/websites/[websiteId]/metrics.ts +++ b/src/pages/api/websites/[websiteId]/metrics.ts @@ -28,6 +28,7 @@ export interface WebsiteMetricsRequestQuery { limit?: number; offset?: number; search?: string; + fieldName?: string; } const schema = { @@ -51,6 +52,7 @@ const schema = { limit: yup.number(), offset: yup.number(), search: yup.string(), + fieldName: yup.string(), }), }; @@ -80,6 +82,7 @@ export default async ( limit, offset, search, + fieldName, } = req.query; if (req.method === 'GET') { @@ -146,6 +149,10 @@ export default async ( offset, ); + return ok(res, data); + } else if (type === 'custom') { + const data = await getPageviewMetrics(websiteId, column, filters, limit, fieldName); + return ok(res, data); } diff --git a/src/queries/analytics/eventData/getEventDataEvents.ts b/src/queries/analytics/eventData/getEventDataEvents.ts index 3eeb3702d..71e4ffa13 100644 --- a/src/queries/analytics/eventData/getEventDataEvents.ts +++ b/src/queries/analytics/eventData/getEventDataEvents.ts @@ -51,6 +51,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { on website_event.event_id = event_data.website_event_id where event_data.website_id = {{websiteId::uuid}} and event_data.created_at between {{startDate}} and {{endDate}} + and website_event.event_name is not null group by website_event.event_name, event_data.event_key, event_data.data_type order by 1 asc, 2 asc limit 500 diff --git a/src/queries/analytics/eventData/getEventDataStats.ts b/src/queries/analytics/eventData/getEventDataStats.ts index 39afa1ae7..8750b1cd6 100644 --- a/src/queries/analytics/eventData/getEventDataStats.ts +++ b/src/queries/analytics/eventData/getEventDataStats.ts @@ -32,8 +32,10 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { event_key, count(*) as "total" from event_data - where website_id = {{websiteId::uuid}} - and created_at between {{startDate}} and {{endDate}} + join website_event on website_event.event_id = event_data.website_event_id + where website_event.website_id = {{websiteId::uuid}} + and event_data.created_at between {{startDate}} and {{endDate}} + and website_event.event_name is not null ${filterQuery} group by website_event_id, event_key ) as t diff --git a/src/queries/analytics/pageviews/getPageviewMetrics.ts b/src/queries/analytics/pageviews/getPageviewMetrics.ts index 8673dbe60..6112c69df 100644 --- a/src/queries/analytics/pageviews/getPageviewMetrics.ts +++ b/src/queries/analytics/pageviews/getPageviewMetrics.ts @@ -11,6 +11,7 @@ export async function getPageviewMetrics( filters: QueryFilters, limit?: number, offset?: number, + fieldName?: string, ] ) { return runQuery({ @@ -25,6 +26,7 @@ async function relationalQuery( filters: QueryFilters, limit: number = 500, offset: number = 0, + fieldName?: string, ) { const { rawQuery, parseFilters } = prisma; const { filterQuery, joinSession, params } = await parseFilters( @@ -37,19 +39,26 @@ async function relationalQuery( ); let excludeDomain = ''; + let joinEventData = ''; + let filterEventDataOnFieldName = ''; if (column === 'referrer_domain') { excludeDomain = 'and (website_event.referrer_domain != {{websiteDomain}} or website_event.referrer_domain is null)'; + } else if (column === 'custom') { + joinEventData = 'join event_data on event_data.website_event_id = website_event.event_id'; + filterEventDataOnFieldName = 'and event_data.event_key = {{fieldName}}'; } return rawQuery( ` - select ${column} x, count(*) y + select ${column === 'custom' ? `event_data.string_value` : column} x, count(*) y from website_event + ${joinEventData} ${joinSession} where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} and event_type = {{eventType}} + ${filterEventDataOnFieldName} ${excludeDomain} ${filterQuery} group by 1 @@ -57,7 +66,7 @@ async function relationalQuery( limit ${limit} offset ${offset} `, - params, + { ...params, fieldName }, ); } @@ -67,6 +76,8 @@ async function clickhouseQuery( filters: QueryFilters, limit: number = 500, offset: number = 0, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + fieldName?: string, ): Promise<{ x: string; y: number }[]> { const { rawQuery, parseFilters } = clickhouse; const { filterQuery, params } = await parseFilters(websiteId, {