Heatmaps bug fixes, update schema to match overlay, use react-zen component to match ui/ux, polish
This commit is contained in:
1 parent
a22274bbbc
commit
cd20b3aafd
18 files changed
+1262
-628
No files matched your search
@@ -116,7 +116,7 @@ export async function POST(request: Request) {
|
||||
});
|
||||
|
||||
try {
|
||||
const heatmapRows = extractHeatmapEvents(events).map(e => ({
|
||||
const heatmapRows = extractHeatmapEvents(events, { chunkIndex }).map(e => ({
|
||||
websiteId,
|
||||
sessionId,
|
||||
visitId,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { parseRequest, setWebsiteDate } from '@/lib/request';
|
||||
import { getQueryFilters, parseRequest } from '@/lib/request';
|
||||
import { json, unauthorized } from '@/lib/response';
|
||||
import { reportResultSchema } from '@/lib/schema';
|
||||
import { canViewWebsite } from '@/permissions';
|
||||
@@ -17,7 +17,22 @@ export async function POST(request: Request) {
|
||||
return unauthorized();
|
||||
}
|
||||
|
||||
const parameters = (await setWebsiteDate(websiteId, body.parameters)) as HeatmapParameters;
|
||||
const filters = await getQueryFilters(
|
||||
{
|
||||
...body.filters,
|
||||
startAt: body.parameters.startDate.getTime(),
|
||||
endAt: body.parameters.endDate.getTime(),
|
||||
timezone: body.parameters.timezone,
|
||||
unit: body.parameters.unit,
|
||||
},
|
||||
websiteId,
|
||||
);
|
||||
|
||||
const parameters = {
|
||||
...filters,
|
||||
urlPath: body.parameters.urlPath,
|
||||
mode: body.parameters.mode,
|
||||
} as HeatmapParameters;
|
||||
|
||||
const data = await getHeatmap(websiteId, parameters);
|
||||
|
||||
|
||||
@@ -3,6 +3,72 @@ import { json, unauthorized } from '@/lib/response';
|
||||
import { canViewWebsite } from '@/permissions';
|
||||
import { getReplayChunks } from '@/queries/sql';
|
||||
|
||||
function getEventTimestamp(event: any): number | null {
|
||||
const timestamp = Number(event?.timestamp);
|
||||
|
||||
return Number.isFinite(timestamp) ? timestamp : null;
|
||||
}
|
||||
|
||||
function parseOptionalInteger(value: string | null): number | undefined {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const parsed = Number(value);
|
||||
|
||||
return Number.isInteger(parsed) ? parsed : undefined;
|
||||
}
|
||||
|
||||
function mergeReplayEvents(
|
||||
chunks: Awaited<ReturnType<typeof getReplayChunks>>,
|
||||
{
|
||||
until,
|
||||
endChunkIndex,
|
||||
endEventIndex,
|
||||
}: { until?: number; endChunkIndex?: number; endEventIndex?: number },
|
||||
) {
|
||||
const events: any[] = [];
|
||||
let isSorted = true;
|
||||
let lastTimestamp = -Infinity;
|
||||
|
||||
for (const chunk of chunks) {
|
||||
if (endChunkIndex !== undefined && chunk.chunkIndex > endChunkIndex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let chunkEventIndex = 0; chunkEventIndex < chunk.events.length; chunkEventIndex++) {
|
||||
const event = chunk.events[chunkEventIndex];
|
||||
const timestamp = getEventTimestamp(event);
|
||||
|
||||
if (chunk.chunkIndex === endChunkIndex && endEventIndex !== undefined) {
|
||||
if (chunkEventIndex > endEventIndex) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (until !== undefined && timestamp !== null && timestamp > until) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (timestamp !== null) {
|
||||
if (timestamp < lastTimestamp) {
|
||||
isSorted = false;
|
||||
} else {
|
||||
lastTimestamp = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
events.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSorted) {
|
||||
events.sort((a, b) => (getEventTimestamp(a) ?? 0) - (getEventTimestamp(b) ?? 0));
|
||||
}
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
export async function GET(
|
||||
request: Request,
|
||||
{ params }: { params: Promise<{ websiteId: string; replayId: string }> },
|
||||
@@ -14,16 +80,18 @@ export async function GET(
|
||||
}
|
||||
|
||||
const { websiteId, replayId } = await params;
|
||||
const searchParams = new URL(request.url).searchParams;
|
||||
const until = parseOptionalInteger(searchParams.get('until'));
|
||||
const endChunkIndex = parseOptionalInteger(searchParams.get('chunkIndex'));
|
||||
const endEventIndex = parseOptionalInteger(searchParams.get('eventIndex'));
|
||||
const endAt = until !== undefined ? new Date(until) : undefined;
|
||||
|
||||
if (!(await canViewWebsite(auth, websiteId))) {
|
||||
return unauthorized();
|
||||
}
|
||||
|
||||
const chunks = await getReplayChunks(websiteId, replayId);
|
||||
|
||||
const allEvents = chunks
|
||||
.flatMap(chunk => chunk.events)
|
||||
.sort((a, b) => a.timestamp - b.timestamp);
|
||||
const chunks = await getReplayChunks(websiteId, replayId, { endAt, endChunkIndex });
|
||||
const allEvents = mergeReplayEvents(chunks, { until, endChunkIndex, endEventIndex });
|
||||
const sessionId = chunks.length > 0 ? chunks[0].sessionId : null;
|
||||
const startedAt = chunks.length > 0 ? chunks[0].startedAt : null;
|
||||
const endedAt = chunks.length > 0 ? chunks[chunks.length - 1].endedAt : null;
|
||||
|
||||
Reference in new issue
Block a user