diff --git a/package.json b/package.json index bb0c77dda..fd263afcf 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "postbuild": "node scripts/postbuild.js", "test": "vitest run", "test:watch": "vitest", + "install-heatmap": "playwright install chromium", "test:e2e": "playwright test", "test:e2e:ui": "playwright test --ui", "seed-data": "tsx scripts/seed-data.ts", diff --git a/src/app/(main)/websites/[websiteId]/(reports)/heatmaps/Heatmap.tsx b/src/app/(main)/websites/[websiteId]/(reports)/heatmaps/Heatmap.tsx index 8294e01df..d43d951d5 100644 --- a/src/app/(main)/websites/[websiteId]/(reports)/heatmaps/Heatmap.tsx +++ b/src/app/(main)/websites/[websiteId]/(reports)/heatmaps/Heatmap.tsx @@ -408,18 +408,18 @@ function ScrollHeatmapView({ for (let depth = 0; depth < 100; depth += SCROLL_BUCKET_SIZE) { const reached = Math.max(0, totalSessions - dropped); - const ratio = totalSessions ? reached / totalSessions : 0; + dropped += sessionsByDepth.get(depth) ?? 0; + const nextReached = Math.max(0, totalSessions - dropped); + const ratio = totalSessions ? nextReached / totalSessions : 0; if (reached > 0) { bands.push({ fromPct: depth, toPct: Math.min(100, depth + SCROLL_BUCKET_SIZE), - reached, + reached: nextReached, ratio, }); } - - dropped += sessionsByDepth.get(depth) ?? 0; } return ( @@ -481,10 +481,10 @@ function ScrollHeatmapView({ height: `${Math.max(0, band.toPct - band.fromPct)}%`, background: `hsla(${hue}, 90%, 55%, ${0.12 + intensity * 0.45})`, }} - title={`${band.fromPct}% depth • ${formatLongNumber(band.reached)} sessions reached`} + title={`${band.toPct}% depth • ${formatLongNumber(band.reached)} sessions reached`} > - {band.fromPct}% depth • {Math.round(intensity * 100)}% reached + {band.toPct}% depth • {Math.round(intensity * 100)}% reached ); diff --git a/src/queries/sql/heatmap/getHeatmap.ts b/src/queries/sql/heatmap/getHeatmap.ts index 0a6c4a932..198c62443 100644 --- a/src/queries/sql/heatmap/getHeatmap.ts +++ b/src/queries/sql/heatmap/getHeatmap.ts @@ -4,9 +4,9 @@ import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db'; import prisma from '@/lib/prisma'; import type { QueryFilters } from '@/lib/types'; import { + type HeatmapSnapshotImage, ensureHeatmapSnapshot, shouldSkipSnapshot, - type HeatmapSnapshotImage, } from './ensureHeatmapSnapshot'; const FUNCTION_NAME = 'getHeatmap'; @@ -86,7 +86,7 @@ async function relationalQuery( const { startDate, endDate, urlPath, mode = 'click' } = parameters; const eventType = mode === 'scroll' ? HEATMAP_EVENT_TYPE.scroll : HEATMAP_EVENT_TYPE.click; const filterContext = getRelationalHeatmapFilterContext(websiteId, parameters); - const clickPageFilter = + const pageFilter = mode === 'click' ? ` and x is not null @@ -98,7 +98,12 @@ async function relationalQuery( and viewport_w is not null and viewport_h is not null ` - : ''; + : ` + and scroll_pct is not null + and page_w is not null + and page_h is not null + and viewport_w is not null + `; const rawPages: HeatmapPage[] = await rawQuery( ` @@ -111,7 +116,7 @@ async function relationalQuery( where h.website_id = {{websiteId::uuid}} and h.event_type = {{eventType}} and h.created_at between {{startDate}} and {{endDate}} - ${clickPageFilter} + ${pageFilter} group by h.url_path order by sessions desc, count desc limit ${PAGE_LIMIT} @@ -267,7 +272,7 @@ async function clickhouseQuery( const { startDate, endDate, urlPath, mode = 'click' } = parameters; const eventType = mode === 'scroll' ? HEATMAP_EVENT_TYPE.scroll : HEATMAP_EVENT_TYPE.click; const filterContext = getClickhouseHeatmapFilterContext(websiteId, parameters); - const clickPageFilter = + const pageFilter = mode === 'click' ? ` and x is not null @@ -279,7 +284,12 @@ async function clickhouseQuery( and viewport_w is not null and viewport_h is not null ` - : ''; + : ` + and scroll_pct is not null + and page_w is not null + and page_h is not null + and viewport_w is not null + `; const pageRows = await rawQuery< { urlPath: string; count: string | number; sessions: string | number }[] @@ -294,7 +304,7 @@ async function clickhouseQuery( where h.website_id = {websiteId:UUID} and h.event_type = {eventType:UInt8} and h.created_at between {startDate:DateTime64} and {endDate:DateTime64} - ${clickPageFilter} + ${pageFilter} group by h.url_path order by sessions desc, count desc limit ${PAGE_LIMIT}