export interface DailyCount { day: string; count: number; } export function mergeRiskActivity< T extends { userId: string; observedAt: Date }, S extends { userId: string | null }, >(latestRows: T[], summaryRows: S[]) { const summaries = new Map(summaryRows.flatMap((summary) => summary.userId ? [[summary.userId, summary] as const] : [])); return latestRows .flatMap((activity) => { const summary = summaries.get(activity.userId); return summary ? [{ ...activity, ...summary }] : []; }) .sort((left, right) => right.observedAt.getTime() - left.observedAt.getTime()); } export function fillDailySeries(rows: DailyCount[], end: Date, days: number) { const counts = new Map(rows.map((row) => [row.day, Number(row.count)])); const endDay = new Date(Date.UTC(end.getUTCFullYear(), end.getUTCMonth(), end.getUTCDate())); return Array.from({ length: days }, (_, index) => { const date = new Date(endDay); date.setUTCDate(endDay.getUTCDate() - (days - index - 1)); const day = date.toISOString().slice(0, 10); return { day, count: counts.get(day) ?? 0 }; }); }