import type { Payload } from "payload"; import type { AnalyticsEvent } from "@/payload-types"; import { aggregateEvents, type AggregatedStats } from "@/lib/analytics/aggregate"; export type DashboardRange = { from: Date; to: Date }; export type DailyPoint = { date: string; pageViews: number; sessions: number }; export type DashboardStats = AggregatedStats & { series: DailyPoint[]; }; function dateKey(iso: string): string { return iso.slice(0, 10); } async function fetchEvents(payload: Payload, range: DashboardRange): Promise { const { docs } = await payload.find({ collection: "analytics-events", where: { and: [ { createdAt: { greater_than_equal: range.from.toISOString() } }, { createdAt: { less_than_equal: range.to.toISOString() } }, ], }, limit: 0, depth: 0, sort: "createdAt", }); return docs; } function buildSeries(events: AnalyticsEvent[]): DailyPoint[] { const byDay = new Map }>(); for (const ev of events) { const key = dateKey(ev.createdAt); const bucket = byDay.get(key) ?? { pageViews: 0, sessions: new Set() }; if (ev.eventName === "page_view") bucket.pageViews += 1; bucket.sessions.add(ev.sessionId); byDay.set(key, bucket); } return [...byDay.entries()] .sort(([a], [b]) => a.localeCompare(b)) .map(([date, bucket]) => ({ date, pageViews: bucket.pageViews, sessions: bucket.sessions.size })); } export async function getDashboardStats(payload: Payload, range: DashboardRange): Promise { const events = await fetchEvents(payload, range); return { ...aggregateEvents(events), series: buildSeries(events) }; } export type PercentDelta = number | null; // null = no comparison basis (previous value was 0) export function percentDelta(current: number, previous: number): PercentDelta { if (previous === 0) return current === 0 ? 0 : null; return Math.round(((current - previous) / previous) * 1000) / 10; } export async function getDashboardComparison( payload: Payload, range: DashboardRange, compareRange: DashboardRange, ): Promise<{ current: DashboardStats; previous: DashboardStats }> { const [current, previous] = await Promise.all([ getDashboardStats(payload, range), getDashboardStats(payload, compareRange), ]); return { current, previous }; }