Skip to content

Commit cd20b3a

Browse files
committed
Heatmaps bug fixes, update schema to match overlay, use react-zen component to match ui/ux, polish
1 parent a22274b commit cd20b3a

18 files changed

Lines changed: 981 additions & 347 deletions

File tree

db/clickhouse/migrations/12_add_heatmap.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ CREATE TABLE umami.heatmap_event
1414
viewport_h Nullable(Int32),
1515
page_h Nullable(Int32),
1616
scroll_pct Nullable(UInt8),
17+
replay_chunk_index Nullable(UInt32),
18+
replay_event_index Nullable(UInt32),
19+
replay_time_ms Nullable(Int64),
1720
created_at DateTime('UTC')
1821
)
1922
ENGINE = MergeTree

db/clickhouse/schema.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,9 @@ CREATE TABLE umami.heatmap_event
416416
viewport_h Nullable(Int32),
417417
page_h Nullable(Int32),
418418
scroll_pct Nullable(UInt8),
419+
replay_chunk_index Nullable(UInt32),
420+
replay_event_index Nullable(UInt32),
421+
replay_time_ms Nullable(Int64),
419422
created_at DateTime('UTC')
420423
)
421424
ENGINE = MergeTree

prisma/migrations/20_add_heatmap/migration.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ CREATE TABLE "heatmap_event" (
1313
"viewport_h" INTEGER,
1414
"page_h" INTEGER,
1515
"scroll_pct" INTEGER,
16+
"replay_chunk_index" INTEGER,
17+
"replay_event_index" INTEGER,
18+
"replay_time_ms" BIGINT,
1619
"created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
1720

1821
CONSTRAINT "heatmap_event_pkey" PRIMARY KEY ("heatmap_event_id")
@@ -23,3 +26,4 @@ CREATE INDEX "heatmap_event_website_id_idx" ON "heatmap_event"("website_id");
2326
CREATE INDEX "heatmap_event_visit_id_idx" ON "heatmap_event"("visit_id");
2427
CREATE INDEX "heatmap_event_website_id_created_at_idx" ON "heatmap_event"("website_id", "created_at");
2528
CREATE INDEX "heatmap_event_website_id_url_path_event_type_created_at_idx" ON "heatmap_event"("website_id", "url_path", "event_type", "created_at");
29+
CREATE INDEX "heatmap_event_website_id_visit_id_replay_chunk_index_replay_event_index_idx" ON "heatmap_event"("website_id", "visit_id", "replay_chunk_index", "replay_event_index");

prisma/schema.prisma

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -402,26 +402,30 @@ model SessionReplaySaved {
402402
}
403403

404404
model HeatmapEvent {
405-
id String @id() @map("heatmap_event_id") @db.Uuid
406-
websiteId String @map("website_id") @db.Uuid
407-
sessionId String @map("session_id") @db.Uuid
408-
visitId String @map("visit_id") @db.Uuid
409-
urlPath String @map("url_path") @db.VarChar(500)
410-
eventType Int @map("event_type") @db.Integer
411-
nodeId Int? @map("node_id") @db.Integer
412-
x Int? @db.Integer
413-
y Int? @db.Integer
414-
viewportW Int? @map("viewport_w") @db.Integer
415-
viewportH Int? @map("viewport_h") @db.Integer
416-
pageH Int? @map("page_h") @db.Integer
417-
scrollPct Int? @map("scroll_pct") @db.Integer
418-
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
405+
id String @id() @map("heatmap_event_id") @db.Uuid
406+
websiteId String @map("website_id") @db.Uuid
407+
sessionId String @map("session_id") @db.Uuid
408+
visitId String @map("visit_id") @db.Uuid
409+
urlPath String @map("url_path") @db.VarChar(500)
410+
eventType Int @map("event_type") @db.Integer
411+
nodeId Int? @map("node_id") @db.Integer
412+
x Int? @db.Integer
413+
y Int? @db.Integer
414+
viewportW Int? @map("viewport_w") @db.Integer
415+
viewportH Int? @map("viewport_h") @db.Integer
416+
pageH Int? @map("page_h") @db.Integer
417+
scrollPct Int? @map("scroll_pct") @db.Integer
418+
replayChunkIndex Int? @map("replay_chunk_index") @db.Integer
419+
replayEventIndex Int? @map("replay_event_index") @db.Integer
420+
replayTimeMs BigInt? @map("replay_time_ms") @db.BigInt
421+
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
419422
420423
website Website @relation(fields: [websiteId], references: [id])
421424
422425
@@index([websiteId])
423426
@@index([visitId])
424427
@@index([websiteId, createdAt])
425428
@@index([websiteId, urlPath, eventType, createdAt])
429+
@@index([websiteId, visitId, replayChunkIndex, replayEventIndex])
426430
@@map("heatmap_event")
427-
}
431+
}

