diff --git a/src/app/(main)/reports/journey/JourneyView.tsx b/src/app/(main)/reports/journey/JourneyView.tsx index b013a3e193..08a51ce2df 100644 --- a/src/app/(main)/reports/journey/JourneyView.tsx +++ b/src/app/(main)/reports/journey/JourneyView.tsx @@ -103,8 +103,6 @@ export default function JourneyView() { return null; } - //console.log({ data, columns, selectedNode, activeNode }); - return (
diff --git a/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx b/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx index ea037852e2..617015f1d6 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx @@ -18,6 +18,8 @@ import styles from './WebsiteExpandedView.module.css'; const views = { url: PagesTable, + entry: PagesTable, + exit: PagesTable, title: PagesTable, referrer: ReferrersTable, browser: BrowsersTable, diff --git a/src/components/messages.ts b/src/components/messages.ts index a4c43af22b..dd5eb9ddcc 100644 --- a/src/components/messages.ts +++ b/src/components/messages.ts @@ -88,6 +88,8 @@ export const labels = defineMessages({ leaveTeam: { id: 'label.leave-team', defaultMessage: 'Leave team' }, refresh: { id: 'label.refresh', defaultMessage: 'Refresh' }, pages: { id: 'label.pages', defaultMessage: 'Pages' }, + entry: { id: 'label.entry', defaultMessage: 'Entry URL' }, + exit: { id: 'label.exit', defaultMessage: 'Exit URL' }, referrers: { id: 'label.referrers', defaultMessage: 'Referrers' }, screens: { id: 'label.screens', defaultMessage: 'Screens' }, browsers: { id: 'label.browsers', defaultMessage: 'Browsers' }, diff --git a/src/components/metrics/PagesTable.tsx b/src/components/metrics/PagesTable.tsx index d29952d4f3..767c4764b4 100644 --- a/src/components/metrics/PagesTable.tsx +++ b/src/components/metrics/PagesTable.tsx @@ -1,11 +1,10 @@ -import FilterLink from 'components/common/FilterLink'; +import { WebsiteContext } from 'app/(main)/websites/[websiteId]/WebsiteProvider'; import FilterButtons from 'components/common/FilterButtons'; -import MetricsTable, { MetricsTableProps } from './MetricsTable'; -import { useMessages } from 'components/hooks'; -import { useNavigation } from 'components/hooks'; +import FilterLink from 'components/common/FilterLink'; +import { useMessages, useNavigation } from 'components/hooks'; import { emptyFilter } from 'lib/filters'; import { useContext } from 'react'; -import { WebsiteContext } from 'app/(main)/websites/[websiteId]/WebsiteProvider'; +import MetricsTable, { MetricsTableProps } from './MetricsTable'; export interface PagesTableProps extends MetricsTableProps { allowFilter?: boolean; @@ -21,7 +20,7 @@ export function PagesTable({ allowFilter, ...props }: PagesTableProps) { const { domain } = useContext(WebsiteContext); const handleSelect = (key: any) => { - router.push(renderUrl({ view: key }), { scroll: true }); + router.push(renderUrl({ view: key }), { scroll: false }); }; const buttons = [ @@ -29,6 +28,14 @@ export function PagesTable({ allowFilter, ...props }: PagesTableProps) { label: 'URL', key: 'url', }, + { + label: formatMessage(labels.entry), + key: 'entry', + }, + { + label: formatMessage(labels.exit), + key: 'exit', + }, { label: formatMessage(labels.title), key: 'title', @@ -38,11 +45,13 @@ export function PagesTable({ allowFilter, ...props }: PagesTableProps) { const renderLink = ({ x }) => { return ( ); diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 745673e96c..f8447b8b7e 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -32,7 +32,7 @@ export const FILTER_RANGE = 'filter-range'; export const FILTER_REFERRERS = 'filter-referrers'; export const FILTER_PAGES = 'filter-pages'; export const UNIT_TYPES = ['year', 'month', 'hour', 'day', 'minute']; -export const EVENT_COLUMNS = ['url', 'referrer', 'title', 'query', 'event']; +export const EVENT_COLUMNS = ['url', 'entry', 'exit', 'referrer', 'title', 'query', 'event']; export const SESSION_COLUMNS = [ 'browser', @@ -47,6 +47,8 @@ export const SESSION_COLUMNS = [ export const FILTER_COLUMNS = { url: 'url_path', + entry: 'url_path', + exit: 'url_path', referrer: 'referrer_domain', title: 'page_title', query: 'url_query', diff --git a/src/queries/analytics/pageviews/getPageviewMetrics.ts b/src/queries/analytics/pageviews/getPageviewMetrics.ts index 5f609c126e..67ccb04aab 100644 --- a/src/queries/analytics/pageviews/getPageviewMetrics.ts +++ b/src/queries/analytics/pageviews/getPageviewMetrics.ts @@ -1,7 +1,7 @@ -import prisma from 'lib/prisma'; import clickhouse from 'lib/clickhouse'; -import { runQuery, CLICKHOUSE, PRISMA } from 'lib/db'; import { EVENT_TYPE, FILTER_COLUMNS, SESSION_COLUMNS } from 'lib/constants'; +import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; +import prisma from 'lib/prisma'; import { QueryFilters } from 'lib/types'; export async function getPageviewMetrics( @@ -31,17 +31,34 @@ async function relationalQuery( { joinSession: SESSION_COLUMNS.includes(type) }, ); + let entryExitQuery = ''; let excludeDomain = ''; if (column === 'referrer_domain') { excludeDomain = `and website_event.referrer_domain != {{websiteDomain}} and website_event.referrer_domain is not null`; } + if (type === 'entry' || type === 'exit') { + const aggregrate = type === 'entry' ? 'min' : 'max'; + + entryExitQuery = ` + JOIN (select visit_id, + ${aggregrate}(created_at) target_created_at + from website_event + where website_event.website_id = {{websiteId::uuid}} + and website_event.created_at between {{startDate}} and {{endDate}} + and event_type = {{eventType}} + group by visit_id) x + ON x.visit_id = website_event.visit_id + and x.target_created_at = website_event.created_at`; + } + return rawQuery( ` select ${column} x, count(*) y from website_event ${joinSession} + ${entryExitQuery} where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} and event_type = {{eventType}} @@ -70,15 +87,32 @@ async function clickhouseQuery( eventType: column === 'event_name' ? EVENT_TYPE.customEvent : EVENT_TYPE.pageView, }); + let entryExitQuery = ''; let excludeDomain = ''; if (column === 'referrer_domain') { excludeDomain = `and referrer_domain != {websiteDomain:String} and referrer_domain != ''`; } + if (type === 'entry' || type === 'exit') { + const aggregrate = type === 'entry' ? 'min' : 'max'; + + entryExitQuery = ` + JOIN (select visit_id, + ${aggregrate}(created_at) target_created_at + from website_event + where website_id = {websiteId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} + and event_type = {eventType:UInt32} + group by visit_id) x + ON x.visit_id = website_event.visit_id + and x.target_created_at = website_event.created_at`; + } + return rawQuery( ` select ${column} x, count(*) y from website_event + ${entryExitQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type = {eventType:UInt32}