Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Frontend - Adjusted buttons #2118

Merged
merged 2 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions frontend/src/components/jobs/result/JobOverview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
Spinner,
} from "reactstrap";

import { GoBackButton, Loader } from "@certego/certego-ui";
import { Loader } from "@certego/certego-ui";
import { JSONTree } from "react-json-tree";

import { useNavigate, useLocation } from "react-router-dom";
Expand Down Expand Up @@ -288,15 +288,17 @@ export function JobOverview({
render={() => (
<Container fluid>
{/* bar with job id and utilities buttons */}
<Row className="g-0 d-flex-between-end" id="utilitiesRow">
<Row
className="g-0 d-flex-between-end align-items-center"
id="utilitiesRow"
>
<Col>
<GoBackButton onlyIcon color="gray" />
<h2>
<span className="me-2 text-secondary">Job #{job.id}</span>
<StatusIcon status={job.status} className="small" />
</h2>
</Col>
<Col className="d-flex justify-content-end">
<Col className="d-flex justify-content-end mt-1">
<JobActionsBar job={job} />
</Col>
</Row>
Expand Down
63 changes: 38 additions & 25 deletions frontend/src/components/jobs/result/bar/JobActionBar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ import { Button } from "reactstrap";
import { useNavigate } from "react-router-dom";
import { FaFileDownload } from "react-icons/fa";

import {
ContentSection,
SocialShareBtn,
IconButton,
addToast,
} from "@certego/certego-ui";
import { ContentSection, IconButton, addToast } from "@certego/certego-ui";

import { SaveAsPlaybookButton } from "./SaveAsPlaybooksForm";

import { downloadJobSample, deleteJob } from "../jobApi";
import { createJob } from "../../../scan/scanApi";
import { ScanModesNumeric } from "../../../../constants/advancedSettingsConst";
import { JobResultSections } from "../../../../constants/miscConst";
import { DeleteIcon, CommentIcon, retryJobIcon } from "../utils/icons";
import {
DeleteIcon,
CommentIcon,
retryJobIcon,
downloadReportIcon,
} from "../utils/icons";

export function JobActionsBar({ job }) {
// routers
Expand All @@ -31,24 +31,26 @@ export function JobActionsBar({ job }) {
setTimeout(() => navigate(-1), 250);
};

const onDownloadSampleBtnClick = async () => {
const blob = await downloadJobSample(job.id);
if (!blob) return;
const fileDownload = (blob, filename) => {
// create URL blob and a hidden <a> tag to serve file for download
const fileLink = document.createElement("a");
fileLink.href = window.URL.createObjectURL(blob);
fileLink.rel = "noopener,noreferrer";
if (job?.file_name) {
// it forces the name of the downloaded file
fileLink.download = `${job.file_name}`;
}
fileLink.download = `${filename}`;
// triggers the click event
fileLink.click();
};

const shareText = `Checkout this job (#${job.id}, ${
job.is_sample ? job.file_name : job.observable_name
}) on IntelOwl`;
const onDownloadSampleBtnClick = async () => {
const blob = await downloadJobSample(job.id);
if (!blob) return;
let filename = "file";
if (job?.file_name) {
// it forces the name of the downloaded file
filename = `${job.file_name}`;
}
fileDownload(blob, filename);
};

const handleRetry = async () => {
if (job.is_sample) {
Expand Down Expand Up @@ -80,6 +82,14 @@ export function JobActionsBar({ job }) {
}
};

const onDownloadReport = () => {
if (job) {
const blob = new Blob([JSON.stringify(job)], { type: "text/json" });
if (!blob) return;
fileDownload(blob, `job#${job.id}_report.json`);
}
};

const commentIcon = () => <CommentIcon commentNumber={job.comments.length} />;
return (
<ContentSection className="d-inline-flex me-2">
Expand Down Expand Up @@ -110,29 +120,32 @@ export function JobActionsBar({ job }) {
Icon={retryJobIcon}
onClick={handleRetry}
color="light"
size="xs"
size="sm"
title="Force run the same analysis"
titlePlacement="top"
className="me-2"
/>

<SaveAsPlaybookButton job={job} />
{job?.is_sample && (
<Button
size="sm"
color="secondary"
className="me-2"
className="ms-2"
onClick={onDownloadSampleBtnClick}
>
<FaFileDownload />
&nbsp;Sample
</Button>
)}
<SocialShareBtn
id="analysis-actions-share"
url={window.location.href}
text={shareText}
longtext={shareText}
<IconButton
id="downloadreportbtn"
Icon={downloadReportIcon}
size="sm"
color="accent-2"
className="ms-2"
onClick={onDownloadReport}
title="Download report in json format"
titlePlacement="top"
/>
</ContentSection>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ export function SaveAsPlaybookButton({ job }) {
return (
<PopupFormButton
id="saveasplaybook"
className="me-2"
Form={SaveAsPlaybookForm}
Icon={SaveAsPlaybookIcon}
popOverPlacement="bottom"
Expand Down
16 changes: 15 additions & 1 deletion frontend/src/components/jobs/result/utils/icons.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import React from "react";
import PropTypes from "prop-types";
import { MdDeleteOutline, MdOutlineRefresh, MdComment } from "react-icons/md";
import {
MdDeleteOutline,
MdOutlineRefresh,
MdComment,
MdFileDownload,
} from "react-icons/md";

export function DeleteIcon() {
return (
Expand Down Expand Up @@ -31,3 +36,12 @@ export function retryJobIcon() {
</span>
);
}

export function downloadReportIcon() {
return (
<span>
<MdFileDownload className="me-1" />
Report
</span>
);
}
4 changes: 2 additions & 2 deletions frontend/src/layouts/AppHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
} from "reactstrap";
import { NavLink as RRNavLink, useLocation } from "react-router-dom";
import { AiOutlineDashboard } from "react-icons/ai";
import { SiHackaday } from "react-icons/si";
import { MdHome, MdShare } from "react-icons/md";
import {
RiFileListFill,
Expand All @@ -22,6 +21,7 @@ import {
RiTwitterXFill,
} from "react-icons/ri";
import { FaGithub, FaGoogle } from "react-icons/fa";
import { IoSearch } from "react-icons/io5";

// lib
import { NavLink, AxiosLoadingBar } from "@certego/certego-ui";
Expand Down Expand Up @@ -64,7 +64,7 @@ const authLinks = (
</NavItem>
<NavItem>
<NavLink className="d-flex-start-center" end to="/scan">
<SiHackaday />
<IoSearch />
<span className="ms-1">Scan</span>
</NavLink>
</NavItem>
Expand Down
4 changes: 1 addition & 3 deletions frontend/tests/components/jobs/result/JobOverview.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,6 @@ describe("test JobOverview (job report)", () => {
// utility bar
const utilitiesRow = container.querySelector("#utilitiesRow");
expect(within(utilitiesRow).getByText("Job #1")).toBeInTheDocument();
const goBackButton = within(utilitiesRow).getByRole("button", { name: "" });
expect(goBackButton.id).toBe("gobackbutton");
expect(
within(utilitiesRow).getByRole("button", { name: "Comments (1)" }),
).toBeInTheDocument();
Expand All @@ -91,7 +89,7 @@ describe("test JobOverview (job report)", () => {
within(utilitiesRow).getByRole("button", { name: "Save As Playbook" }),
).toBeInTheDocument();
expect(
within(utilitiesRow).getByRole("button", { name: "Share" }),
within(utilitiesRow).getByRole("button", { name: "Report" }),
).toBeInTheDocument();
});

Expand Down
Loading