src/app/(main)/websites/[websiteId]/(reports)/heatmaps/Heatmap.module.css

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,33 @@
1+
.layoutGrid {
2+
height: 100%;
3+
min-height: 0;
4+
overflow: hidden;
5+
}
6+
17
.pageList {
8+
min-width: 0;
9+
padding-right: 6px;
10+
}
11+
12+
.railDivider {
13+
width: 1px;
14+
height: 100%;
15+
background: var(--gray-300, #d1d5db);
16+
justify-self: center;
17+
}
18+
19+
.contentColumn {
20+
min-width: 0;
21+
min-height: 0;
22+
padding-left: 20px;
23+
}
24+
25+
.pageListItems {
226
overflow-y: auto;
27+
padding-top: 8px;
328
padding-right: 8px;
4-
max-height: 100%;
29+
max-height: 850px;
30+
scrollbar-gutter: stable;
531
}
632

733
.pageButton {
@@ -10,7 +36,7 @@
1036
border: 0;
1137
background: transparent;
1238
border-radius: 6px;
13-
padding: 8px 10px;
39+
padding: 8px 10px 8px 6px;
1440
cursor: pointer;
1541
font: inherit;
1642
color: inherit;
@@ -26,25 +52,30 @@
2652
color: var(--surface-base);
2753
}
2854

29-
.toggleButton {
30-
border: 1px solid var(--border-base);
31-
background: var(--surface-base);
32-
border-radius: 6px;
33-
padding: 4px 10px;
34-
cursor: pointer;
35-
font: inherit;
36-
color: inherit;
55+
.summaryHeader {
56+
min-width: 0;
3757
}
3858

39-
.toggleButton:hover {
40-
background: var(--interactive-bg-hover);
59+
.summaryPath {
60+
display: block;
61+
min-width: 0;
62+
overflow: hidden;
63+
text-overflow: ellipsis;
64+
white-space: nowrap;
4165
}
4266

43-
.toggleButtonSelected,
44-
.toggleButtonSelected:hover {
45-
background: var(--surface-inverted);
46-
color: var(--surface-base);
47-
border-color: var(--surface-inverted);
67+
.summaryStats {
68+
flex-wrap: wrap;
69+
}
70+
71+
.summaryStat {
72+
display: inline-flex;
73+
align-items: center;
74+
padding: 4px 10px;
75+
border-radius: 999px;
76+
background: var(--surface-raised);
77+
border: 1px solid var(--border-base);
78+
white-space: nowrap;
4879
}
4980

5081
.scrollBand {
@@ -73,15 +104,27 @@
73104
min-width: 0;
74105
}
75106

107+
.snapshotControlRow {
108+
margin-top: 8px;
109+
}
110+
76111
.canvas {
77112
position: relative;
78-
overflow: hidden;
113+
overflow: visible;
79114
border: 1px solid var(--border-base);
80115
border-radius: 8px;
81116
background: var(--surface-sunken);
82117
max-width: 100%;
83118
}
84119

120+
.canvasClip {
121+
position: absolute;
122+
inset: 0;
123+
overflow: hidden;
124+
border-radius: inherit;
125+
background: inherit;
126+
}
127+
85128
.snapshot {
86129
position: absolute;
87130
top: 0;
@@ -117,11 +160,18 @@
117160
pointer-events: none;
118161
}
119162

163+
.heatOverlay {
164+
overflow: visible;
165+
}
166+
120167
.canvasLoading {
121168
position: absolute;
122169
inset: 0;
123170
z-index: 2;
124171
background: var(--surface-sunken);
172+
display: flex;
173+
align-items: center;
174+
justify-content: center;
125175
}
126176

127177
.dot {

0 commit comments

Comments
 (0)