@@ -63,64 +63,41 @@ export function AttackDistributionChart({
6363 const { totalAttacks, attackLabels, attackCounts, metrics } = useMemo ( ( ) => {
6464 console . log ( 'AttackDistributionChart - Raw data:' , data ) ;
6565
66- // Define all possible attack types to maintain consistent chart structure
67- const allAttackTypes = [
68- "SQL Injection" ,
69- "XSS" ,
70- "Command Injection" ,
71- "Directory Traversal" ,
72- "Brute Force"
73- ] ;
66+ // When there's an active filter, we want to show all attack types but highlight the filtered one
67+ // The parent component has already filtered the data, so we just need to work with what we have
68+ const displayData = { ...data } ;
7469
75- // When there's an active filter, the parent has set non-matching types to 0
76- // We still want to show all categories to maintain the radar chart structure
77- const displayData : Record < string , number > = { } ;
70+ const total = Object . values ( displayData ) . reduce ( ( sum , value ) => sum + ( Number ( value ) || 0 ) , 0 ) ;
71+ console . log ( 'AttackDistributionChart - Total attacks:' , total ) ;
7872
79- // Initialize all attack types with their values (or 0 if not present)
80- allAttackTypes . forEach ( type => {
81- displayData [ type ] = data [ type ] || 0 ;
82- } ) ;
83-
84- // Calculate total based on the original data, not the display data with zeros
85- const actualTotal = Object . values ( data ) . reduce ( ( sum , value ) => sum + ( Number ( value ) || 0 ) , 0 ) ;
86- console . log ( 'AttackDistributionChart - Total attacks:' , actualTotal ) ;
87-
88- if ( ! data || Object . keys ( data ) . length === 0 ) {
73+ if ( ! data || Object . keys ( displayData ) . length === 0 ) {
8974 console . log ( 'AttackDistributionChart - No data or zero total attacks' ) ;
9075 return { totalAttacks : 0 , attackLabels : [ ] , attackCounts : [ ] , metrics : [ ] } ;
9176 }
9277
93- // Create arrays maintaining the order of allAttackTypes
94- // but put the active filter first if it exists
95- const labels : string [ ] = [ ] ;
96- const counts : number [ ] = [ ] ;
97-
98- // Add active filter first if it exists
99- if ( activeFilter && allAttackTypes . includes ( activeFilter ) ) {
100- labels . push ( activeFilter ) ;
101- counts . push ( displayData [ activeFilter ] ) ;
102- }
103-
104- // Add remaining attack types
105- allAttackTypes . forEach ( type => {
106- if ( type !== activeFilter ) {
107- labels . push ( type ) ;
108- counts . push ( displayData [ type ] ) ;
78+ // Sort all data but put the active filter first if it exists
79+ const sorted = Object . entries ( displayData ) . sort ( ( [ aKey , aVal ] , [ bKey , bVal ] ) => {
80+ // If we have an active filter, put it first
81+ if ( activeFilter ) {
82+ if ( aKey === activeFilter ) return - 1 ;
83+ if ( bKey === activeFilter ) return 1 ;
10984 }
85+ // Otherwise sort by count descending
86+ return bVal - aVal ;
11087 } ) ;
11188
112- // Find top threat (excluding zero values)
113- const nonZeroEntries = Object . entries ( data ) . filter ( ( [ , value ] ) => value > 0 ) ;
114- const sortedNonZero = nonZeroEntries . sort ( ( [ , a ] , [ , b ] ) => b - a ) ;
115- const [ topKey , topValue ] = sortedNonZero [ 0 ] || [ ] ;
116- const topPct = topValue ? ( topValue / actualTotal ) * 100 : 0 ;
89+ const labels = sorted . map ( ( [ key ] ) => key ) ;
90+ const counts = sorted . map ( ( [ , value ] ) => value ) ;
91+
92+ const [ topKey , topValue ] = sorted [ 0 ] || [ ] ;
93+ const topPct = topValue ? ( topValue / total ) * 100 : 0 ;
11794
11895 return {
119- totalAttacks : actualTotal ,
96+ totalAttacks : total ,
12097 attackLabels : labels ,
12198 attackCounts : counts ,
12299 metrics : [
123- { title : "Total Attacks" , value : actualTotal . toLocaleString ( ) , icon : ShieldCheck , color : "red" } ,
100+ { title : "Total Attacks" , value : total . toLocaleString ( ) , icon : ShieldCheck , color : "red" } ,
124101 {
125102 title : "Top Threat" ,
126103 value : topKey || "None" ,
@@ -132,7 +109,7 @@ export function AttackDistributionChart({
132109 } ,
133110 {
134111 title : "Attack Varieties" ,
135- value : nonZeroEntries . length ,
112+ value : labels . length ,
136113 icon : ListTree ,
137114 color : "purple" ,
138115 subtitle : "Unique attack types detected" ,
@@ -152,41 +129,17 @@ export function AttackDistributionChart({
152129 backgroundColor : isDarkMode ? 'rgba(255, 0, 0, 0.2)' : 'rgba(220, 38, 38, 0.15)' ,
153130 borderColor : isDarkMode ? 'rgba(255, 50, 50, 1)' : 'rgba(220, 38, 38, 1)' ,
154131 borderWidth : isDarkMode ? 4 : 3 ,
155- pointBackgroundColor : attackLabels . map ( ( label , index ) => {
156- // Highlight the active filter with a different color
157- if ( activeFilter && label === activeFilter ) {
158- return isDarkMode ? 'rgba(255, 215, 0, 1)' : 'rgba(255, 215, 0, 1)' ; // Gold color for active filter
159- }
160- return isDarkMode ? 'rgba(255, 100, 100, 1)' : 'rgba(220, 38, 38, 1)' ;
161- } ) ,
132+ pointBackgroundColor : isDarkMode ? 'rgba(255, 100, 100, 1)' : 'rgba(220, 38, 38, 1)' ,
162133 pointBorderColor : isDarkMode ? 'rgba(255, 200, 200, 1)' : '#fff' ,
163- pointHoverBackgroundColor : attackLabels . map ( ( label , index ) => {
164- // Highlight the active filter with a different hover color
165- if ( activeFilter && label === activeFilter ) {
166- return isDarkMode ? 'rgba(255, 215, 0, 1)' : 'rgba(255, 215, 0, 1)' ; // Gold color for active filter
167- }
168- return isDarkMode ? 'rgba(255, 0, 0, 1)' : '#fff' ;
169- } ) ,
134+ pointHoverBackgroundColor : isDarkMode ? 'rgba(255, 0, 0, 1)' : '#fff' ,
170135 pointHoverBorderColor : isDarkMode ? 'rgba(255, 255, 255, 1)' : 'rgba(220, 38, 38, 1)' ,
171- pointRadius : attackLabels . map ( ( label , index ) => {
172- // Make the active filter point larger
173- if ( activeFilter && label === activeFilter ) {
174- return isDarkMode ? 9 : 8 ;
175- }
176- return isDarkMode ? 7 : 6 ;
177- } ) ,
178- pointHoverRadius : attackLabels . map ( ( label , index ) => {
179- // Make the active filter point larger on hover
180- if ( activeFilter && label === activeFilter ) {
181- return isDarkMode ? 11 : 10 ;
182- }
183- return isDarkMode ? 9 : 8 ;
184- } ) ,
136+ pointRadius : isDarkMode ? 7 : 6 ,
137+ pointHoverRadius : isDarkMode ? 9 : 8 ,
185138 // Add glow effect through shadow
186139 pointStyle : 'circle' ,
187140 } ,
188141 ] ,
189- } ) , [ attackLabels , attackCounts , isDarkMode , activeFilter ] ) ;
142+ } ) , [ attackLabels , attackCounts , isDarkMode ] ) ;
190143
191144 const options : ChartOptions < "radar" > = {
192145 responsive : true ,
@@ -218,13 +171,7 @@ export function AttackDistributionChart({
218171 cornerRadius : 8 ,
219172 displayColors : false ,
220173 callbacks : {
221- title : ( ctx ) => {
222- const label = ctx [ 0 ] . label as string ;
223- if ( activeFilter && label === activeFilter ) {
224- return `Attack Type: ${ label } (FILTERED)` ;
225- }
226- return `Attack Type: ${ label } ` ;
227- } ,
174+ title : ( ctx ) => `Attack Type: ${ ctx [ 0 ] . label } ` ,
228175 label : ( ctx ) => {
229176 const count = ctx . raw as number ;
230177 const pct = totalAttacks > 0 ? ( ( count / totalAttacks ) * 100 ) . toFixed ( 1 ) : 0 ;
@@ -258,13 +205,9 @@ export function AttackDistributionChart({
258205 } ,
259206 } ;
260207
261- // Check if we have any non-zero values in the original data
262208 const hasNoData = Object . values ( data ) . every ( val => val === 0 || val === undefined ) ;
263209
264- // But if we have an active filter that exists in the data, we should show the chart
265- const shouldShowChart = ! hasNoData || ( activeFilter && data [ activeFilter ] > 0 ) ;
266-
267- if ( ! shouldShowChart ) {
210+ if ( hasNoData ) {
268211 return (
269212 < div className = { className } >
270213 < div className = "p-6 pb-4 border-b border-gray-200/50 dark:border-gray-700/50" >
@@ -307,9 +250,7 @@ export function AttackDistributionChart({
307250 < span > Attack Distribution</ span >
308251 </ h2 >
309252 < p className = "text-gray-600 dark:text-gray-400 mt-1" >
310- { activeFilter
311- ? `Distribution of ${ totalAttacks . toLocaleString ( ) } threats. Showing filtered view for "${ activeFilter } ".`
312- : `Distribution of ${ totalAttacks . toLocaleString ( ) } threats. Click a point to filter.` }
253+ Distribution of { totalAttacks . toLocaleString ( ) } threats. Click a point to filter.
313254 </ p >
314255 </ div >
315256
0 commit comments