33import { Button } from '@comp/ui/button' ;
44import { Select , SelectContent , SelectItem , SelectTrigger , SelectValue } from '@comp/ui/select' ;
55import { useRealtimeRun } from '@trigger.dev/react-hooks' ;
6- import { CheckCircle2 , Loader2 , RefreshCw , X } from 'lucide-react' ;
6+ import { CheckCircle2 , Info , Loader2 , RefreshCw , X } from 'lucide-react' ;
77import { useEffect , useState } from 'react' ;
88import { FindingsTable } from './FindingsTable' ;
99
@@ -27,6 +27,23 @@ interface ResultsViewProps {
2727
2828const severityOrder = { critical : 0 , high : 1 , medium : 2 , low : 3 , info : 4 } ;
2929
30+ // Helper function to extract clean error messages from cloud provider errors
31+ function extractCleanErrorMessage ( errorMessage : string ) : string {
32+ try {
33+ // Try to parse as JSON (GCP returns JSON blob)
34+ const parsed = JSON . parse ( errorMessage ) ;
35+
36+ // GCP error structure: { error: { message: "actual message" } }
37+ if ( parsed . error ?. message ) {
38+ return parsed . error . message ;
39+ }
40+ } catch {
41+ // Not JSON, return original
42+ }
43+
44+ return errorMessage ;
45+ }
46+
3047export function ResultsView ( {
3148 findings,
3249 scanTaskId,
@@ -46,6 +63,7 @@ export function ResultsView({
4663 const [ selectedStatus , setSelectedStatus ] = useState < string > ( 'all' ) ;
4764 const [ selectedSeverity , setSelectedSeverity ] = useState < string > ( 'all' ) ;
4865 const [ showSuccessBanner , setShowSuccessBanner ] = useState ( false ) ;
66+ const [ showErrorBanner , setShowErrorBanner ] = useState ( false ) ;
4967
5068 // Show success banner when scan completes, auto-hide after 5 seconds
5169 useEffect ( ( ) => {
@@ -58,6 +76,17 @@ export function ResultsView({
5876 }
5977 } , [ scanCompleted ] ) ;
6078
79+ // Auto-dismiss error banner after 30 seconds
80+ useEffect ( ( ) => {
81+ if ( scanFailed ) {
82+ setShowErrorBanner ( true ) ;
83+ const timer = setTimeout ( ( ) => {
84+ setShowErrorBanner ( false ) ;
85+ } , 30000 ) ;
86+ return ( ) => clearTimeout ( timer ) ;
87+ }
88+ } , [ scanFailed ] ) ;
89+
6190 // Get unique statuses and severities
6291 const uniqueStatuses = Array . from (
6392 new Set ( findings . map ( ( f ) => f . status ) . filter ( Boolean ) as string [ ] ) ,
@@ -117,15 +146,36 @@ export function ResultsView({
117146 </ div >
118147 ) }
119148
120- { scanFailed && ! isScanning && (
149+ { /* Propagation delay info banner - only when scan succeeds but returns empty output */ }
150+ { scanCompleted && findings . length === 0 && ! isScanning && ! scanFailed && (
151+ < div className = "bg-blue-50 dark:bg-blue-950/20 flex items-center gap-3 rounded-lg border border-blue-200 dark:border-blue-900 p-4" >
152+ < Info className = "text-blue-600 dark:text-blue-400 h-5 w-5 flex-shrink-0" />
153+ < div className = "flex-1" >
154+ < p className = "text-blue-900 dark:text-blue-100 text-sm font-medium" > Initial scan complete</ p >
155+ < p className = "text-muted-foreground text-xs" >
156+ Security findings may take 24-48 hours to appear after enabling cloud security services. Check back later.
157+ </ p >
158+ </ div >
159+ </ div >
160+ ) }
161+
162+ { showErrorBanner && scanFailed && ! isScanning && (
121163 < div className = "bg-destructive/10 flex items-center gap-3 rounded-lg border border-destructive/20 p-4" >
122164 < X className = "text-destructive h-5 w-5 flex-shrink-0" />
123165 < div className = "flex-1" >
124166 < p className = "text-destructive text-sm font-medium" > Scan failed</ p >
125167 < p className = "text-muted-foreground text-xs" >
126- { run ?. error ?. message || 'An error occurred during the scan. Please try again.' }
168+ { extractCleanErrorMessage ( run ?. error ?. message || 'An error occurred during the scan. Please try again.' ) }
127169 </ p >
128170 </ div >
171+ < Button
172+ variant = "ghost"
173+ size = "sm"
174+ onClick = { ( ) => setShowErrorBanner ( false ) }
175+ className = "text-muted-foreground hover:text-foreground h-auto p-1"
176+ >
177+ < X className = "h-4 w-4" />
178+ </ Button >
129179 </ div >
130180 ) }
131181
0 commit comments