175175 </template >
176176 </v-data-table >
177177
178- <v-treeview
179- v-else
180- :items =" formattedDirectoriesForTreeViewFileCounts"
181- activatable
182- item-key =" fullPath"
183- open-on-click
184- @update:active =" onTreeFileClick"
185- >
186- <template #prepend =" { item , open } " >
187- <v-icon v-if =" item.children.length > 0" >
188- {{ open ? 'mdi-folder-open' : 'mdi-folder' }}
189- </v-icon >
190- <v-icon v-else >
191- mdi-file
192- </v-icon >
193-
194- <v-chip class =" right ml-2" >
195- {{ item.findings }}
196- </v-chip >
197- </template >
198- </v-treeview >
178+ <splitpanes v-else class =" default-theme tree-split" >
179+ <pane size =" 30" :style =" { 'min-width': '250px' }" >
180+ <v-treeview
181+ :items =" treeItems"
182+ item-key =" fullPath"
183+ open-on-click
184+ activatable
185+ :return-object =" true"
186+ dense
187+ @update:active =" onTreeFileClick"
188+ >
189+ <template #prepend =" { item , open } " >
190+ <v-icon v-if =" item.isDirectory" >
191+ {{ open ? 'mdi-folder-open' : 'mdi-folder' }}
192+ </v-icon >
193+ <v-icon v-else >
194+ mdi-file
195+ </v-icon >
196+
197+ <v-chip class =" right ml-2" small >
198+ {{ item.findings }}
199+ </v-chip >
200+ </template >
201+ </v-treeview >
202+ </pane >
203+ <pane >
204+ <v-data-table
205+ v-if =" treeSelectedFile"
206+ :headers =" treeTableHeaders"
207+ :items =" formattedTreeReports"
208+ :loading =" treeReportsLoading"
209+ loading-text =" Loading reports..."
210+ :must-sort =" false"
211+ dense
212+ :mobile-breakpoint =" 1100"
213+ item-key =" $id"
214+ >
215+ <template #item .checkedFile =" { item } " >
216+ <router-link
217+ :to =" { name: 'report-detail', query: {
218+ ...$router.currentRoute.query,
219+ 'report-id': item.reportId ? item.reportId : undefined,
220+ 'report-hash': item.bugHash,
221+ 'report-filepath': item.checkedFile
222+ }}"
223+ class =" file-name"
224+ >
225+ Line  ; {{ item.line }}
226+ </router-link >
227+ </template >
228+
229+ <template #item .checkerMsg =" { item } " >
230+ <span class =" checker-message" >
231+ {{ item.checkerMsg }}
232+ </span >
233+ </template >
234+
235+ <template #item .checkerId =" { item } " >
236+ <span
237+ class =" checker-name primary--text"
238+ @click =" openCheckerDocDialog(
239+ item.checkerId, item.analyzerName)"
240+ >
241+ {{ item.checkerId }}
242+ </span >
243+ </template >
244+
245+ <template #item .severity =" { item } " >
246+ <severity-icon :status =" item.severity" />
247+ </template >
248+
249+ <template #item .bugPathLength =" { item } " >
250+ <v-chip :color =" getBugPathLenColor(item.bugPathLength)" >
251+ {{ item.bugPathLength }}
252+ </v-chip >
253+ </template >
254+
255+ <template #item .reviewData =" { item } " >
256+ <review-status-icon
257+ :status =" parseInt(item.reviewData.status)"
258+ />
259+ </template >
260+
261+ <template #item .detectionStatus =" { item } " >
262+ <detection-status-icon
263+ :status =" parseInt(item.detectionStatus)"
264+ :title =" item.$detectionStatusTitle"
265+ />
266+ </template >
267+ </v-data-table >
268+
269+ <v-container
270+ v-else
271+ class =" text-center grey--text"
272+ fill-height
273+ >
274+ <v-row align =" center" justify =" center" >
275+ <v-col >
276+ <v-icon large class =" mb-2" >
277+ mdi-cursor-default-click
278+ </v-icon >
279+ <div >Select a file from the tree to view its reports</div >
280+ </v-col >
281+ </v-row >
282+ </v-container >
283+ </pane >
284+ </splitpanes >
199285 </pane >
200286 </splitpanes >
201287</template >
@@ -206,7 +292,14 @@ import { Pane, Splitpanes } from "splitpanes";
206292import { mapGetters , mapMutations } from " vuex" ;
207293
208294import { ccService , handleThriftError } from " @cc-api" ;
209- import { Checker , Order , SortMode , SortType } from " @cc/report-server-types" ;
295+ import {
296+ Checker ,
297+ MAX_QUERY_SIZE ,
298+ Order ,
299+ SortMode ,
300+ SortType ,
301+ ReportFilter as ThriftReportFilter
302+ } from " @cc/report-server-types" ;
210303import { SET_REPORT_FILTER } from " @/store/mutations.type" ;
211304
212305import { FillHeight } from " @/directives" ;
@@ -349,7 +442,11 @@ export default {
349442 initalized: false ,
350443 checkerDocDialog: false ,
351444 selectedChecker: null ,
352- expanded: []
445+ expanded: [],
446+ treeItems: [],
447+ treeSelectedFile: null ,
448+ treeReports: [],
449+ treeReportsLoading: false
353450 };
354451 },
355452
@@ -421,9 +518,121 @@ export default {
421518 });
422519 },
423520
424- formattedDirectoriesForTreeViewFileCounts () {
521+ treeTableHeaders () {
522+ return [
523+ {
524+ text: " Line" ,
525+ value: " checkedFile" ,
526+ sortable: false
527+ },
528+ {
529+ text: " Message" ,
530+ value: " checkerMsg" ,
531+ sortable: false
532+ },
533+ {
534+ text: " Checker name" ,
535+ value: " checkerId" ,
536+ sortable: false
537+ },
538+ {
539+ text: " Analyzer" ,
540+ value: " analyzerName" ,
541+ align: " center" ,
542+ sortable: false
543+ },
544+ {
545+ text: " Severity" ,
546+ value: " severity" ,
547+ sortable: false
548+ },
549+ {
550+ text: " Bug path length" ,
551+ value: " bugPathLength" ,
552+ align: " center" ,
553+ sortable: false
554+ },
555+ {
556+ text: " Latest review status" ,
557+ value: " reviewData" ,
558+ align: " center" ,
559+ sortable: false
560+ },
561+ {
562+ text: " Latest detection status" ,
563+ value: " detectionStatus" ,
564+ align: " center" ,
565+ sortable: false
566+ }
567+ ];
568+ },
569+
570+ formattedTreeReports () {
571+ return this .treeReports .map ((report , idx ) => {
572+ const detectionStatus =
573+ this .detectionStatusFromCodeToString (report .detectionStatus );
574+ const detectedAt = report .detectedAt
575+ ? this .$options .filters .prettifyDate (report .detectedAt ) : null ;
576+ const fixedAt = report .fixedAt
577+ ? this .$options .filters .prettifyDate (report .fixedAt ) : null ;
578+
579+ const detectionStatusTitle = [
580+ ` Status: ${ detectionStatus} ` ,
581+ ... (detectedAt ? [ ` Detected at: ${ detectedAt} ` ] : []),
582+ ... (fixedAt ? [ ` Fixed at: ${ fixedAt} ` ] : [])
583+ ].join (" \n " );
584+
585+ const reportId = report .reportId
586+ ? report .reportId .toString () : String (idx);
587+
588+ return {
589+ ... report,
590+ " $detectionStatusTitle" : detectionStatusTitle,
591+ " $id" : reportId + report .bugHash
592+ };
593+ });
594+ },
595+
596+ },
597+
598+ watch: {
599+ pagination: {
600+ handler () {
601+ this .updateUrl ();
602+ if (this .initalized ) {
603+ this .fetchReports ();
604+ }
605+ },
606+ deep: true
607+ },
608+ formattedReports: {
609+ handler () {
610+ this .hasTimeStamp =
611+ this .formattedReports .some (report => report .timestamp );
612+
613+ this .hasTestCase =
614+ this .formattedReports .some (report => report .testcase );
615+
616+ this .hasChronologicalOrder =
617+ this .formattedReports .some (report => report[" chronological_order" ]);
618+ }
619+ },
620+ allReportsFileCounts: {
621+ handler () {
622+ this .buildTreeItems ();
623+ },
624+ deep: true
625+ }
626+ },
627+
628+ methods: {
629+ ... mapMutations (namespace, {
630+ setReportFilter: SET_REPORT_FILTER
631+ }),
632+
633+ buildTreeItems () {
425634 const items = [];
426-
635+
427636 Object .entries (
428637 this .allReportsFileCounts || {}
429638 ).forEach (([ filePath , count ]) => {
@@ -443,7 +652,8 @@ export default {
443652 name: part,
444653 fullPath: currentPath,
445654 children: [],
446- findings: 0
655+ findings: 0 ,
656+ isDirectory: true
447657 };
448658 currentLevel .push (existingPart);
449659 }
@@ -482,61 +692,33 @@ export default {
482692 }
483693 }
484694 items .forEach (countFindings);
485- return items;
486- },
487-
488-
489- },
490-
491- watch: {
492- pagination: {
493- handler () {
494- this .updateUrl ();
495- if (this .initalized ) {
496- this .fetchReports ();
497- }
498- },
499- deep: true
695+ this .treeItems = items;
500696 },
501- formattedReports: {
502- handler () {
503- this .hasTimeStamp =
504- this .formattedReports .some (report => report .timestamp );
505-
506- this .hasTestCase =
507- this .formattedReports .some (report => report .testcase );
508697
509- this .hasChronologicalOrder =
510- this .formattedReports .some (report => report[" chronological_order" ]);
698+ onTreeFileClick (activeItems ) {
699+ if (! activeItems || activeItems .length === 0 ) {
700+ this .treeSelectedFile = null ;
701+ this .treeReports = [];
702+ return ;
511703 }
512- }
513- },
514704
515- methods: {
516- ... mapMutations (namespace, {
517- setReportFilter: SET_REPORT_FILTER
518- }),
705+ const item = activeItems[0 ];
706+ if (! item || item .isDirectory ) return ;
519707
520- onTreeFileClick (activeItems ) {
521- // activeItems is an array of item-key values (fullPath)
522- if (! activeItems || activeItems .length === 0 ) return ;
708+ this .treeSelectedFile = item .fullPath ;
709+ this .treeReportsLoading = true ;
523710
524- const filePath = activeItems[ 0 ] ;
525- if ( ! filePath) return ;
711+ const filter = new ThriftReportFilter ( this . reportFilter ) ;
712+ filter . filepath = [ item . fullPath ] ;
526713
527- // Find the FilePathFilter instance inside ReportFilter
528- // and call its setSelectedItems to select this file.
529- const filters = this .$refs .reportFilter .$refs .filters ;
530- const filePathFilter = filters .find (
531- f => f .id === " filepath"
714+ ccService .getClient ().getRunResults (
715+ this .runIds , MAX_QUERY_SIZE , 0 , [],
716+ filter, this .cmpData , false ,
717+ handleThriftError (reports => {
718+ this .treeReports = reports;
719+ this .treeReportsLoading = false ;
720+ })
532721 );
533- if (filePathFilter) {
534- filePathFilter .setSelectedItems ([
535- { id: filePath, title: filePath, count: " N/A" }
536- ]);
537- }
538-
539- this .viewMode = " table" ;
540722 },
541723
542724 itemExpanded (expandedItem ) {
@@ -547,6 +729,7 @@ export default {
547729 expandedItem .item .sameReports = sameReports;
548730 });
549731 },
732+
550733 loadFileCounts () {
551734 ccService .getClient ().getFileCounts (
552735 this .runIds , this .reportFilter ,
@@ -698,4 +881,10 @@ export default {
698881 cursor : pointer ;
699882 }
700883}
884+
885+ .tree-split {
886+ .splitpanes__pane {
887+ background-color : inherit ;
888+ }
889+ }
701890 </style >
0 commit comments