1
- import { format } from "date-fns" ;
1
+ import { formatDistanceToNow } from "date-fns" ;
2
2
import {
3
3
Cell ,
4
4
Column ,
@@ -12,58 +12,71 @@ import {
12
12
SearchFieldClearButton ,
13
13
Badge ,
14
14
Button ,
15
+ ResizableTableContainer ,
15
16
} from "@stacklok/ui-kit" ;
16
17
import { Switch } from "@stacklok/ui-kit" ;
17
- import { AlertConversation } from "@/api/generated" ;
18
+ import { AlertConversation , QuestionType } from "@/api/generated" ;
18
19
import { Tooltip , TooltipTrigger } from "@stacklok/ui-kit" ;
19
- import { getMaliciousPackage } from "@/lib/utils" ;
20
- import { Search } from "lucide-react" ;
21
- import { Markdown } from "./Markdown" ;
20
+ import {
21
+ sanitizeQuestionPrompt ,
22
+ parsingPromptText ,
23
+ getIssueDetectedType ,
24
+ } from "@/lib/utils" ;
25
+ import { KeyRoundIcon , PackageX , Search } from "lucide-react" ;
22
26
import { useAlertSearch } from "@/hooks/useAlertSearch" ;
23
27
import { useCallback } from "react" ;
24
28
import { useNavigate , useSearchParams } from "react-router-dom" ;
25
29
import { useFilteredAlerts } from "@/hooks/useAlertsData" ;
26
30
import { useClientSidePagination } from "@/hooks/useClientSidePagination" ;
27
31
28
- const wrapObjectOutput = ( input : AlertConversation [ "trigger_string" ] ) => {
29
- const data = getMaliciousPackage ( input ) ;
30
- if ( data === null ) return "N/A" ;
31
- if ( typeof data === "string" ) {
32
- return (
33
- < div className = "bg-gray-25 rounded-lg overflow-auto p-4" >
34
- < Markdown > { data } </ Markdown >
35
- </ div >
36
- ) ;
32
+ const getTitle = ( alert : AlertConversation ) => {
33
+ const prompt = alert . conversation ;
34
+ const title = parsingPromptText (
35
+ sanitizeQuestionPrompt ( {
36
+ question : prompt . question_answers ?. [ 0 ] ?. question . message ?? "" ,
37
+ answer : prompt . question_answers ?. [ 0 ] ?. answer ?. message ?? "" ,
38
+ } ) ,
39
+ prompt . conversation_timestamp ,
40
+ ) ;
41
+
42
+ return title ;
43
+ } ;
44
+
45
+ function TypeCellContent ( { alert } : { alert : AlertConversation } ) {
46
+ const conversationType = alert . conversation . type ;
47
+
48
+ switch ( conversationType ) {
49
+ case QuestionType . CHAT :
50
+ return "Chat" ;
51
+ case QuestionType . FIM :
52
+ return "Code Suggestion" ;
53
+ default :
54
+ return "Unknown" ;
37
55
}
38
- if ( ! data . type || ! data . name ) return "N/A" ;
56
+ }
39
57
40
- return (
41
- < div className = "max-h-40 w-fit overflow-y-auto whitespace-pre-wrap p-2" >
42
- < label className = "font-medium" > Package:</ label >
43
-
44
- < a
45
- href = { `https://www.insight.stacklok.com/report/${ data . type } /${ data . name } ` }
46
- target = "_blank"
47
- rel = "noopener noreferrer"
48
- className = "text-brand-500 hover:underline"
49
- >
50
- { data . type } /{ data . name }
51
- </ a >
52
- { data . status && (
58
+ function IssueDetectedCellContent ( { alert } : { alert : AlertConversation } ) {
59
+ const issueDetected = getIssueDetectedType ( alert ) ;
60
+
61
+ switch ( issueDetected ) {
62
+ case "leaked_secret" :
63
+ return (
53
64
< >
54
- < br />
55
- < label className = "font-medium" > Status: </ label > { data . status }
65
+ < KeyRoundIcon className = "size-4 text-blue-700" />
66
+ Blocked secret exposure
56
67
</ >
57
- ) }
58
- { data . description && (
68
+ ) ;
69
+ case "malicious_package" :
70
+ return (
59
71
< >
60
- < br />
61
- < label className = "font-medium" > Description: </ label > { data . description }
72
+ < PackageX className = "size-4 text-blue-700" />
73
+ Blocked malicious package
62
74
</ >
63
- ) }
64
- </ div >
65
- ) ;
66
- } ;
75
+ ) ;
76
+ default :
77
+ return "" ;
78
+ }
79
+ }
67
80
68
81
export function AlertsTable ( ) {
69
82
const {
@@ -161,55 +174,46 @@ export function AlertsTable() {
161
174
</ div >
162
175
</ div >
163
176
< div className = "overflow-x-auto" >
164
- < Table data-testid = "alerts-table" aria-label = "Alerts table" >
165
- < TableHeader >
166
- < Row >
167
- < Column isRowHeader width = { 150 } >
168
- Trigger Type
169
- </ Column >
170
- < Column width = { 300 } > Trigger Token</ Column >
171
- < Column width = { 150 } > File</ Column >
172
- < Column width = { 250 } > Code</ Column >
173
- < Column width = { 100 } > Timestamp</ Column >
174
- </ Row >
175
- </ TableHeader >
176
- < TableBody >
177
- { dataView . map ( ( alert ) => (
178
- < Row
179
- key = { alert . alert_id }
180
- className = "h-20"
181
- onAction = { ( ) =>
182
- navigate ( `/prompt/${ alert . conversation . chat_id } ` )
183
- }
184
- >
185
- < Cell className = "truncate" > { alert . trigger_type } </ Cell >
186
- < Cell className = "overflow-auto whitespace-nowrap max-w-80" >
187
- { wrapObjectOutput ( alert . trigger_string ) }
188
- </ Cell >
189
- < Cell className = "truncate" >
190
- { alert . code_snippet ?. filepath || "N/A" }
191
- </ Cell >
192
- < Cell className = "overflow-auto whitespace-nowrap max-w-80" >
193
- { alert . code_snippet ?. code ? (
194
- < pre className = "max-h-40 overflow-auto bg-gray-100 p-2 whitespace-pre-wrap" >
195
- < code > { alert . code_snippet . code } </ code >
196
- </ pre >
197
- ) : (
198
- "N/A"
199
- ) }
200
- </ Cell >
201
- < Cell className = "truncate" >
202
- < div data-testid = "date" >
203
- { format ( new Date ( alert . timestamp ?? "" ) , "y/MM/dd" ) }
204
- </ div >
205
- < div data-testid = "time" >
206
- { format ( new Date ( alert . timestamp ?? "" ) , "hh:mm:ss a" ) }
207
- </ div >
208
- </ Cell >
177
+ < ResizableTableContainer >
178
+ < Table data-testid = "alerts-table" aria-label = "Alerts table" >
179
+ < TableHeader >
180
+ < Row >
181
+ < Column isRowHeader width = { 150 } >
182
+ Time
183
+ </ Column >
184
+ < Column width = { 150 } > Type</ Column >
185
+ < Column > Event</ Column >
186
+ < Column width = { 325 } > Issue Detected</ Column >
209
187
</ Row >
210
- ) ) }
211
- </ TableBody >
212
- </ Table >
188
+ </ TableHeader >
189
+ < TableBody >
190
+ { dataView . map ( ( alert ) => (
191
+ < Row
192
+ key = { alert . alert_id }
193
+ className = "h-20"
194
+ onAction = { ( ) =>
195
+ navigate ( `/prompt/${ alert . conversation . chat_id } ` )
196
+ }
197
+ >
198
+ < Cell className = "truncate" >
199
+ { formatDistanceToNow ( new Date ( alert . timestamp ) , {
200
+ addSuffix : true ,
201
+ } ) }
202
+ </ Cell >
203
+ < Cell className = "truncate" >
204
+ < TypeCellContent alert = { alert } />
205
+ </ Cell >
206
+ < Cell className = "truncate" > { getTitle ( alert ) } </ Cell >
207
+ < Cell >
208
+ < div className = "truncate flex gap-2 items-center" >
209
+ < IssueDetectedCellContent alert = { alert } />
210
+ </ div >
211
+ </ Cell >
212
+ </ Row >
213
+ ) ) }
214
+ </ TableBody >
215
+ </ Table >
216
+ </ ResizableTableContainer >
213
217
</ div >
214
218
215
219
< div className = "flex justify-center w-full p-4" >
0 commit comments