@@ -90,6 +90,13 @@ function formatBucket(value: string) {
9090 } ) . format ( date ) ;
9191}
9292
93+ function formatDimension ( value : string ) {
94+ return value
95+ . split ( "_" )
96+ . map ( ( part ) => part . charAt ( 0 ) . toUpperCase ( ) + part . slice ( 1 ) )
97+ . join ( " " ) ;
98+ }
99+
93100function maxSeriesValue ( points : AnalyticsTimeSeriesPoint [ ] ) {
94101 return Math . max ( 1 , ...points . map ( ( point ) => Math . max ( point . views , point . request_count ) ) ) ;
95102}
@@ -198,16 +205,18 @@ export default function AnalyticsClient({
198205 { analytics ? (
199206 < div className = "flex flex-col gap-5" >
200207 < StatGrid >
201- < Stat label = "Views" value = { formatNumber ( analytics . views ) } hint = "Sessions with first frame" icon = { Eye } />
208+ < Stat label = "Views" value = { formatNumber ( analytics . views ) } hint = "Playback sessions with first frame" icon = { Eye } />
209+ < Stat label = "Unique viewers" value = { formatNumber ( analytics . unique_viewers ) } hint = "Pseudonymous viewer hashes" icon = { Eye } />
202210 < Stat label = "Watch time" value = { formatWatchTime ( analytics . watch_time_ms ) } hint = "Player heartbeat estimate" icon = { Clock } />
203211 < Stat label = "Startup" value = { formatPercent ( analytics . startup_success_rate ) } hint = { `p95 ${ formatMs ( analytics . startup_p95_ms ) } ` } icon = { Gauge } />
204212 < Stat label = "Rebuffer" value = { formatPercent ( analytics . rebuffer_ratio ) } hint = { `${ formatNumber ( analytics . stalled_sessions ) } stalled sessions` } icon = { Activity } />
205213 </ StatGrid >
206214
207215 < StatGrid >
208- < Stat label = "Requests" value = { formatCompact ( analytics . request_count ) } hint = { ` ${ formatBytes ( analytics . bytes_served ) } served` } icon = { Database } />
216+ < Stat label = "Requests" value = { formatCompact ( analytics . request_count ) } hint = "Edge artifact requests" icon = { Database } />
209217 < Stat label = "Cache hit" value = { formatPercent ( analytics . cache_hit_rate ) } hint = "Edge request rollup" icon = { Signal } />
210218 < Stat label = "Errors" value = { formatPercent ( analytics . error_rate ) } hint = { `${ formatNumber ( analytics . playback_failures ) } player failures` } icon = { Activity } />
219+ < Stat label = "Completions" value = { formatNumber ( analytics . completions ) } hint = { `${ formatNumber ( analytics . exits_before_start ) } exits before start` } icon = { Activity } />
211220 < Stat label = "Edge latency" value = { formatMs ( analytics . request_p95_ms ) } hint = { `p50 ${ formatMs ( analytics . request_p50_ms ) } ` } icon = { Gauge } />
212221 </ StatGrid >
213222
@@ -277,6 +286,38 @@ export default function AnalyticsClient({
277286 </ Table >
278287 ) }
279288 </ Panel >
289+
290+ < Panel title = "Breakdowns" flush = { analytics . breakdowns . length > 0 } >
291+ { analytics . breakdowns . length === 0 ? (
292+ < p className = "text-[13.5px] text-muted" > No breakdowns available yet.</ p >
293+ ) : (
294+ < div className = "grid gap-4 lg:grid-cols-2" >
295+ { analytics . breakdowns . map ( ( breakdown ) => (
296+ < div key = { breakdown . dimension } className = "min-w-0" >
297+ < p className = "px-4 pb-2 pt-3 text-[12px] font-medium text-faint" >
298+ { formatDimension ( breakdown . dimension ) }
299+ </ p >
300+ < Table >
301+ < TBody >
302+ { breakdown . rows . map ( ( row ) => (
303+ < TR key = { `${ breakdown . dimension } :${ row . value } ` } >
304+ < TD className = "max-w-[220px] truncate text-[12.5px] text-ink-soft" >
305+ { row . value }
306+ </ TD >
307+ < TD className = "text-right font-mono text-[12px] tabular-nums text-muted" >
308+ { row . request_count > 0
309+ ? formatNumber ( row . request_count )
310+ : formatNumber ( row . views ) }
311+ </ TD >
312+ </ TR >
313+ ) ) }
314+ </ TBody >
315+ </ Table >
316+ </ div >
317+ ) ) }
318+ </ div >
319+ ) }
320+ </ Panel >
280321 </ div >
281322 ) : null }
282323 </ DashboardContent >
0 commit comments