Skip to content

Commit 05dfa37

Browse files
Frontend - Toggle investigations (#2821)
* draft investigation icons * added badges + draft reliability bar * changes * prettier * fix * changes
1 parent ec18f05 commit 05dfa37

File tree

22 files changed

+659
-252
lines changed

22 files changed

+659
-252
lines changed

api_app/serializers/job.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,20 @@ class JobTreeSerializer(ModelSerializer):
503503
evaluation = rfs.CharField(
504504
source="data_model.evaluation", allow_null=True, read_only=True
505505
)
506+
reliability = rfs.IntegerField(
507+
source="data_model.reliability", allow_null=True, read_only=True
508+
)
509+
tags = rfs.ListField(
510+
source="data_model.tags",
511+
allow_null=True,
512+
child=rfs.CharField(read_only=True),
513+
read_only=True,
514+
default=[],
515+
)
516+
isp = rfs.CharField(source="data_model.isp", allow_null=True, read_only=True)
517+
country = rfs.CharField(
518+
source="data_model.country_code", allow_null=True, read_only=True
519+
)
506520
is_sample = rfs.BooleanField(read_only=True)
507521

508522
playbook = rfs.SlugRelatedField(
@@ -513,6 +527,9 @@ class JobTreeSerializer(ModelSerializer):
513527
required=False,
514528
)
515529
analyzed_object_name = rfs.CharField(source="analyzable.name", read_only=True)
530+
mimetype = rfs.CharField(
531+
source="analyzable.mimetype", allow_null=True, read_only=True
532+
)
516533

517534
class Meta:
518535
model = Job
@@ -525,6 +542,11 @@ class Meta:
525542
"received_request_time",
526543
"is_sample",
527544
"evaluation",
545+
"reliability",
546+
"tags",
547+
"mimetype",
548+
"isp",
549+
"country",
528550
]
529551

530552
def to_representation(self, instance):

frontend/package-lock.json

