1- import { useCallback , useEffect , useMemo , useRef , useState } from 'react'
1+ import { Fragment , useCallback , useEffect , useMemo , useRef , useState } from 'react'
22import { AlertTriangle , Loader2 } from 'lucide-react'
33import { useLocation , useNavigate } from 'react-router-dom'
44import { useTranslation } from 'react-i18next'
@@ -29,6 +29,7 @@ import { AlertsVolumeCard } from '../components/alerts-volume-card'
2929import { AlertsBreakdownCard } from '../components/alerts-breakdown-card'
3030import { AlertsBulkBar } from '../components/alerts-bulk-bar'
3131import { AlertsTableHeader , AlertRow } from '../components/alerts-table'
32+ import { EchoesTimeline } from '../components/echoes-timeline'
3233import { AlertDrawer } from '../components/alert-drawer'
3334import { AlertIncidentModal } from '../components/alert-incident-modal'
3435import { Center } from '../components/ui-primitives'
@@ -48,9 +49,18 @@ export function AlertsPage() {
4849 const [ pageSize ] = useState ( 50 )
4950
5051 const [ selected , setSelected ] = useState < Set < string > > ( new Set ( ) )
52+ const [ expandedEchoes , setExpandedEchoes ] = useState < Set < string > > ( new Set ( ) )
5153 const [ openAlert , setOpenAlert ] = useState < Alert | null > ( null )
5254 const [ incidentTargets , setIncidentTargets ] = useState < Alert [ ] | null > ( null )
5355
56+ const toggleEchoes = ( id : string ) =>
57+ setExpandedEchoes ( ( prev ) => {
58+ const next = new Set ( prev )
59+ if ( next . has ( id ) ) next . delete ( id )
60+ else next . add ( id )
61+ return next
62+ } )
63+
5464 // SOC-AI chat navigation: seed the filters + time window the agent emitted.
5565 const location = useLocation ( )
5666 const navigate = useNavigate ( )
@@ -239,19 +249,23 @@ export function AlertsPage() {
239249 < div className = "px-6 py-16 text-center text-sm text-muted-foreground" > { t ( 'alerts.list.empty' ) } </ div >
240250 ) : (
241251 alerts . map ( ( a ) => (
242- < AlertRow
243- key = { a . id }
244- alert = { a }
245- tagCatalog = { tagCatalog }
246- checked = { selected . has ( a . id ) }
247- onToggle = { ( ) => toggleSel ( a . id ) }
248- onOpen = { ( ) => setOpenAlert ( a ) }
249- onCreateRule = { ( alert ) =>
250- navigate ( '/threat-management/alerts/tagging-rules' , {
251- state : { createWithConditions : alertToRuleConditions ( alert ) } ,
252- } )
253- }
254- />
252+ < Fragment key = { a . id } >
253+ < AlertRow
254+ alert = { a }
255+ tagCatalog = { tagCatalog }
256+ checked = { selected . has ( a . id ) }
257+ expanded = { expandedEchoes . has ( a . id ) }
258+ onToggle = { ( ) => toggleSel ( a . id ) }
259+ onOpen = { ( ) => setOpenAlert ( a ) }
260+ onCreateRule = { ( alert ) =>
261+ navigate ( '/threat-management/alerts/tagging-rules' , {
262+ state : { createWithConditions : alertToRuleConditions ( alert ) } ,
263+ } )
264+ }
265+ onToggleEchoes = { ( ) => toggleEchoes ( a . id ) }
266+ />
267+ { expandedEchoes . has ( a . id ) && < EchoesTimeline parentId = { a . id } /> }
268+ </ Fragment >
255269 ) )
256270 ) }
257271 { alerts . length > 0 && (
0 commit comments