From 9fb8cef94fbcdd436b26dc190e32fdf9a0e8d2cb Mon Sep 17 00:00:00 2001 From: Amit Kumar Das <40661238+amityt@users.noreply.github.com> Date: Fri, 4 Sep 2020 17:55:52 +0530 Subject: [PATCH] chore(litmus-portal): Added date filter in workflows table and some important refactorings (#1962) This commit has the following changes: - Date filter in the workflows table - Important refactoring in the Browse Workflow Section - Minor CSS fixes Signed-off-by: Amit Kumar Das --- litmus-portal/frontend/package-lock.json | 31 ++- litmus-portal/frontend/package.json | 2 + .../BrowseWorkflow/TableData.tsx | 5 +- .../BrowseWorkflow/headerSection.tsx | 169 +++++++++++++++ .../ChaosWorkflows/BrowseWorkflow/index.tsx | 198 +++++++++--------- .../ChaosWorkflows/BrowseWorkflow/styles.ts | 84 +++++--- .../ScheduleWorkflow/TableData.tsx | 34 +-- .../ChaosWorkflows/ScheduleWorkflow/index.tsx | 175 +++++++++++++--- .../ChaosWorkflows/ScheduleWorkflow/styles.ts | 71 ++++--- 9 files changed, 571 insertions(+), 198 deletions(-) create mode 100644 litmus-portal/frontend/src/components/Sections/ChaosWorkflows/BrowseWorkflow/headerSection.tsx diff --git a/litmus-portal/frontend/package-lock.json b/litmus-portal/frontend/package-lock.json index d71f567ef..f8ce2cffa 100644 --- a/litmus-portal/frontend/package-lock.json +++ b/litmus-portal/frontend/package-lock.json @@ -2753,6 +2753,16 @@ "csstype": "^2.2.0" } }, + "@types/react-date-range": { + "version": "0.95.1", + "resolved": "https://registry.npmjs.org/@types/react-date-range/-/react-date-range-0.95.1.tgz", + "integrity": "sha512-jzuh7fL9F1Y3eSoeyWoS/H2zSCn9WXvlbLfOOox9bopmZjyNwp/fx08TYrgi8jAJ60F2Aap8uA2BQbe8+7mNGg==", + "dev": true, + "requires": { + "@types/react": "*", + "moment": ">=2.14.0" + } + }, "@types/react-dom": { "version": "16.9.8", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.8.tgz", @@ -13718,6 +13728,24 @@ "gl-mat4": "^1.0.1" } }, + "materialui-daterange-picker": { + "version": "1.1.91", + "resolved": "https://registry.npmjs.org/materialui-daterange-picker/-/materialui-daterange-picker-1.1.91.tgz", + "integrity": "sha512-vMEQlI3WrTuWEVgAhO6P5X6WfC46A1I4BwnlaEKz9Tbsw/1vj1fms5BX37yJkZZs4+Rbj1XIp34l7uy3UyuVyw==", + "requires": { + "@material-ui/core": "^4.9.4", + "@material-ui/icons": "^4.9.1", + "classnames": "^2.2.6", + "date-fns": "^1.30.1" + }, + "dependencies": { + "date-fns": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", + "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==" + } + } + }, "math-log2": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/math-log2/-/math-log2-1.0.1.tgz", @@ -14174,8 +14202,7 @@ "moment": { "version": "2.27.0", "resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz", - "integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==", - "dev": true + "integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==" }, "monotone-convex-hull-2d": { "version": "1.0.1", diff --git a/litmus-portal/frontend/package.json b/litmus-portal/frontend/package.json index 2a8c6d413..be5c062f3 100644 --- a/litmus-portal/frontend/package.json +++ b/litmus-portal/frontend/package.json @@ -27,6 +27,8 @@ "js-yaml": "^3.14.0", "jsonwebtoken": "^8.5.1", "localforage": "^1.7.3", + "materialui-daterange-picker": "^1.1.91", + "moment": "^2.27.0", "plotly.js": "^1.54.6", "prop-types": "^15.7.2", "rc-progress": "^3.0.0", diff --git a/litmus-portal/frontend/src/components/Sections/ChaosWorkflows/BrowseWorkflow/TableData.tsx b/litmus-portal/frontend/src/components/Sections/ChaosWorkflows/BrowseWorkflow/TableData.tsx index d96e017a2..b8e317059 100644 --- a/litmus-portal/frontend/src/components/Sections/ChaosWorkflows/BrowseWorkflow/TableData.tsx +++ b/litmus-portal/frontend/src/components/Sections/ChaosWorkflows/BrowseWorkflow/TableData.tsx @@ -12,9 +12,10 @@ import LinearProgressBar from '../../ReturningHome/ProgressBar/LinearProgressBar import useStyles from './styles'; import timeDifferenceForDate from '../../../../utils/datesModifier'; import { history } from '../../../../redux/configureStore'; +import { WorkflowRun } from '../../../../models/workflowData'; interface TableDataProps { - data: any; + data: WorkflowRun; } const TableData: React.FC = ({ data }) => { @@ -33,7 +34,7 @@ const TableData: React.FC = ({ data }) => { const handleMenu = () => {}; return ( <> - + diff --git a/litmus-portal/frontend/src/components/Sections/ChaosWorkflows/BrowseWorkflow/headerSection.tsx b/litmus-portal/frontend/src/components/Sections/ChaosWorkflows/BrowseWorkflow/headerSection.tsx new file mode 100644 index 000000000..2b9c4da0e --- /dev/null +++ b/litmus-portal/frontend/src/components/Sections/ChaosWorkflows/BrowseWorkflow/headerSection.tsx @@ -0,0 +1,169 @@ +import React from 'react'; +import { + InputBase, + InputAdornment, + FormControl, + InputLabel, + Select, + MenuItem, + Typography, + IconButton, + Popover, +} from '@material-ui/core'; +import SearchIcon from '@material-ui/icons/Search'; +import { DateRangePicker } from 'materialui-daterange-picker'; +import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown'; +import ChevronRightIcon from '@material-ui/icons/ChevronRight'; +import useStyles from './styles'; + +interface HeaderSectionProps { + searchValue: string; + statusValue: string; + clusterValue: string; + isOpen: boolean; + isDateOpen: boolean; + popAnchorEl: HTMLElement | null; + displayDate: string; + changeSearch: ( + event: React.ChangeEvent + ) => void; + changeStatus: ( + event: React.ChangeEvent<{ + name?: string | undefined; + value: unknown; + }>, + child: React.ReactNode + ) => void; + changeCluster: ( + event: React.ChangeEvent<{ + name?: string | undefined; + value: unknown; + }>, + child: React.ReactNode + ) => void; + popOverClick: ( + event: React.MouseEvent + ) => void; + popOverClose: ( + event: React.MouseEvent + ) => void; + toggle: () => void; + selectDate: (range: any) => void; +} + +const HeaderSection: React.FC = ({ + searchValue, + statusValue, + clusterValue, + isOpen, + popAnchorEl, + isDateOpen, + displayDate, + changeSearch, + changeStatus, + changeCluster, + popOverClick, + popOverClose, + toggle, + selectDate, +}) => { + const classes = useStyles(); + + return ( +
+
+ {/* Search Field */} + + + + } + /> + + {/* Select Workflow */} + + + Workflow Status + + + + + {/* Select Cluster */} + + Target Cluster + + + +
+ + {displayDate} + + {isOpen ? : } + + +
+ + + +
+
+ ); +}; +export default HeaderSection; diff --git a/litmus-portal/frontend/src/components/Sections/ChaosWorkflows/BrowseWorkflow/index.tsx b/litmus-portal/frontend/src/components/Sections/ChaosWorkflows/BrowseWorkflow/index.tsx index b5f46dab8..22e63d1a3 100644 --- a/litmus-portal/frontend/src/components/Sections/ChaosWorkflows/BrowseWorkflow/index.tsx +++ b/litmus-portal/frontend/src/components/Sections/ChaosWorkflows/BrowseWorkflow/index.tsx @@ -1,12 +1,6 @@ import { useQuery } from '@apollo/client'; import { - FormControl, IconButton, - InputAdornment, - InputBase, - InputLabel, - MenuItem, - Select, Table, TableBody, TableCell, @@ -16,9 +10,9 @@ import { TableRow, Typography, } from '@material-ui/core'; +import moment from 'moment'; import ExpandLessIcon from '@material-ui/icons/ExpandLess'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; -import SearchIcon from '@material-ui/icons/Search'; import React, { useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; import { WORKFLOW_DETAILS, WORKFLOW_EVENTS } from '../../../../graphql'; @@ -41,6 +35,7 @@ import { import Loader from '../../../Loader'; import useStyles from './styles'; import TableData from './TableData'; +import HeaderSection from './headerSection'; interface FilterOptions { search: string; @@ -59,6 +54,12 @@ interface SortData { noOfSteps: { sort: boolean; ascending: boolean }; } +interface DateData { + dateValue: string; + fromDate: Date | undefined; + toDate: Date | undefined; +} + const BrowseWorkflow = () => { const classes = useStyles(); const userData: UserData = useSelector((state: RootState) => state.userData); @@ -72,7 +73,6 @@ const BrowseWorkflow = () => { variables: { projectID: selectedProjectID }, fetchPolicy: 'cache-and-network', }); - // Using subscription to get realtime data useEffect(() => { subscribeToMore({ @@ -121,6 +121,29 @@ const BrowseWorkflow = () => { rowsPerPage: 5, }); + const [popAnchorEl, setPopAnchorEl] = React.useState( + null + ); + const isOpen = Boolean(popAnchorEl); + + const [open, setOpen] = React.useState(false); + + const handlePopOverClose = () => { + setPopAnchorEl(null); + setOpen(false); + }; + const handlePopOverClick = (event: React.MouseEvent) => { + setPopAnchorEl(event.currentTarget); + setOpen(true); + }; + + // State for start date and end date + const [dateRange, setDateRange] = React.useState({ + dateValue: 'Select a period', + fromDate: new Date(0), + toDate: new Date(new Date().valueOf() + 1000 * 3600 * 24), + }); + const filteredData = data?.getWorkFlowRuns .filter((dataRow) => dataRow.workflow_name.toLowerCase().includes(filters.search) @@ -137,6 +160,14 @@ const BrowseWorkflow = () => { ? true : dataRow.cluster_name.toLowerCase().includes(filters.cluster) ) + .filter((dataRow) => { + return dateRange.fromDate && dateRange.toDate === undefined + ? true + : parseInt(dataRow.last_updated, 10) * 1000 >= + new Date(moment(dateRange.fromDate).format()).getTime() && + parseInt(dataRow.last_updated, 10) * 1000 <= + new Date(moment(dateRange.toDate).format()).getTime(); + }) .sort((a: WorkflowRun, b: WorkflowRun) => { // Sorting based on unique fields if (sortData.name.sort) { @@ -178,94 +209,73 @@ const BrowseWorkflow = () => { return 0; }); + // Functions passed as props in the headerSeaction + const changeSearch = ( + event: React.ChangeEvent + ) => { + setFilters({ ...filters, search: event.target.value as string }); + setPaginationData({ ...paginationData, pageNo: 0 }); + }; + + const changeStatus = ( + event: React.ChangeEvent<{ + name?: string | undefined; + value: unknown; + }> + ) => { + setFilters({ ...filters, status: event.target.value as string }); + setPaginationData({ ...paginationData, pageNo: 0 }); + }; + + const changeCluster = ( + event: React.ChangeEvent<{ + name?: string | undefined; + value: unknown; + }> + ) => { + setFilters({ ...filters, cluster: event.target.value as string }); + setPaginationData({ ...paginationData, pageNo: 0 }); + }; + + // Function to set the date range for filtering + const dateChange = (range: any) => { + setDateRange({ + dateValue: `${moment(range?.startDate) + .format('DD.MM.YYYY') + .toString()}-${moment(range?.endDate).format('DD.MM.YYYY').toString()}`, + fromDate: + moment(range?.startDate).format('DD-MM-YYYY') === + moment(new Date()).format('DD-MM-YYYY') + ? new Date(new Date().setHours(0, 0, 0)) + : new Date(range?.startDate.setHours(0, 0, 0)), + toDate: + moment(range?.endDate).format('DD-MM-YYYY') === + moment(new Date()).format('DD-MM-YYYY') + ? new Date(new Date().setHours(23, 59, 59)) + : new Date(range?.endDate.setHours(23, 59, 59)), + }); + }; + return (
-
- { - setFilters({ ...filters, search: e.target.value as string }); - setPaginationData({ ...paginationData, pageNo: 0 }); - }} - startAdornment={ - - - - } - /> - - - Workflow Status - - - - - - - Target Cluster - - - - - Date - setFilter({ ...filter, cluster: event.target.value as string }) } - disableUnderline + label="Target Cluster" + className={classes.selectText} > - - All - - - - Cluset pre-defined - - - - - Kubernetes Cluster - - + All + Cluset pre-defined + Kubernetes Cluster
+ {/* Table Header */} + {/* WorkFlow Name */} - - Workflow Name - +
+ + Workflow Name + +
+ + setSortData({ + ...sortData, + name: { sort: true, ascending: true }, + startDate: { sort: false, ascending: true }, + }) + } + > + + + + setSortData({ + ...sortData, + name: { sort: true, ascending: false }, + startDate: { sort: false, ascending: false }, + }) + } + > + + +
+
+ + {/* Starting Date */} - Starting Date +
+ + Starting Date + +
+ + setSortData({ + ...sortData, + startDate: { sort: true, ascending: true }, + name: { sort: false, ascending: true }, + }) + } + > + + + + setSortData({ + ...sortData, + startDate: { sort: true, ascending: false }, + name: { sort: false, ascending: true }, + }) + } + > + + +
+
+ + {/* Regularity */} Regularity + + {/* Cluster */} Cluster + + {/* Show Experiments */} Show Experiments @@ -169,9 +290,9 @@ const ScheduleWorkflow = () => { paginationData.pageNo * paginationData.rowsPerPage + paginationData.rowsPerPage ) - .map((data: any) => ( + .map((data) => ( - + )) ) : ( @@ -184,6 +305,8 @@ const ScheduleWorkflow = () => {
+ + {/* Pagination Section */} ({ + // Header Section Properties headerSection: { width: '100%', + height: '5.625rem', display: 'flex', flexDirection: 'row', alignContent: 'center', alignItems: 'center', justifyContent: 'center', border: '1px solid ', - borderColor: theme.palette.text.hint, + borderColor: theme.palette.customColors.black(0.07), backgroundColor: theme.palette.common.white, }, search: { + fontSize: 14, marginRight: 'auto', + borderBottom: `1px solid ${theme.palette.customColors.black(0.1)}`, marginLeft: theme.spacing(6.25), }, - select: { - width: '10.375rem', - marginLeft: theme.spacing(1.25), - paddingBottom: theme.spacing(2.5), - marginRight: theme.spacing(3.75), + + // Form Select Properties + formControl: { + margin: theme.spacing(0.5), + marginRight: theme.spacing(6.25), + height: '2.5rem', + minWidth: '9rem', }, + selectText: { + height: '2.5rem', + padding: theme.spacing(0.5), + }, + + // Table and Table Data Properties headerText: { marginLeft: theme.spacing(3.75), color: theme.palette.text.disabled, @@ -29,9 +41,9 @@ const useStyles = makeStyles((theme) => ({ }, tableMain: { marginTop: theme.spacing(6.25), - border: '1px solid rgba(0,0,0,0.1)', + border: `1px solid ${theme.palette.customColors.black(0.07)}`, backgroundColor: theme.palette.common.white, - height: '29.180rem', + height: '29.220rem', }, tableHead: { opacity: 1, @@ -54,11 +66,16 @@ const useStyles = makeStyles((theme) => ({ paddingLeft: theme.spacing(1.75), }, workflowName: { - borderRight: '1px solid rgba(0,0,0,0.1)', + borderRight: `1px solid ${theme.palette.customColors.black(0.1)}`, color: theme.palette.customColors.black(0.4), }, + sortDiv: { + display: 'flex', + flexDirection: 'column', + marginLeft: theme.spacing(1.25), + }, workflowNameData: { - borderRight: '1px solid rgba(0,0,0,0.1)', + borderRight: `1px solid ${theme.palette.customColors.black(0.1)}`, }, regularity: { paddingLeft: theme.spacing(3.75), @@ -84,24 +101,6 @@ const useStyles = makeStyles((theme) => ({ paddingLeft: theme.spacing(1), color: theme.palette.customColors.black(0.4), }, - expDiv: { - display: 'flex', - flexDirection: 'row', - }, - expDiv1: { - display: 'flex', - flexDirection: 'row', - cursor: 'pointer', - }, - weightDiv: { - width: 243, - padding: '25px 20px', - }, - weightInfo: { - display: 'flex', - flexDirection: 'row', - paddingBottom: theme.spacing(0.625), - }, clusterData: { paddingTop: theme.spacing(1.25), }, @@ -123,6 +122,22 @@ const useStyles = makeStyles((theme) => ({ btnText: { paddingLeft: theme.spacing(1.625), }, + + // Experiment Weights PopOver Property + weightDiv: { + width: '15.1875rem', + padding: theme.spacing(3.125, 2.6), + }, + weightInfo: { + display: 'flex', + flexDirection: 'row', + paddingBottom: theme.spacing(0.625), + }, + expDiv: { + display: 'flex', + flexDirection: 'row', + cursor: 'pointer', + }, })); export default useStyles;