diff --git a/src/app/(main)/boards/boardComponentRegistry.tsx b/src/app/(main)/boards/boardComponentRegistry.tsx index 2af1c7006..2c717bee0 100644 --- a/src/app/(main)/boards/boardComponentRegistry.tsx +++ b/src/app/(main)/boards/boardComponentRegistry.tsx @@ -49,6 +49,7 @@ export const CATEGORIES = [ const METRIC_TYPES = [ { label: 'Path', value: 'path' }, + { label: 'URL', value: 'fullPath' }, { label: 'Entry page', value: 'entry' }, { label: 'Exit page', value: 'exit' }, { label: 'Title', value: 'title' }, diff --git a/src/app/(main)/links/[linkId]/LinkPage.tsx b/src/app/(main)/links/[linkId]/LinkPage.tsx index 5d10fa82b..910d1f51a 100644 --- a/src/app/(main)/links/[linkId]/LinkPage.tsx +++ b/src/app/(main)/links/[linkId]/LinkPage.tsx @@ -10,7 +10,7 @@ import { WebsiteChart } from '@/app/(main)/websites/[websiteId]/WebsiteChart'; import { PageBody } from '@/components/common/PageBody'; import { Panel } from '@/components/common/Panel'; -const excludedIds = ['path', 'entry', 'exit', 'title', 'language', 'screen', 'event']; +const excludedIds = ['path', 'fullPath', 'entry', 'exit', 'title', 'language', 'screen', 'event']; export function LinkPage({ linkId, diff --git a/src/app/(main)/pixels/[pixelId]/PixelPage.tsx b/src/app/(main)/pixels/[pixelId]/PixelPage.tsx index e8d605d24..09d6ce4e2 100644 --- a/src/app/(main)/pixels/[pixelId]/PixelPage.tsx +++ b/src/app/(main)/pixels/[pixelId]/PixelPage.tsx @@ -10,7 +10,7 @@ import { WebsiteChart } from '@/app/(main)/websites/[websiteId]/WebsiteChart'; import { PageBody } from '@/components/common/PageBody'; import { Panel } from '@/components/common/Panel'; -const excludedIds = ['path', 'entry', 'exit', 'title', 'language', 'screen', 'event']; +const excludedIds = ['path', 'fullPath', 'entry', 'exit', 'title', 'language', 'screen', 'event']; export function PixelPage({ pixelId, diff --git a/src/app/(main)/websites/[websiteId]/WebsiteExpandedMenu.tsx b/src/app/(main)/websites/[websiteId]/WebsiteExpandedMenu.tsx index c49eaa02f..e3c333693 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteExpandedMenu.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteExpandedMenu.tsx @@ -25,6 +25,7 @@ import { Tag, Target, Type, + Waypoints, } from '@/components/icons'; import { Lightning } from '@/components/svg'; @@ -45,7 +46,7 @@ export function WebsiteExpandedMenu({ const items = [ { - label: 'URL', + label: t(labels.pages), items: [ { id: 'path', @@ -53,6 +54,12 @@ export function WebsiteExpandedMenu({ path: updateParams({ view: 'path' }), icon: , }, + { + id: 'fullPath', + label: t(labels.url), + path: updateParams({ view: 'fullPath' }), + icon: , + }, { id: 'entry', label: t(labels.entry), diff --git a/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx b/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx index 71fbdc703..ebe8d976c 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx @@ -17,6 +17,7 @@ export function WebsiteExpandedView({ const { query: { view }, } = useNavigation(); + const title = view === 'fullPath' ? t(labels.url) : t(labels[view]); return ( @@ -44,12 +45,7 @@ export function WebsiteExpandedView({ - + diff --git a/src/app/(main)/websites/[websiteId]/WebsitePanels.tsx b/src/app/(main)/websites/[websiteId]/WebsitePanels.tsx index 7d5d08c8c..f19a7b400 100644 --- a/src/app/(main)/websites/[websiteId]/WebsitePanels.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsitePanels.tsx @@ -26,12 +26,16 @@ export function WebsitePanels({ websiteId }: { websiteId: string }) { {t(labels.path)} + {t(labels.url)} {t(labels.entry)} {t(labels.exit)} + + + diff --git a/src/components/metrics/MetricLabel.tsx b/src/components/metrics/MetricLabel.tsx index dae5dbabf..21618b90d 100644 --- a/src/components/metrics/MetricLabel.tsx +++ b/src/components/metrics/MetricLabel.tsx @@ -94,6 +94,9 @@ export function MetricLabel({ type, data }: MetricLabelProps) { /> ); + case 'fullPath': + return label || `(${t(labels.none)})`; + case 'device': return ( { const { type, limit = 500, offset = 0 } = parameters; - let column = FILTER_COLUMNS[type] || type; + let column = getPageviewColumn(type); const { rawQuery, parseFilters, getTimestampDiffSQL } = prisma; const { filterQuery, joinSessionQuery, cohortQuery, excludeBounceQuery, queryParams } = parseFilters( @@ -49,8 +49,6 @@ async function relationalQuery( let entryExitQuery = ''; let excludeDomain = ''; - const isPathType = type === 'path' || type === 'entry' || type === 'exit'; - if (column === 'referrer_domain') { excludeDomain = `and website_event.referrer_domain != regexp_replace(website_event.hostname, '^www.', '') and website_event.referrer_domain != ''`; @@ -77,11 +75,11 @@ async function relationalQuery( `; } - const selectColumn = isPathType + const selectColumn = type === 'fullPath' ? `case when website_event.url_query != '' then website_event.url_path || '?' || website_event.url_query else website_event.url_path end` : column; - const groupByColumn = isPathType + const groupByColumn = type === 'fullPath' ? `case when website_event.url_query != '' then website_event.url_path || '?' || website_event.url_query else website_event.url_path end` : column; @@ -131,7 +129,7 @@ async function clickhouseQuery( filters: QueryFilters, ): Promise<{ x: string; y: number }[]> { const { type, limit = 500, offset = 0 } = parameters; - let column = FILTER_COLUMNS[type] || type; + let column = getPageviewColumn(type); const { rawQuery, parseFilters } = clickhouse; const { filterQuery, cohortQuery, excludeBounceQuery, queryParams } = parseFilters({ ...filters, @@ -140,7 +138,6 @@ async function clickhouseQuery( let excludeDomain = ''; let entryExitQuery = ''; - const isPathType = type === 'path' || type === 'entry' || type === 'exit'; let selectColumn = column; if (column === 'referrer_domain') { @@ -165,8 +162,8 @@ async function clickhouseQuery( group by visit_id) x ON x.visit_id = website_event.visit_id`; - selectColumn = `if(x.url_query != '', concat(x.url_path, '?', x.url_query), x.url_path)`; - } else if (isPathType) { + selectColumn = `x.url_path`; + } else if (type === 'fullPath') { selectColumn = `if(url_query != '', concat(url_path, '?', url_query), url_path)`; } @@ -244,3 +241,11 @@ export function toPostgresGroupedReferrer( function toPostgresLikeClause(column: string, arr: string[]) { return arr.map(val => `${column} ilike '%${val.replace(/'/g, "''")}%'`).join(' OR\n '); } + +function getPageviewColumn(type: string) { + if (type === 'fullPath') { + return 'url_path'; + } + + return FILTER_COLUMNS[type] || type; +} diff --git a/src/queries/sql/pageviews/getPageviewMetrics.ts b/src/queries/sql/pageviews/getPageviewMetrics.ts index 9d6120bed..cfc8d0c7f 100644 --- a/src/queries/sql/pageviews/getPageviewMetrics.ts +++ b/src/queries/sql/pageviews/getPageviewMetrics.ts @@ -32,7 +32,7 @@ async function relationalQuery( filters: QueryFilters, ): Promise { const { type, limit = 500, offset = 0 } = parameters; - let column = FILTER_COLUMNS[type] || type; + let column = getPageviewColumn(type); const { rawQuery, parseFilters } = prisma; const { filterQuery, joinSessionQuery, cohortQuery, excludeBounceQuery, queryParams } = parseFilters( @@ -45,8 +45,6 @@ async function relationalQuery( let entryExitQuery = ''; let excludeDomain = ''; - const isPathType = type === 'path' || type === 'entry' || type === 'exit'; - if (column === 'referrer_domain') { excludeDomain = `and website_event.referrer_domain != regexp_replace(website_event.hostname, '^www.', '') and website_event.referrer_domain != ''`; @@ -70,12 +68,10 @@ async function relationalQuery( on x.visit_id = website_event.visit_id `; - column = isPathType - ? `case when x.url_query != '' then x.url_path || '?' || x.url_query else x.url_path end` - : `x.${FILTER_COLUMNS[type] || type}`; + column = `x.${FILTER_COLUMNS[type] || type}`; } - const selectColumn = isPathType && type !== 'entry' && type !== 'exit' + const selectColumn = type === 'fullPath' ? `case when website_event.url_query != '' then website_event.url_path || '?' || website_event.url_query else website_event.url_path end` : column; @@ -110,7 +106,7 @@ async function clickhouseQuery( filters: QueryFilters, ): Promise<{ x: string; y: number }[]> { const { type, limit = 500, offset = 0 } = parameters; - let column = FILTER_COLUMNS[type] || type; + let column = getPageviewColumn(type); const { rawQuery, parseFilters } = clickhouse; const { filterQuery, cohortQuery, excludeBounceQuery, queryParams } = parseFilters({ ...filters, @@ -119,9 +115,7 @@ async function clickhouseQuery( let sql = ''; let excludeDomain = ''; - const isPathType = type === 'path' || type === 'entry' || type === 'exit'; - - if (EVENT_COLUMNS.some(item => Object.keys(filters).includes(item))) { + if (type === 'fullPath' || EVENT_COLUMNS.some(item => Object.keys(filters).includes(item))) { let entryExitQuery = ''; let selectColumn = column; @@ -143,8 +137,9 @@ async function clickhouseQuery( group by visit_id) x ON x.visit_id = website_event.visit_id`; - selectColumn = `if(x.url_query != '', concat(x.url_path, '?', x.url_query), x.url_path)`; - } else if (isPathType) { + column = `x.url_path`; + selectColumn = `x.url_path`; + } else if (type === 'fullPath') { selectColumn = `if(url_query != '', concat(url_path, '?', url_query), url_path)`; } @@ -210,3 +205,11 @@ async function clickhouseQuery( return rawQuery(sql, { ...queryParams, ...parameters }, FUNCTION_NAME); } + +function getPageviewColumn(type: string) { + if (type === 'fullPath') { + return 'url_path'; + } + + return FILTER_COLUMNS[type] || type; +}