@@ -22,6 +22,13 @@ import {
2222} from '@/components/ui/table'
2323import { Tabs , TabsContent , TabsList , TabsTrigger } from '@/components/ui/tabs'
2424import { displayDstIp , displaySrcIp } from '@/lib/ipAliasDisplay'
25+ import {
26+ eventPayloadField ,
27+ formatJsonField ,
28+ highlightJSON ,
29+ isJsonDisplayable ,
30+ serializeRowForDisplay ,
31+ } from '@/lib/jsonDisplay'
2532import { cn } from '@/lib/utils'
2633import { newModalKey } from '@/lib/modalKey'
2734import { useLocale } from '@/components/locale/locale-provider'
@@ -291,6 +298,11 @@ function formatEventsCell(value, col, timeZone, locale) {
291298 const s = formatDateTime ( value , locale , timeZone )
292299 return s || '—'
293300 }
301+ const payloadLike = col . keys ?. some ( ( k ) => k === 'payload' || k === 'message' || k === 'data' || k === 'body' )
302+ if ( payloadLike && isJsonDisplayable ( value ) ) {
303+ const pretty = formatJsonField ( value ) . replace ( / \s + / g, ' ' )
304+ return pretty . length > 200 ? `${ pretty . slice ( 0 , 197 ) } …` : pretty
305+ }
294306 if ( typeof value === 'object' ) {
295307 try {
296308 const j = JSON . stringify ( value )
@@ -303,16 +315,85 @@ function formatEventsCell(value, col, timeZone, locale) {
303315 return s . length > 200 ? `${ s . slice ( 0 , 197 ) } …` : s
304316}
305317
306- function serializeRowJSONForWindow ( row ) {
307- try {
308- return JSON . stringify (
309- row ,
310- ( _k , v ) => ( typeof v === 'bigint' ? v . toString ( ) : v ) ,
311- 2 ,
312- )
313- } catch ( e ) {
314- return `Error serializing row: ${ e ?. message || e } `
315- }
318+ function EventRecordDetail ( { row } ) {
319+ const [ recordTab , setRecordTab ] = React . useState ( 'pretty' )
320+ const [ payloadTab , setPayloadTab ] = React . useState ( 'pretty' )
321+ const payloadVal = eventPayloadField ( row )
322+ const payloadIsJson = isJsonDisplayable ( payloadVal )
323+ const recordText = serializeRowForDisplay ( row )
324+ const recordPrettyHtml = highlightJSON ( recordText )
325+ const payloadPrettyHtml = highlightJSON ( payloadVal )
326+
327+ return (
328+ < div className = "flex min-h-0 flex-1 flex-col gap-3 overflow-hidden" >
329+ { payloadIsJson ? (
330+ < div className = "shrink-0 space-y-1" >
331+ < span className = "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground" >
332+ Payload
333+ </ span >
334+ < Tabs value = { payloadTab } onValueChange = { setPayloadTab } className = "flex flex-col gap-2" >
335+ < TabsList variant = "line" className = "h-8 w-fit justify-start" >
336+ < TabsTrigger value = "pretty" className = "text-[11px]" >
337+ Pretty
338+ </ TabsTrigger >
339+ < TabsTrigger value = "raw" className = "text-[11px]" >
340+ Raw
341+ </ TabsTrigger >
342+ </ TabsList >
343+ < TabsContent value = "pretty" className = "mt-0" >
344+ < ScrollArea className = "max-h-[220px] rounded-md border border-border bg-muted/40" >
345+ < pre
346+ className = "whitespace-pre p-2 font-mono text-[11px] leading-relaxed"
347+ dangerouslySetInnerHTML = { { __html : payloadPrettyHtml || '(no payload)' } }
348+ />
349+ </ ScrollArea >
350+ </ TabsContent >
351+ < TabsContent value = "raw" className = "mt-0" >
352+ < ScrollArea className = "max-h-[220px] rounded-md border border-border bg-muted/40" >
353+ < pre className = "whitespace-pre-wrap break-all p-2 font-mono text-[11px] leading-relaxed text-foreground" >
354+ { formatJsonField ( payloadVal ) }
355+ </ pre >
356+ </ ScrollArea >
357+ </ TabsContent >
358+ </ Tabs >
359+ </ div >
360+ ) : null }
361+ < div className = "flex min-h-0 flex-1 flex-col gap-1 overflow-hidden" >
362+ < span className = "shrink-0 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground" >
363+ Full record
364+ </ span >
365+ < Tabs
366+ value = { recordTab }
367+ onValueChange = { setRecordTab }
368+ className = "flex min-h-0 flex-1 flex-col overflow-hidden"
369+ >
370+ < TabsList variant = "line" className = "h-8 w-fit shrink-0 justify-start" >
371+ < TabsTrigger value = "pretty" className = "text-[11px]" >
372+ Pretty
373+ </ TabsTrigger >
374+ < TabsTrigger value = "raw" className = "text-[11px]" >
375+ Raw
376+ </ TabsTrigger >
377+ </ TabsList >
378+ < TabsContent value = "pretty" className = "mt-0 min-h-0 flex-1 overflow-hidden" >
379+ < ScrollArea className = "min-h-0 flex-1 rounded-md border border-border bg-muted/40" >
380+ < pre
381+ className = "whitespace-pre p-2 font-mono text-[11px] leading-relaxed"
382+ dangerouslySetInnerHTML = { { __html : recordPrettyHtml || '(empty)' } }
383+ />
384+ </ ScrollArea >
385+ </ TabsContent >
386+ < TabsContent value = "raw" className = "mt-0 min-h-0 flex-1 overflow-hidden" >
387+ < ScrollArea className = "min-h-0 flex-1 rounded-md border border-border bg-muted/40" >
388+ < pre className = "whitespace-pre-wrap break-words p-2 font-mono text-[11px] leading-relaxed text-foreground" >
389+ { recordText }
390+ </ pre >
391+ </ ScrollArea >
392+ </ TabsContent >
393+ </ Tabs >
394+ </ div >
395+ </ div >
396+ )
316397}
317398
318399function EventsTab ( { data, isLoading, err, emptyLabel, timeZone, locale } ) {
@@ -408,15 +489,8 @@ function EventsTab({ data, isLoading, err, emptyLabel, timeZone, locale }) {
408489 minHeight = { 280 }
409490 className = "flex min-h-0 flex-col"
410491 >
411- < div className = "flex min-h-0 flex-1 flex-col gap-2 overflow-hidden px-3 pb-3 pt-0" >
412- < p className = "shrink-0 text-[10px] text-muted-foreground" >
413- Full record including data_extra (JSON).
414- </ p >
415- < ScrollArea className = "min-h-0 flex-1 rounded-md border border-border bg-muted/40" >
416- < pre className = "whitespace-pre-wrap break-words p-3 font-mono text-[11px] leading-relaxed text-foreground" >
417- { serializeRowJSONForWindow ( detailRow ) }
418- </ pre >
419- </ ScrollArea >
492+ < div className = "flex min-h-0 flex-1 flex-col overflow-hidden px-3 pb-3 pt-0" >
493+ < EventRecordDetail row = { detailRow } />
420494 </ div >
421495 </ FloatingWindow >
422496 ) : null }
@@ -539,15 +613,8 @@ function OtlpLogsTab({
539613 minHeight = { 320 }
540614 className = "flex min-h-0 flex-col"
541615 >
542- < div className = "flex min-h-0 flex-1 flex-col gap-2 overflow-hidden px-3 pb-3 pt-0" >
543- < p className = "shrink-0 text-[10px] text-muted-foreground" >
544- Full record (JSON), including fields not shown in the table.
545- </ p >
546- < ScrollArea className = "min-h-0 flex-1 rounded-md border border-border bg-muted/40" >
547- < pre className = "whitespace-pre-wrap break-words p-3 font-mono text-[11px] leading-relaxed text-foreground" >
548- { serializeRowJSONForWindow ( detailRow ) }
549- </ pre >
550- </ ScrollArea >
616+ < div className = "flex min-h-0 flex-1 flex-col overflow-hidden px-3 pb-3 pt-0" >
617+ < EventRecordDetail row = { detailRow } />
551618 </ div >
552619 </ FloatingWindow >
553620 ) : null }
0 commit comments