Spaces:
Sleeping
Sleeping
| import type { ChannelPoint, DailyPoint, DayKey, Event, EventType, Kpis, Lead } from "@/lib/domain" | |
| import { clamp, toDayKey } from "@/lib/time" | |
| export function computeKpis(args: { events: Event[]; leads: Lead[] }): Kpis { | |
| const uv = countUv(args.events) | |
| const clicks = countType(args.events, "click") | |
| const submits = countType(args.events, "submit") | |
| const claims = countType(args.events, "claim") | |
| const redeems = countType(args.events, "redeem") | |
| const submitRate = uv ? submits / uv : 0 | |
| const redeemRate = submits ? redeems / submits : 0 | |
| return { uv, clicks, submits, claims, redeems, submitRate, redeemRate } | |
| } | |
| export function dailySeries(args: { events: Event[]; days: number }): DailyPoint[] { | |
| const map = new Map<DayKey, DailyPoint>() | |
| const types: EventType[] = ["view", "submit", "redeem"] | |
| for (const e of args.events) { | |
| if (!types.includes(e.type)) continue | |
| const day = toDayKey(e.ts) | |
| const cur = map.get(day) ?? { day, uv: 0, submits: 0, redeems: 0 } | |
| if (e.type === "view") cur.uv += 1 | |
| if (e.type === "submit") cur.submits += 1 | |
| if (e.type === "redeem") cur.redeems += 1 | |
| map.set(day, cur) | |
| } | |
| const keys = Array.from(map.keys()).sort((a, b) => a.localeCompare(b)) | |
| const tail = keys.slice(Math.max(0, keys.length - args.days)) | |
| return tail.map((k) => map.get(k)!).map((p) => ({ ...p, uv: clamp(p.uv, 0, 999999) })) | |
| } | |
| export function channelSeries(args: { events: Event[]; max: number }): ChannelPoint[] { | |
| const map = new Map<string, ChannelPoint>() | |
| for (const e of args.events) { | |
| const channel = (e.meta?.utm_source as string | undefined) ?? "direct" | |
| const cur = map.get(channel) ?? { channel, uv: 0, submits: 0, redeems: 0 } | |
| if (e.type === "view") cur.uv += 1 | |
| if (e.type === "submit") cur.submits += 1 | |
| if (e.type === "redeem") cur.redeems += 1 | |
| map.set(channel, cur) | |
| } | |
| return Array.from(map.values()) | |
| .sort((a, b) => b.uv - a.uv) | |
| .slice(0, args.max) | |
| } | |
| export function countUv(events: Event[]) { | |
| const views = events.filter((e) => e.type === "view") | |
| return views.length | |
| } | |
| export function countType(events: Event[], type: EventType) { | |
| return events.reduce((acc, e) => acc + (e.type === type ? 1 : 0), 0) | |
| } | |