Skip to content

Commit f989613

Browse files
[web] Add error boundaries around UI in run detail view (#694)
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
1 parent e6b1bef commit f989613

File tree

2 files changed

+109
-88
lines changed

2 files changed

+109
-88
lines changed

.changeset/afraid-paws-rescue.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@workflow/web": patch
3+
---
4+
5+
Add error boundaries around tabs in run detail view

packages/web/src/components/run-detail-view.tsx

Lines changed: 104 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { parseWorkflowName } from '@workflow/core/parse-name';
44
import {
55
cancelRun,
6+
ErrorBoundary,
67
type Event,
78
recreateRun,
89
type Step,
@@ -550,115 +551,130 @@ export function RunDetailView({
550551
</TabsList>
551552

552553
<TabsContent value="trace" className="mt-0 flex-1 min-h-0">
553-
<div className="h-full">
554-
<WorkflowTraceViewer
555-
error={error}
556-
steps={allSteps}
557-
events={allEvents}
558-
hooks={allHooks}
559-
env={env}
560-
run={run}
561-
isLoading={loading}
562-
onStreamClick={handleStreamClick}
563-
/>
564-
</div>
554+
<ErrorBoundary
555+
title="Trace Viewer Error"
556+
description="Failed to load trace viewer. Please try refreshing the page."
557+
>
558+
<div className="h-full">
559+
<WorkflowTraceViewer
560+
error={error}
561+
steps={allSteps}
562+
events={allEvents}
563+
hooks={allHooks}
564+
env={env}
565+
run={run}
566+
isLoading={loading}
567+
onStreamClick={handleStreamClick}
568+
/>
569+
</div>
570+
</ErrorBoundary>
565571
</TabsContent>
566572

567573
<TabsContent value="streams" className="mt-0 flex-1 min-h-0">
568-
<div className="h-full flex gap-4">
569-
{/* Stream list sidebar */}
570-
<div
571-
className="w-64 flex-shrink-0 border rounded-lg overflow-hidden"
572-
style={{
573-
borderColor: 'var(--ds-gray-300)',
574-
backgroundColor: 'var(--ds-background-100)',
575-
}}
576-
>
574+
<ErrorBoundary
575+
title="Streams Error"
576+
description="Failed to load streams. Please try refreshing the page."
577+
>
578+
<div className="h-full flex gap-4">
579+
{/* Stream list sidebar */}
577580
<div
578-
className="px-3 py-2 border-b text-xs font-medium"
581+
className="w-64 flex-shrink-0 border rounded-lg overflow-hidden"
579582
style={{
580583
borderColor: 'var(--ds-gray-300)',
581-
color: 'var(--ds-gray-900)',
584+
backgroundColor: 'var(--ds-background-100)',
582585
}}
583586
>
584-
Streams ({streams.length})
585-
</div>
586-
<div className="overflow-auto max-h-[calc(100vh-400px)]">
587-
{streamsLoading ? (
588-
<div className="p-4 flex items-center justify-center">
589-
<Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
590-
</div>
591-
) : streamsError ? (
592-
<div className="p-4 text-xs text-destructive">
593-
{streamsError.message}
594-
</div>
595-
) : streams.length === 0 ? (
596-
<div
597-
className="p-4 text-xs"
598-
style={{ color: 'var(--ds-gray-600)' }}
599-
>
600-
No streams found for this run
601-
</div>
602-
) : (
603-
streams.map((streamId) => (
604-
<button
605-
key={streamId}
606-
type="button"
607-
onClick={() => setSelectedStreamId(streamId)}
608-
className="w-full text-left px-3 py-2 text-xs font-mono truncate hover:bg-accent transition-colors"
609-
style={{
610-
backgroundColor:
611-
selectedStreamId === streamId
612-
? 'var(--ds-gray-200)'
613-
: 'transparent',
614-
color: 'var(--ds-gray-1000)',
615-
}}
616-
title={streamId}
617-
>
618-
{streamId}
619-
</button>
620-
))
621-
)}
622-
</div>
623-
</div>
624-
625-
{/* Stream viewer */}
626-
<div className="flex-1 min-w-0">
627-
{selectedStreamId ? (
628-
<StreamViewer env={env} streamId={selectedStreamId} />
629-
) : (
630587
<div
631-
className="h-full flex items-center justify-center rounded-lg border"
588+
className="px-3 py-2 border-b text-xs font-medium"
632589
style={{
633590
borderColor: 'var(--ds-gray-300)',
634-
backgroundColor: 'var(--ds-gray-100)',
591+
color: 'var(--ds-gray-900)',
635592
}}
636593
>
594+
Streams ({streams.length})
595+
</div>
596+
<div className="overflow-auto max-h-[calc(100vh-400px)]">
597+
{streamsLoading ? (
598+
<div className="p-4 flex items-center justify-center">
599+
<Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
600+
</div>
601+
) : streamsError ? (
602+
<div className="p-4 text-xs text-destructive">
603+
{streamsError.message}
604+
</div>
605+
) : streams.length === 0 ? (
606+
<div
607+
className="p-4 text-xs"
608+
style={{ color: 'var(--ds-gray-600)' }}
609+
>
610+
No streams found for this run
611+
</div>
612+
) : (
613+
streams.map((streamId) => (
614+
<button
615+
key={streamId}
616+
type="button"
617+
onClick={() => setSelectedStreamId(streamId)}
618+
className="w-full text-left px-3 py-2 text-xs font-mono truncate hover:bg-accent transition-colors"
619+
style={{
620+
backgroundColor:
621+
selectedStreamId === streamId
622+
? 'var(--ds-gray-200)'
623+
: 'transparent',
624+
color: 'var(--ds-gray-1000)',
625+
}}
626+
title={streamId}
627+
>
628+
{streamId}
629+
</button>
630+
))
631+
)}
632+
</div>
633+
</div>
634+
635+
{/* Stream viewer */}
636+
<div className="flex-1 min-w-0">
637+
{selectedStreamId ? (
638+
<StreamViewer env={env} streamId={selectedStreamId} />
639+
) : (
637640
<div
638-
className="text-sm"
639-
style={{ color: 'var(--ds-gray-600)' }}
641+
className="h-full flex items-center justify-center rounded-lg border"
642+
style={{
643+
borderColor: 'var(--ds-gray-300)',
644+
backgroundColor: 'var(--ds-gray-100)',
645+
}}
640646
>
641-
{streams.length > 0
642-
? 'Select a stream to view its data'
643-
: 'No streams available'}
647+
<div
648+
className="text-sm"
649+
style={{ color: 'var(--ds-gray-600)' }}
650+
>
651+
{streams.length > 0
652+
? 'Select a stream to view its data'
653+
: 'No streams available'}
654+
</div>
644655
</div>
645-
</div>
646-
)}
656+
)}
657+
</div>
647658
</div>
648-
</div>
659+
</ErrorBoundary>
649660
</TabsContent>
650661

651662
{isLocalBackend && (
652663
<TabsContent value="graph" className="mt-0 flex-1 min-h-0">
653-
<div className="h-full min-h-[500px]">
654-
<GraphTabContent
655-
config={config}
656-
run={run}
657-
allSteps={allSteps}
658-
allEvents={allEvents}
659-
env={env}
660-
/>
661-
</div>
664+
<ErrorBoundary
665+
title="Graph Viewer Error"
666+
description="Failed to load execution graph. Please try refreshing the page."
667+
>
668+
<div className="h-full min-h-[500px]">
669+
<GraphTabContent
670+
config={config}
671+
run={run}
672+
allSteps={allSteps}
673+
allEvents={allEvents}
674+
env={env}
675+
/>
676+
</div>
677+
</ErrorBoundary>
662678
</TabsContent>
663679
)}
664680
</Tabs>

0 commit comments

Comments
 (0)