55 <pane size =" 20" :style =" { 'min-width': '320px' }" >
66 <ReportFilter
77 :report-count =" totalItems "
8+ :refresh-filter =" refreshFilterState "
89 @refresh =" refresh "
10+ @set-refresh-filter-state =" setRefreshFilterState "
911 />
1012 </pane >
1113 <pane >
350352 class =" tree-item-label clickable"
351353 @click.stop =" onTreeItemClick(item)"
352354 >{{ item.name }}</span >
353- <span class =" tree-stat-cell" >{{ item.findings }}</span >
354- <span class =" tree-stat-cell" >{{ item.stats.style || '' }}</span >
355- <span class =" tree-stat-cell" >{{ item.stats.low || '' }}</span >
356- <span class =" tree-stat-cell" >{{ item.stats.medium || '' }}</span >
357- <span class =" tree-stat-cell" >{{ item.stats.high || '' }}</span >
358- <span class =" tree-stat-cell" >
355+ <span
356+ class =" tree-stat-cell"
357+ :class =" { clickable: item.findings }"
358+ @click.stop =" onTreeStatClick(item, 'all')"
359+ >{{ item.findings }}</span >
360+ <span
361+ class =" tree-stat-cell"
362+ :class =" { clickable: item.stats.style }"
363+ @click.stop =" onTreeStatClick(item, 'style')"
364+ >{{ item.stats.style || '' }}</span >
365+ <span
366+ class =" tree-stat-cell"
367+ :class =" { clickable: item.stats.low }"
368+ @click.stop =" onTreeStatClick(item, 'low')"
369+ >{{ item.stats.low || '' }}</span >
370+ <span
371+ class =" tree-stat-cell"
372+ :class =" { clickable: item.stats.medium }"
373+ @click.stop =" onTreeStatClick(item, 'medium')"
374+ >{{ item.stats.medium || '' }}</span >
375+ <span
376+ class =" tree-stat-cell"
377+ :class =" { clickable: item.stats.high }"
378+ @click.stop =" onTreeStatClick(item, 'high')"
379+ >{{ item.stats.high || '' }}</span >
380+ <span
381+ class =" tree-stat-cell"
382+ :class =" { clickable: item.stats.critical }"
383+ @click.stop =" onTreeStatClick(item, 'critical')"
384+ >
359385 {{ item.stats.critical || '' }}
360386 </span >
361- <span class =" tree-stat-cell" >
387+ <span
388+ class =" tree-stat-cell"
389+ :class =" { clickable: item.stats.unreviewed }"
390+ @click.stop =" onTreeStatClick(item, 'unreviewed')"
391+ >
362392 {{ item.stats.unreviewed || '' }}
363393 </span >
364- <span class =" tree-stat-cell" >
394+ <span
395+ class =" tree-stat-cell"
396+ :class =" { clickable: item.stats.confirmed }"
397+ @click.stop =" onTreeStatClick(item, 'confirmed')"
398+ >
365399 {{ item.stats.confirmed || '' }}
366400 </span >
367- <span class =" tree-stat-cell" >
401+ <span
402+ class =" tree-stat-cell"
403+ :class =" { clickable: item.stats.false_positive }"
404+ @click.stop =" onTreeStatClick(item, 'false_positive')"
405+ >
368406 {{ item.stats.false_positive || '' }}
369407 </span >
370- <span class =" tree-stat-cell" >
408+ <span
409+ class =" tree-stat-cell"
410+ :class =" { clickable: item.stats.intentional }"
411+ @click.stop =" onTreeStatClick(item, 'intentional')"
412+ >
371413 {{ item.stats.intentional || '' }}
372414 </span >
373415 </div >
@@ -388,6 +430,7 @@ import { ccService, handleThriftError } from "@cc-api";
388430import {
389431 Checker ,
390432 Order ,
433+ ReviewStatus ,
391434 Severity ,
392435 SortMode ,
393436 SortType
@@ -396,6 +439,8 @@ import { SET_REPORT_FILTER } from "@/store/mutations.type";
396439
397440import { useGradientColor } from " @/composables/useGradientColor" ;
398441import { useDetectionStatus } from " @/composables/useDetectionStatus" ;
442+ import { useSeverity } from " @/composables/useSeverity" ;
443+ import { useReviewStatus } from " @/composables/useReviewStatus" ;
399444import {
400445 DetectionStatusIcon ,
401446 ReviewStatusIcon ,
@@ -412,6 +457,9 @@ const router = useRouter();
412457const store = useStore ();
413458const gradientColor = useGradientColor ();
414459const detectionStatus = useDetectionStatus ();
460+ const severity = useSeverity ();
461+ const reviewStatus = useReviewStatus ();
462+ const refreshFilterState = ref (false );
415463
416464const itemsPerPageOptions = [
417465 { value: 25 , title: " 25" },
@@ -665,13 +713,74 @@ function setReportFilter(params) {
665713 store .commit (SET_REPORT_FILTER , params);
666714}
667715
668- function onTreeItemClick (item ) {
716+ const SEVERITY_STAT_KEYS = {
717+ style: Severity .STYLE ,
718+ low: Severity .LOW ,
719+ medium: Severity .MEDIUM ,
720+ high: Severity .HIGH ,
721+ critical: Severity .CRITICAL
722+ };
723+
724+ const REVIEW_STATUS_STAT_KEYS = {
725+ unreviewed: ReviewStatus .UNREVIEWED ,
726+ confirmed: ReviewStatus .CONFIRMED ,
727+ false_positive: ReviewStatus .FALSE_POSITIVE ,
728+ intentional: ReviewStatus .INTENTIONAL
729+ };
730+
731+ function getTreeItemFilePattern (item ) {
669732 const isDir = item .children && item .children .length > 0 ;
670- const pattern = isDir ? item .fullPath + " /*" : item .fullPath ;
671- setReportFilter ({ filepath: [ pattern ] });
733+ return isDir ? item .fullPath + " /*" : item .fullPath ;
734+ }
735+
736+ function setRefreshFilterState (state ) {
737+ refreshFilterState .value = state;
738+ }
739+
740+ // Push the given filter values into the URL and let ReportFilter's
741+ // initByUrl() (triggered via the refresh-filter prop) apply them to the
742+ // individual filter components and the store, instead of writing to the
743+ // store directly and leaving the URL/filters out of sync.
744+ async function applyTreeFilter (params ) {
745+ const query = { ... route .query };
746+
747+ if (params .filepath ) {
748+ query[" filepath" ] = params .filepath ;
749+ }
750+ if (params .severity ) {
751+ query[" severity" ] =
752+ params .severity .map (s => severity .severityFromCodeToString (s));
753+ }
754+ if (params .reviewStatus ) {
755+ query[" review-status" ] = params .reviewStatus .map (
756+ s => reviewStatus .reviewStatusFromCodeToString (s)
757+ );
758+ }
759+
760+ await router .replace ({ query }).catch (() => {});
761+ setRefreshFilterState (true );
672762 viewMode .value = " table" ;
673763}
674764
765+ function onTreeItemClick (item ) {
766+ applyTreeFilter ({ filepath: [ getTreeItemFilePattern (item) ] });
767+ }
768+
769+ function onTreeStatClick (item , statKey ) {
770+ const count = statKey === " all" ? item .findings : item .stats [statKey];
771+ if (! count) return ;
772+
773+ const params = { filepath: [ getTreeItemFilePattern (item) ] };
774+
775+ if (statKey in SEVERITY_STAT_KEYS ) {
776+ params .severity = [ SEVERITY_STAT_KEYS [statKey] ];
777+ } else if (statKey in REVIEW_STATUS_STAT_KEYS ) {
778+ params .reviewStatus = [ REVIEW_STATUS_STAT_KEYS [statKey] ];
779+ }
780+
781+ applyTreeFilter (params);
782+ }
783+
675784function toggleTreeSort (statKey ) {
676785 if (treeSortKey .value !== statKey) {
677786 treeSortKey .value = statKey;
@@ -1055,6 +1164,15 @@ body {
10551164 text- align: center;
10561165 flex- shrink: 0 ;
10571166 font- size: 0 .8em ;
1167+
1168+ & .clickable {
1169+ cursor: pointer;
1170+
1171+ & : hover {
1172+ text- decoration: underline;
1173+ color: rgb (var (-- v- theme- primary));
1174+ }
1175+ }
10581176}
10591177
10601178.v - data- table {
0 commit comments