Skip to content

Commit

Permalink
[GUI] Update report sidebar structure and add review status reactivity
Browse files Browse the repository at this point in the history
This PR gives a new report sidebar structure on the report-detail page. There are two main types in the report tree. One of them is the Outstanding category that contains outstanding reports classified into severity groups. The another category is the Closed that contains resolved, and not resolved but closed reports such as false positive, intentional.

Opening the a report-detail page, the report sidebar automatically opens a specific tree where the current report is located. If we changed the review status, the sidebar would reacts immediately.
  • Loading branch information
cservakt committed Dec 6, 2023
1 parent 262a78f commit ca914ca
Show file tree
Hide file tree
Showing 8 changed files with 313 additions and 119 deletions.
18 changes: 11 additions & 7 deletions web/server/vue-cli/e2e/pages/reportDetail.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,20 @@ const bugTreeCommands = {
return `${this.elements.node.selector}:nth-child(${index})`;
},

getTreeNodeSelector(severityIndex, bugIndex, stepIndex) {
let selectors = [ this.selector, ">", this.node(severityIndex) ];

if (bugIndex !== undefined) {
getTreeNodeSelector(outstandingStateIndex, severityIndex, bugIndex, stepIndex) {
let selectors = [ this.selector, ">", this.node(outstandingStateIndex) ];
if (severityIndex !== undefined) {
selectors.push(
...[ ">", this.elements.childNode.selector, this.node(bugIndex)]);
...[ ">", this.elements.childNode.selector, this.node(severityIndex)]);

if (stepIndex !== undefined) {
if (bugIndex !== undefined) {
selectors.push(
...[">", this.elements.childNode.selector, this.node(stepIndex)]);
...[ ">", this.elements.childNode.selector, this.node(bugIndex)]);

if (stepIndex !== undefined) {
selectors.push(
...[">", this.elements.childNode.selector, this.node(stepIndex)]);
}
}
}

Expand Down
40 changes: 36 additions & 4 deletions web/server/vue-cli/e2e/specs/reportDetail.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,14 +232,46 @@ module.exports = {
const reportDetailPage = browser.page.reportDetail();
const bugTree = reportDetailPage.section.bugTree;

const oldOutstandingStateIndex = 2;
const outstandingStateIndex = 1;
const oldSeverityIndex = 1;
const oldBugIndex = 1;
const severityIndex = 2;
const bugIndex = 1;
const stepIndex = 1;

bugTree.click(bugTree.getTreeNodeSelector(severityIndex));
bugTree.click(bugTree.getTreeNodeSelector(severityIndex, bugIndex));
bugTree.click(
bugTree.getTreeNodeSelector(severityIndex, bugIndex, stepIndex));
bugTree.click(bugTree.getTreeNodeSelector(
oldOutstandingStateIndex,
oldSeverityIndex,
oldBugIndex
));

bugTree.click(bugTree.getTreeNodeSelector(
oldOutstandingStateIndex,
oldSeverityIndex
));

bugTree.click(bugTree.getTreeNodeSelector(oldOutstandingStateIndex));

bugTree.click(bugTree.getTreeNodeSelector(outstandingStateIndex));

bugTree.click(bugTree.getTreeNodeSelector(
outstandingStateIndex,
severityIndex
));

bugTree.click(bugTree.getTreeNodeSelector(
outstandingStateIndex,
severityIndex,
bugIndex
));

bugTree.click(bugTree.getTreeNodeSelector(
outstandingStateIndex,
severityIndex,
bugIndex,
stepIndex
));

reportDetailPage.waitForProgressBarNotPresent();
}
Expand Down
8 changes: 8 additions & 0 deletions web/server/vue-cli/src/components/Report/Report.vue
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,9 @@ export default {
props: {
treeItem: { type: Object, default: null }
},
emits: [ "update-review-data" ],
data() {
const enableBlameView =
this.$router.currentRoute.query["view"] === "blame";
Expand Down Expand Up @@ -886,6 +889,11 @@ export default {
this.reviewData.status = status;
this.reviewData.author = author;
this.reviewData.date = format(new Date(), "yyyy-MM-dd HH:mm:ss");
this.$emit(
"update-review-data",
this.reviewData,
this.report.reportId
);
}));
},
Expand Down
163 changes: 104 additions & 59 deletions web/server/vue-cli/src/components/Report/ReportTree/ReportTree.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ import { ccService, handleThriftError } from "@cc-api";
import {
DetectionStatus,
MAX_QUERY_SIZE,
ReportFilter
ReportFilter,
ReviewStatus
} from "@cc/report-server-types";
import { ReviewStatusIcon } from "@/components/Icons";
Expand All @@ -64,7 +65,8 @@ export default {
},
props: {
report: { type: Object, default: null }
report: { type: Object, default: null },
reviewStatus: { type: Number, default: null }
},
data() {
Expand All @@ -82,7 +84,19 @@ export default {
watch: {
report() {
this.fetchReports();
// If the runId and the checkedFile are not changed, we should not load
// the reports.
if (this.isTheReportFileChanged()) {
this.fetchReports();
}
},
reviewStatus() {
// If the runId and the checkedFile are not changed, but the review
// status is, we should load the reports again.
if (!this.isTheReportFileChanged()) {
this.fetchReports();
}
}
},
Expand All @@ -95,23 +109,23 @@ export default {
methods: {
// Remove root elements which do not have any children.
removeEmptyRootElements() {
let i = this.items.length;
while (i--) {
if (!this.items[i].children.length) {
this.items.splice(i, 1);
if (this.items && this.items.length) {
let i = this.items.length;
while (i--) {
let j = this.items[i].children.length;
while (j--) {
if (!this.items[i].children[j].children.length) {
this.items[i].children.splice(j, 1);
}
}
if (!this.items[i].children.length){
this.items.splice(i, 1);
}
}
}
},
fetchReports() {
// If the runId and the checkedFile are not changed, we should not load
// the reports.
if (this.runId && this.runId.equals(this.report.runId) &&
this.fileId && this.fileId.equals(this.report.fileId)
) {
return;
}
this.runId = this.report.runId;
this.fileId = this.report.fileId;
Expand Down Expand Up @@ -147,38 +161,48 @@ export default {
const isResolved =
report.detectionStatus === DetectionStatus.RESOLVED;
const parent = this.items.find(item => {
return isResolved
const status = !(
isResolved ||
report.reviewData.status === ReviewStatus.FALSE_POSITIVE ||
report.reviewData.status === ReviewStatus.INTENTIONAL
) ? this.items.find(item => item.isOutstanding)
: this.items.find(item => !item.isOutstanding);
const parent = status.children.find(item => {
return isResolved
? item.detectionStatus === DetectionStatus.RESOLVED
: item.severity === report.severity;
: item.severity === report.severity
;
});
parent.children.push({
id: ReportTreeKind.getId(ReportTreeKind.REPORT, report),
name: report.checkerId,
kind: ReportTreeKind.REPORT,
report: report,
children: [],
itemChildren: [],
isLoading: false,
getChildren: item => {
return new Promise(resolve => {
ccService.getClient().getReportDetails(report.reportId,
handleThriftError(details => {
item.children = formatReportDetails(report, details);
resolve();
if (this.report.reportId.equals(item.report.reportId)) {
const bugItem = item.children.find(c =>
c.id === `${report.reportId}_${ReportTreeKind.BUG}`
);
this.activeItems.push(bugItem);
}
}));
});
}
});
if (parent){
parent.children.push({
id: ReportTreeKind.getId(ReportTreeKind.REPORT, report),
name: report.checkerId,
kind: ReportTreeKind.REPORT,
report: report,
children: [],
itemChildren: [],
isLoading: false,
getChildren: item => {
return new Promise(resolve => {
ccService.getClient().getReportDetails(report.reportId,
handleThriftError(details => {
item.children = formatReportDetails(report, details);
resolve();
if (this.report.reportId.equals(item.report.reportId)) {
const bugItem = item.children.find(c =>
c.id === `${report.reportId}_${ReportTreeKind.BUG}`
);
this.activeItems.push(bugItem);
}
}));
});
}
});
}
});
this.openReportItems();
Expand All @@ -205,31 +229,52 @@ export default {
const isResolved =
this.report.detectionStatus === DetectionStatus.RESOLVED;
const rootNode = this.items.find(item => {
return isResolved
const status = !(
isResolved ||
this.report.reviewData.status === ReviewStatus.FALSE_POSITIVE ||
this.report.reviewData.status === ReviewStatus.INTENTIONAL
) ? this.items.find(item => item.isOutstanding)
: this.items.find(item => !item.isOutstanding);
this.openedItems.push(status);
const rootNode = status.children.find(item => {
return isResolved
? item.detectionStatus === DetectionStatus.RESOLVED
: item.severity === this.report.severity;
});
this.openedItems.push(rootNode);
this.$nextTick(() => {
const reportNode = rootNode.children.find(item => {
return item.id === this.report.reportId.toString();
});
if (rootNode) {
this.openedItems.push(rootNode);
this.$nextTick(() => {
const reportNode = rootNode.children.find(item => {
return item.id === this.report.reportId.toString();
});
if (reportNode) {
const node = this.$el.querySelector(`[data-id='${reportNode.id}']`);
if (node) {
node.scrollIntoView();
if (reportNode) {
const node = this.$el.querySelector(`[data-id='${reportNode.id}']`);
if (node) {
node.scrollIntoView();
}
this.openedItems.push(reportNode);
}
}
this.openedItems.push(reportNode);
});
});
}
},
onClick(activeItems) {
this.$emit("click", activeItems[0]);
},
isTheReportFileChanged() {
if (this.runId && this.runId.equals(this.report.runId) &&
this.fileId && this.fileId.equals(this.report.fileId)
) {
return false;
}
else {
return true;
}
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,24 @@
mdi-note-outline
</v-icon>

<v-icon
v-else-if="item.isOutstanding"
title="Outstanding reports (review status is unreviewed or confirmed
and detection status is not resolved)"
color="primary"
>
mdi-folder-lock-open
</v-icon>

<v-icon
v-else-if="!item.isOutstanding"
title="Closed reports (review status is false positive or intentional
or detection status is resolved)"
color="primary"
>
mdi-folder-lock
</v-icon>

<v-icon v-else>
{{ item.open ? "mdi-folder-open" : "mdi-folder" }}
</v-icon>
Expand Down
Loading

0 comments on commit ca914ca

Please sign in to comment.