From 7d0bf1e5df91a5f650e844285595d5adf53867ba Mon Sep 17 00:00:00 2001 From: Krishna Mahato Date: Wed, 24 Aug 2022 23:14:04 +0530 Subject: [PATCH] feat(UI): added show jobs page to view the history of all the jobs for a given upload refactor(ui): added main license and status stats on the browse table for each upload Signed-off-by: Krishna Mahato --- src/Routes.jsx | 7 ++ src/api/jobs.js | 11 +++ src/constants/constants.js | 13 ++- src/constants/endpoints.js | 3 +- src/constants/routes.js | 1 + src/pages/Browse/index.jsx | 69 ++++++++++------ src/pages/Jobs/ShowJobs/index.jsx | 126 ++++++++++++++++++++++++++++++ src/services/jobs.js | 8 ++ 8 files changed, 210 insertions(+), 28 deletions(-) create mode 100644 src/pages/Jobs/ShowJobs/index.jsx diff --git a/src/Routes.jsx b/src/Routes.jsx index 2705f4105..493d191a6 100644 --- a/src/Routes.jsx +++ b/src/Routes.jsx @@ -71,6 +71,7 @@ const OneShotMonk = React.lazy(() => import("pages/Upload/OneShotMonk")); const AllJobs = React.lazy(() => import("pages/Jobs/AllJobs")); const MyRecentJobs = React.lazy(() => import("pages/Jobs/MyRecentJobs")); const ScheduleAgents = React.lazy(() => import("pages/Jobs/ScheduleAgents")); +const ShowJobs = React.lazy(() => import("pages/Jobs/ShowJobs")); // Organize Pages const DeleteFolder = React.lazy(() => import("pages/Organize/Folder/Delete")); @@ -119,6 +120,12 @@ const Routes = () => { {/* Browse Page */} + {/* Upload job history page */} + {/* Browse Uploads Pages */} { }); }; +export const getUploadHistoryApi = (uploadId) => { + const url = endpoints.jobs.uploadHistory(uploadId); + return sendRequest({ + url, + method: "GET", + headers: { + Authorization: getToken(), + }, + }); +}; + export default getJobApi; diff --git a/src/constants/constants.js b/src/constants/constants.js index dc4cd7f2b..e932317d4 100644 --- a/src/constants/constants.js +++ b/src/constants/constants.js @@ -22,19 +22,19 @@ import { defaultAgentsList, getLocalStorage } from "shared/storageHelper"; export const statusOptions = [ { id: 0, - name: "open", + name: "Open", }, { id: 1, - name: "in progress", + name: "InProgress", }, { id: 2, - name: "closed", + name: "Closed", }, { id: 3, - name: "rejected", + name: "Rejected", }, ]; @@ -80,6 +80,11 @@ export const actionsOptions = [ name: "Export Unified Report", reportFormat: "unifiedreport", }, + { + id: 6, + name: "History", + reportFormat: "uploadHistory", + }, ]; export const initialMessage = { type: "success", diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index 634ddc553..4b3ceac7c 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -17,7 +17,7 @@ */ // Api Url set in the env file -const apiUrl = `${ +export const apiUrl = `${ process.env.REACT_APP_HTTPS === "true" ? "https" : "http" }://${process.env.REACT_APP_SERVER_URL}`; @@ -25,6 +25,7 @@ const apiUrl = `${ const endpoints = { jobs: { details: (jobId) => `${apiUrl}/jobs/${jobId}`, + uploadHistory: (uploadId) => `${apiUrl}/jobs/history?upload=${uploadId}`, scheduleAnalysis: () => `${apiUrl}/jobs`, allJobs: () => `${apiUrl}/jobs/all`, scheduleReport: () => `${apiUrl}/report`, diff --git a/src/constants/routes.js b/src/constants/routes.js index 832858e97..d87c51eb9 100644 --- a/src/constants/routes.js +++ b/src/constants/routes.js @@ -37,6 +37,7 @@ const routes = { myRecentJobs: "/jobs/myRecentJobs", allRecentJobs: "/jobs/allRecentJobs", scheduleAgents: "/jobs/scheduleAgents", + showJobs: "/showJobs/:uploadId", }, organize: { folders: { diff --git a/src/pages/Browse/index.jsx b/src/pages/Browse/index.jsx index b3489a15d..57e41ee4f 100644 --- a/src/pages/Browse/index.jsx +++ b/src/pages/Browse/index.jsx @@ -19,7 +19,7 @@ import React, { useState, useEffect } from "react"; import routes from "constants/routes"; -import { Link } from "react-router-dom"; +import { Link, useHistory } from "react-router-dom"; import arrayToTree from "array-to-tree"; import messages from "constants/messages"; @@ -41,7 +41,7 @@ import { handleError, } from "shared/helper"; import Pagination from "@material-ui/lab/Pagination"; - +import { getUploadSummary } from "services/upload"; import { statusOptions, entriesOptions, @@ -79,30 +79,46 @@ const Browse = () => { const [query, setQuery] = useState(""); const [pages, setPages] = useState(); - useEffect(() => { + const history = useHistory(); + + useEffect(async () => { setMessage({ type: "success", text: messages.loading, }); setShowMessage(true); - getBrowseData(browseData) - .then((res) => { - setBrowseDataList(res.res); - const arr = []; - setPages(res.pages); - for (let i = 0; i < res.pages; i++) { - arr.push({ - id: i + 1, - value: i + 1, - }); - } - setPagesOptions(arr); - setShowMessage(false); - }) - .catch((error) => { - handleError(error, setMessage); - setShowMessage(true); + try { + const res = await getBrowseData(browseData); + const allUploads = res.res; + + // getting the upload summary for each upload + const promises = allUploads.map((upload) => getUploadSummary(upload.id)); + const allUploadSummary = await Promise.all(promises); + const allUploadsWithSummary = allUploads.map((upload) => { + const tempSumm = allUploadSummary.filter( + (val) => val?.id === upload?.id + ); + return { + ...upload, + uploadSummary: tempSumm.length ? tempSumm[0] : null, + }; }); + setBrowseDataList(allUploadsWithSummary); + + const arr = []; + setPages(res.pages); + for (let i = 0; i < res.pages; i++) { + arr.push({ + id: i + 1, + value: i + 1, + }); + } + setPagesOptions(arr); + setShowMessage(false); + } catch (error) { + handleError(error, setMessage); + setShowMessage(true); + } }, [browseData]); useEffect(() => { @@ -146,6 +162,10 @@ const Browse = () => { }; const handleActionChange = (e, uploadId) => { + if (e.target.value === "uploadHistory") { + history.push(`/showJobs/${uploadId}`); + return; + } scheduleReport(uploadId, e.target.value) .then((res) => { return res?.message; @@ -296,7 +316,8 @@ const Browse = () => { return post; return null; }) - ?.map((data) => ( + ?.reverse() + .map((data) => ( { onChange={(e) => handleChange(e)} options={statusOptions} property="name" + value={data?.uploadSummary?.clearingStatus} + valueProperty="name" /> - - - + {data?.uploadSummary?.mainLicense} { name="page" className="col-md-6 pagination-div " property="value" - count={pages} + count={parseInt(pages, 10)} page={browseData.page} onChange={handlePageChange} /> diff --git a/src/pages/Jobs/ShowJobs/index.jsx b/src/pages/Jobs/ShowJobs/index.jsx new file mode 100644 index 000000000..ade7fd468 --- /dev/null +++ b/src/pages/Jobs/ShowJobs/index.jsx @@ -0,0 +1,126 @@ +/* + Copyright (C) 2022 Krishna Mahato (krishhtrishh9304@gmail.com) + + SPDX-License-Identifier: GPL-2.0 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +import React, { useEffect, useState } from "react"; + +// Title +import Title from "components/Title"; + +// Required functions for API and Error handling +import { initialMessage } from "constants/constants"; +import { Alert } from "components/Widgets"; +import { useParams } from "react-router-dom"; +import { getUploadHistory } from "services/jobs"; +import { apiUrl } from "constants/endpoints"; + +const ShowJobs = () => { + const [message, setMessage] = useState(initialMessage); + const [showMessage, setShowMessage] = useState(false); + const [allJobsHistoryList, setAllJobsHistoryList] = useState([]); + const params = useParams(); + + useEffect(async () => { + if (params.uploadId) { + try { + const allJobsHistory = await getUploadHistory(params.uploadId); + setAllJobsHistoryList(allJobsHistory); + } catch (error) { + setMessage(error.message); + setShowMessage(true); + } + } + }, [params]); + + return ( + <> + + <div className="main-container my-3 text-center"> + {showMessage && ( + <Alert + type={message.type} + setShow={setShowMessage} + message={message.text} + /> + )} + <div> + <div> + {allJobsHistoryList?.map((job) => { + return ( + <table + key={job.jobId} + className="table table-striped text-primary-color font-size-medium table-responsive-sm table-bordered" + > + <thead> + <tr className="font-bold"> + <th>Job/Dependency</th> + <th>Status</th> + <th colSpan={3}>{job.jobName}</th> + <th>Average items/sec</th> + <th>ETA</th> + <th>Download</th> + </tr> + </thead> + <tbody> + {job?.jobQueue?.map((jobQues) => { + return ( + <tr key={jobQues.jobQueueId}> + <td> + {jobQues?.jobQueueId} + {jobQues?.dependencies[0] && + ` / ${jobQues?.dependencies[0]}`} + </td> + <td>{jobQues?.status}</td> + <td>{jobQues?.jobQueueType}</td> + <td> + {jobQues?.startTime} - {jobQues?.endTime} + </td> + <td>{jobQues?.itemsProcessed} items</td> + <td>{jobQues?.itemsPerSec} items/sec</td> + <td> + {job.upload.uploadEta.length + ? job.upload.uploadEta + : "Scanned"} + </td> + <td> + <a + target="_blank" + rel="noreferrer" + href={`${apiUrl.slice( + 0, + -7 + )}/?mod=download&report=${job.jobId}`} + > + {jobQues?.itemsProcessed !== 0 && + jobQues?.download} + </a> + </td> + </tr> + ); + })} + </tbody> + </table> + ); + })} + </div> + </div> + </div> + </> + ); +}; + +export default ShowJobs; diff --git a/src/services/jobs.js b/src/services/jobs.js index 01244cc3f..858e5112c 100644 --- a/src/services/jobs.js +++ b/src/services/jobs.js @@ -23,6 +23,7 @@ import { downloadReportApi, getAllJobApi, getAllAdminJobApi, + getUploadHistoryApi, } from "api/jobs"; import { getReportIdFromUrl } from "shared/helper"; import { getLocalStorage } from "shared/storageHelper"; @@ -81,4 +82,11 @@ export const downloadReport = (url) => { }); }; +// Fetching the jobs history based on upload id +export const getUploadHistory = (uploadId) => { + return getUploadHistoryApi(uploadId).then((res) => { + return res; + }); +}; + export default getJob;