Lines changed: 21 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"react-ace": "^13.0.0",
2323
"react-dom": "^17.0.2",
2424
"react-error-boundary": "^5.0.0",
25-
"react-icons": "^4.12.0",
25+
"react-icons": "^5.5.0",
2626
"react-joyride": "^2.9.2",
2727
"react-markdown": "^8.0.7",
2828
"react-router-dom": "^6.27.0",
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
import React from "react";
2+
import PropTypes from "prop-types";
3+
import { Badge, UncontrolledTooltip } from "reactstrap";
4+
import { FaTag } from "react-icons/fa";
5+
import { VscFile } from "react-icons/vsc";
6+
import { TbWorld } from "react-icons/tb";
7+
import classnames from "classnames";
8+
import { EvaluationColors, TagsColors } from "../../constants/colorConst";
9+
import { EvaluationIcons, TagsIcons } from "../../constants/engineConst";
10+
import { getIcon } from "./icon/icons";
11+
12+
export function EvaluationBadge(props) {
13+
const { id, evaluation, className } = props;
14+
15+
const color = EvaluationColors?.[evaluation];
16+
const divClass = classnames(`bg-${color}`, className);
17+
const icon = EvaluationIcons?.[evaluation];
18+
19+
return (
20+
<Badge
21+
id={`evaluation__job${id}_${evaluation}`}
22+
className={`d-flex-center ${divClass}`}
23+
>
24+
{getIcon(icon)}
25+
<UncontrolledTooltip
26+
target={`evaluation__job${id}_${evaluation}`}
27+
placement="top"
28+
fade={false}
29+
>
30+
{evaluation.toUpperCase()}
31+
</UncontrolledTooltip>
32+
</Badge>
33+
);
34+
}
35+
36+
EvaluationBadge.propTypes = {
37+
id: PropTypes.string.isRequired,
38+
evaluation: PropTypes.string.isRequired,
39+
className: PropTypes.string,
40+
};
41+
42+
EvaluationBadge.defaultProps = {
43+
className: null,
44+
};
45+
46+
export function ReliabilityBar(props) {
47+
const { id, reliability, evaluation, className } = props;
48+
const color = EvaluationColors?.[evaluation];
49+
50+
return (
51+
<div
52+
id={`reliability-bar__job${id}_rel${reliability}`}
53+
className={`d-flex-center ${className}`}
54+
style={{ width: "300px" }}
55+
>
56+
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((index) => (
57+
<hr
58+
style={{
59+
width: "10%",
60+
borderTop: "3px solid white",
61+
opacity: 1,
62+
}}
63+
className={`mt-1 me-1 ${
64+
index <= reliability ? `border-${color}` : "border-tertiary"
65+
}`}
66+
/>
67+
))}
68+
<UncontrolledTooltip
69+
target={`reliability-bar__job${id}_rel${reliability}`}
70+
placement="top"
71+
fade={false}
72+
>
73+
Reliability: {reliability}
74+
</UncontrolledTooltip>
75+
</div>
76+
);
77+
}
78+
79+
ReliabilityBar.propTypes = {
80+
id: PropTypes.string.isRequired,
81+
reliability: PropTypes.string.isRequired,
82+
evaluation: PropTypes.number.isRequired,
83+
className: PropTypes.string,
84+
};
85+
86+
ReliabilityBar.defaultProps = {
87+
className: null,
88+
};
89+
90+
export function TagsBadge(props) {
91+
const { id, tag, className } = props;
92+
let color = "";
93+
let icon = "";
94+
if (Object.keys(TagsIcons).includes(tag)) {
95+
color = TagsColors?.[tag];
96+
icon = getIcon(TagsIcons?.[tag]);
97+
} else {
98+
color = "secondary";
99+
icon = <FaTag />;
100+
}
101+
102+
const divClass = classnames(`bg-${color}`, className);
103+
104+
return (
105+
<Badge id={`tag__${id}`} className={`d-flex-center ${divClass}`}>
106+
{icon}
107+
<UncontrolledTooltip target={`tag__${id}`} placement="top" fade={false}>
108+
{tag}
109+
</UncontrolledTooltip>
110+
</Badge>
111+
);
112+
}
113+
114+
TagsBadge.propTypes = {
115+
id: PropTypes.string.isRequired,
116+
tag: PropTypes.string.isRequired,
117+
className: PropTypes.string,
118+
};
119+
120+
TagsBadge.defaultProps = {
121+
className: null,
122+
};
123+
124+
export function CountryBadge(props) {
125+
const { id, country, className } = props;
126+
127+
return (
128+
<span
129+
id={`country__${id}_${country.toLowerCase()}`}
130+
className={`${className}`}
131+
>
132+
{getIcon(country)}
133+
<UncontrolledTooltip
134+
placement="top"
135+
target={`country__${id}_${country.toLowerCase()}`}
136+
>
137+
{country.toUpperCase()}
138+
</UncontrolledTooltip>
139+
</span>
140+
);
141+
}
142+
143+
CountryBadge.propTypes = {
144+
id: PropTypes.string.isRequired,
145+
country: PropTypes.string.isRequired,
146+
className: PropTypes.string,
147+
};
148+
149+
CountryBadge.defaultProps = {
150+
className: null,
151+
};
152+
153+
export function MimetypeBadge(props) {
154+
const { id, mimetype, className } = props;
155+
156+
return (
157+
<Badge
158+
id={`mimetype__job${id}`}
159+
className={`d-flex-center bg-secondary ${className}`}
160+
>
161+
<VscFile />
162+
<UncontrolledTooltip
163+
target={`mimetype__job${id}`}
164+
placement="top"
165+
fade={false}
166+
>
167+
{mimetype}
168+
</UncontrolledTooltip>
169+
</Badge>
170+
);
171+
}
172+
173+
MimetypeBadge.propTypes = {
174+
id: PropTypes.string.isRequired,
175+
mimetype: PropTypes.string.isRequired,
176+
className: PropTypes.string,
177+
};
178+
179+
MimetypeBadge.defaultProps = {
180+
className: null,
181+
};
182+
183+
export function IspBadge(props) {
184+
const { id, isp, className } = props;
185+
186+
return (
187+
<Badge
188+
id={`isp__${id}`}
189+
className={`d-flex-center bg-secondary ${className}`}
190+
>
191+
<TbWorld />
192+
<UncontrolledTooltip target={`isp__${id}`} placement="top" fade={false}>
193+
{isp}
194+
</UncontrolledTooltip>
195+
</Badge>
196+
);
197+
}
198+
199+
IspBadge.propTypes = {
200+
id: PropTypes.string.isRequired,
201+
isp: PropTypes.string.isRequired,
202+
className: PropTypes.string,
203+
};
204+
205+
IspBadge.defaultProps = {
206+
className: null,
207+
};
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import React from "react";
2+
import PropTypes from "prop-types";
3+
import { Spinner } from "reactstrap";
4+
import {
5+
MdDeleteOutline,
6+
MdOutlineRefresh,
7+
MdComment,
8+
MdFileDownload,
9+
} from "react-icons/md";
10+
import { FaRegStopCircle } from "react-icons/fa";
11+
12+
// These function are needed in IconButton because it expects Icon as a function
13+
14+
export function DeleteIcon() {
15+
return (
16+
<span className="d-flex align-items-center">
17+
<MdDeleteOutline className="text-danger me-1" />
18+
Delete
19+
</span>
20+
);
21+
}
22+
23+
export function CommentIcon({ commentNumber }) {
24+
return (
25+
<span className="d-flex align-items-center">
26+
<MdComment className="me-1" />
27+
Comments ({commentNumber})
28+
</span>
29+
);
30+
}
31+
32+
CommentIcon.propTypes = {
33+
commentNumber: PropTypes.number.isRequired,
34+
};
35+
36+
export function retryJobIcon() {
37+
return (
38+
<span className="d-flex align-items-center">
39+
<MdOutlineRefresh className="me-1" />
40+
Rescan
41+
</span>
42+
);
43+
}
44+
45+
export function downloadReportIcon() {
46+
return (
47+
<span className="d-flex align-items-center">
48+
<MdFileDownload className="me-1" />
49+
Report
50+
</span>
51+
);
52+
}
53+
54+
export function SpinnerIcon() {
55+
return <Spinner type="border" size="sm" className="text-darker" />;
56+
}
57+
58+
export function killJobIcon() {
59+
return (
60+
<span className="d-flex align-items-center">
61+
<FaRegStopCircle className="me-1" />
62+
Kill job
63+
</span>
64+
);
65+
}

0 commit comments

Comments
 (0)