@@ -345,13 +345,91 @@ window.ProbeRender = (function () {
345345 } ) ;
346346 }
347347
348+ // ── Category filter ──────────────────────────────────────────
349+ function filterByCategory ( data , categories ) {
350+ return {
351+ commit : data . commit ,
352+ servers : data . servers . map ( function ( sv ) {
353+ var filtered = sv . results . filter ( function ( r ) {
354+ return categories . indexOf ( r . category ) !== - 1 ;
355+ } ) ;
356+ var scored = filtered . filter ( function ( r ) { return r . scored !== false ; } ) ;
357+ return {
358+ name : sv . name ,
359+ language : sv . language ,
360+ results : filtered ,
361+ summary : {
362+ total : filtered . length ,
363+ scored : scored . length ,
364+ passed : scored . filter ( function ( r ) { return r . verdict === 'Pass' ; } ) . length ,
365+ failed : scored . filter ( function ( r ) { return r . verdict === 'Fail' ; } ) . length ,
366+ warnings : filtered . filter ( function ( r ) { return r . verdict === 'Warn' ; } ) . length ,
367+ errors : filtered . filter ( function ( r ) { return r . verdict === 'Error' ; } ) . length
368+ }
369+ } ;
370+ } )
371+ } ;
372+ }
373+
374+ function renderCategoryFilter ( targetId , onChange ) {
375+ var el = document . getElementById ( targetId ) ;
376+ if ( ! el ) return ;
377+
378+ var isDark = document . documentElement . classList . contains ( 'dark' ) ;
379+ var baseBg = isDark ? '#21262d' : '#f6f8fa' ;
380+ var baseFg = isDark ? '#c9d1d9' : '#24292f' ;
381+ var baseBorder = isDark ? '#30363d' : '#d0d7de' ;
382+ var activeBg = isDark ? '#1f6feb' : '#0969da' ;
383+
384+ var btnStyle = 'display:inline-block;padding:4px 12px;font-size:12px;font-weight:600;'
385+ + 'border-radius:20px;cursor:pointer;border:1px solid ' + baseBorder + ';'
386+ + 'margin-right:6px;margin-bottom:6px;transition:all 0.15s;' ;
387+
388+ var filters = [
389+ { label : 'All' , categories : null } ,
390+ { label : 'Compliance' , categories : [ 'Compliance' , 'Malformed Input' ] } ,
391+ { label : 'Smuggling' , categories : [ 'Smuggling' ] }
392+ ] ;
393+
394+ var html = '<div style="margin-bottom:12px;">' ;
395+ filters . forEach ( function ( f , i ) {
396+ var isActive = i === 0 ;
397+ html += '<button class="probe-cat-btn" data-idx="' + i + '" style="' + btnStyle
398+ + 'background:' + ( isActive ? activeBg : baseBg ) + ';color:' + ( isActive ? '#fff' : baseFg )
399+ + ';border-color:' + ( isActive ? activeBg : baseBorder ) + ';">' + f . label + '</button>' ;
400+ } ) ;
401+ html += '</div>' ;
402+ el . innerHTML = html ;
403+
404+ var buttons = el . querySelectorAll ( '.probe-cat-btn' ) ;
405+ buttons . forEach ( function ( btn ) {
406+ btn . addEventListener ( 'click' , function ( ) {
407+ var idx = parseInt ( btn . getAttribute ( 'data-idx' ) ) ;
408+ buttons . forEach ( function ( b ) {
409+ if ( b === btn ) {
410+ b . style . background = activeBg ;
411+ b . style . color = '#fff' ;
412+ b . style . borderColor = activeBg ;
413+ } else {
414+ b . style . background = baseBg ;
415+ b . style . color = baseFg ;
416+ b . style . borderColor = baseBorder ;
417+ }
418+ } ) ;
419+ onChange ( filters [ idx ] . categories ) ;
420+ } ) ;
421+ } ) ;
422+ }
423+
348424 return {
349425 pill : pill ,
350426 verdictBg : verdictBg ,
351427 buildLookups : buildLookups ,
352428 renderSummary : renderSummary ,
353429 renderTable : renderTable ,
354430 renderLanguageFilter : renderLanguageFilter ,
431+ filterByCategory : filterByCategory ,
432+ renderCategoryFilter : renderCategoryFilter ,
355433 EXPECT_BG : EXPECT_BG
356434 } ;
357435} ) ( ) ;
0 commit comments