feat(telemetry): generic event aggregates and usage range presets

- rollups emit event.count (dim name) and event.detail.count (dim name|detail
  from props.source/emoji/tournament)
- usage screen: events section with tap-through breakdown; 7d/30d/180d/custom
  date-range presets
This commit is contained in:
2026-08-26 22:53:58 -07:00
parent cb16d9757e
commit 8171354e2a
8 changed files with 370 additions and 89 deletions
+22
View File
@@ -14,6 +14,7 @@ export interface ClientEventRow {
session_id?: string;
route_id?: string;
value?: number;
props?: { source?: string; emoji?: string; tournament?: string } | null;
}
export interface ClientErrorRow {
@@ -64,6 +65,8 @@ export interface ClientEventAccumulator {
sessions: Set<string>;
pageViewsByRoute: Map<string, number>;
vitals: Map<string, number[]>;
eventCounts: Map<string, number>;
eventDetails: Map<string, number>;
}
export const newClientEventAccumulator = (): ClientEventAccumulator => ({
@@ -71,8 +74,13 @@ export const newClientEventAccumulator = (): ClientEventAccumulator => ({
sessions: new Set(),
pageViewsByRoute: new Map(),
vitals: new Map(),
eventCounts: new Map(),
eventDetails: new Map(),
});
const eventDetail = (row: ClientEventRow): string | undefined =>
row.props?.source ?? row.props?.emoji ?? row.props?.tournament;
export const accumulateClientEvents = (acc: ClientEventAccumulator, rows: ClientEventRow[]) => {
for (const row of rows) {
if (row.player) acc.players.add(row.player);
@@ -86,6 +94,13 @@ export const accumulateClientEvents = (acc: ClientEventAccumulator, rows: Client
const values = acc.vitals.get(metric) ?? [];
values.push(row.value);
acc.vitals.set(metric, values);
} else {
acc.eventCounts.set(row.name, (acc.eventCounts.get(row.name) ?? 0) + 1);
const detail = eventDetail(row);
if (detail) {
const key = `${row.name}|${detail}`;
acc.eventDetails.set(key, (acc.eventDetails.get(key) ?? 0) + 1);
}
}
}
};
@@ -157,6 +172,13 @@ export const buildDayRollups = (
push("page_view.count", "", totalPageViews);
push("page_view.sessions", "", clientEvents.sessions.size);
for (const [name, count] of clientEvents.eventCounts) {
push("event.count", name, count);
}
for (const [key, count] of clientEvents.eventDetails) {
push("event.detail.count", key, count);
}
push("client_error.count", "", clientErrors.total);
for (const [group, count] of clientErrors.byGroup) {
push("client_error.count", group, count);