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 <amitkumar.das@mayadata.io>
This commit is contained in:
parent
d74c1d3898
commit
9fb8cef94f
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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<TableDataProps> = ({ data }) => {
|
||||
|
@ -33,7 +34,7 @@ const TableData: React.FC<TableDataProps> = ({ data }) => {
|
|||
const handleMenu = () => {};
|
||||
return (
|
||||
<>
|
||||
<TableCell className={classes.headerStatus1}>
|
||||
<TableCell className={classes.tableDataStatus}>
|
||||
<CustomStatus status={JSON.parse(data.execution_data).phase} />
|
||||
</TableCell>
|
||||
<TableCell className={classes.workflowNameData}>
|
||||
|
|
|
@ -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<HTMLTextAreaElement | HTMLInputElement>
|
||||
) => 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<HTMLButtonElement, MouseEvent>
|
||||
) => void;
|
||||
popOverClose: (
|
||||
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||
) => void;
|
||||
toggle: () => void;
|
||||
selectDate: (range: any) => void;
|
||||
}
|
||||
|
||||
const HeaderSection: React.FC<HeaderSectionProps> = ({
|
||||
searchValue,
|
||||
statusValue,
|
||||
clusterValue,
|
||||
isOpen,
|
||||
popAnchorEl,
|
||||
isDateOpen,
|
||||
displayDate,
|
||||
changeSearch,
|
||||
changeStatus,
|
||||
changeCluster,
|
||||
popOverClick,
|
||||
popOverClose,
|
||||
toggle,
|
||||
selectDate,
|
||||
}) => {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={classes.headerSection}>
|
||||
{/* Search Field */}
|
||||
<InputBase
|
||||
id="input-with-icon-adornment"
|
||||
placeholder="Search"
|
||||
className={classes.search}
|
||||
value={searchValue}
|
||||
onChange={changeSearch}
|
||||
startAdornment={
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Select Workflow */}
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
className={classes.formControl}
|
||||
color="secondary"
|
||||
focused
|
||||
>
|
||||
<InputLabel className={classes.selectText}>
|
||||
Workflow Status
|
||||
</InputLabel>
|
||||
<Select
|
||||
value={statusValue}
|
||||
onChange={changeStatus}
|
||||
label="Workflow Status"
|
||||
className={classes.selectText}
|
||||
>
|
||||
<MenuItem value="All">All</MenuItem>
|
||||
<MenuItem value="Failed">Failed</MenuItem>
|
||||
<MenuItem value="Running">Running</MenuItem>
|
||||
<MenuItem value="Succeeded">Succeeded</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
{/* Select Cluster */}
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
className={classes.formControl}
|
||||
color="secondary"
|
||||
focused
|
||||
>
|
||||
<InputLabel className={classes.selectText}>Target Cluster</InputLabel>
|
||||
<Select
|
||||
value={clusterValue}
|
||||
onChange={changeCluster}
|
||||
label="Target Cluster"
|
||||
className={classes.selectText}
|
||||
>
|
||||
<MenuItem value="All">All</MenuItem>
|
||||
<MenuItem value="Predefined">Cluset pre-defined</MenuItem>
|
||||
<MenuItem value="Kubernetes">Kubernetes Cluster</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<div className={classes.selectDate}>
|
||||
<Typography className={classes.displayDate}>
|
||||
{displayDate}
|
||||
<IconButton
|
||||
style={{ width: 10, height: 10 }}
|
||||
onClick={popOverClick}
|
||||
>
|
||||
{isOpen ? <KeyboardArrowDownIcon /> : <ChevronRightIcon />}
|
||||
</IconButton>
|
||||
</Typography>
|
||||
</div>
|
||||
<Popover
|
||||
open={isOpen}
|
||||
anchorEl={popAnchorEl}
|
||||
onClose={popOverClose}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
style={{
|
||||
marginTop: 10,
|
||||
}}
|
||||
>
|
||||
<DateRangePicker
|
||||
open={isDateOpen}
|
||||
toggle={toggle}
|
||||
onChange={selectDate}
|
||||
/>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default HeaderSection;
|
|
@ -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<WorkflowSubscription>({
|
||||
|
@ -121,6 +121,29 @@ const BrowseWorkflow = () => {
|
|||
rowsPerPage: 5,
|
||||
});
|
||||
|
||||
const [popAnchorEl, setPopAnchorEl] = React.useState<null | HTMLElement>(
|
||||
null
|
||||
);
|
||||
const isOpen = Boolean(popAnchorEl);
|
||||
|
||||
const [open, setOpen] = React.useState<boolean>(false);
|
||||
|
||||
const handlePopOverClose = () => {
|
||||
setPopAnchorEl(null);
|
||||
setOpen(false);
|
||||
};
|
||||
const handlePopOverClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setPopAnchorEl(event.currentTarget);
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
// State for start date and end date
|
||||
const [dateRange, setDateRange] = React.useState<DateData>({
|
||||
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<HTMLTextAreaElement | HTMLInputElement>
|
||||
) => {
|
||||
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 (
|
||||
<div>
|
||||
<section className="Heading section">
|
||||
<div className={classes.headerSection}>
|
||||
<InputBase
|
||||
id="input-with-icon-adornment"
|
||||
placeholder="Search"
|
||||
className={classes.search}
|
||||
value={filters.search}
|
||||
onChange={(e) => {
|
||||
setFilters({ ...filters, search: e.target.value as string });
|
||||
setPaginationData({ ...paginationData, pageNo: 0 });
|
||||
}}
|
||||
startAdornment={
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
}
|
||||
{/* Header Section */}
|
||||
<HeaderSection
|
||||
searchValue={filters.search}
|
||||
changeSearch={changeSearch}
|
||||
statusValue={filters.status}
|
||||
changeStatus={changeStatus}
|
||||
clusterValue={filters.cluster}
|
||||
changeCluster={changeCluster}
|
||||
popOverClick={handlePopOverClick}
|
||||
popOverClose={handlePopOverClose}
|
||||
isOpen={isOpen}
|
||||
popAnchorEl={popAnchorEl}
|
||||
isDateOpen={open}
|
||||
toggle={handlePopOverClose}
|
||||
displayDate={dateRange.dateValue}
|
||||
selectDate={dateChange}
|
||||
/>
|
||||
<FormControl className={classes.select}>
|
||||
<InputLabel id="demo-simple-select-outlined-label">
|
||||
Workflow Status
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId="demo-simple-select-outlined-label"
|
||||
id="demo-simple-select-outlined"
|
||||
value={filters.status}
|
||||
onChange={(e) => {
|
||||
setFilters({ ...filters, status: e.target.value as string });
|
||||
setPaginationData({ ...paginationData, pageNo: 0 });
|
||||
}}
|
||||
disableUnderline
|
||||
>
|
||||
<MenuItem value="All">
|
||||
<Typography className={classes.menuItem}>All</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem value="Failed">
|
||||
<Typography className={classes.menuItem}>Failed</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem value="Running">
|
||||
<Typography className={classes.menuItem}>Running</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem value="Succeeded">
|
||||
<Typography className={classes.menuItem}>Succeeded</Typography>
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormControl className={classes.select1}>
|
||||
<InputLabel id="demo-simple-select-outlined-label">
|
||||
Target Cluster
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId="demo-simple-select-outlined-label"
|
||||
id="demo-simple-select-outlined"
|
||||
value={filters.cluster}
|
||||
onChange={(e) => {
|
||||
setFilters({ ...filters, cluster: e.target.value as string });
|
||||
setPaginationData({ ...paginationData, pageNo: 0 });
|
||||
}}
|
||||
disableUnderline
|
||||
>
|
||||
<MenuItem value="All">
|
||||
<Typography className={classes.menuItem}>All</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem value="Predefined">
|
||||
<Typography className={classes.menuItem}>
|
||||
Cluset pre-defined
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem value="Kubernetes">
|
||||
<Typography className={classes.menuItem}>
|
||||
Kubernetes Cluster
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormControl className={classes.select}>
|
||||
<InputLabel id="demo-simple-select-outlined-label">Date</InputLabel>
|
||||
<Select
|
||||
labelId="demo-simple-select-outlined-label"
|
||||
id="demo-simple-select-outlined"
|
||||
value=""
|
||||
disableUnderline
|
||||
onChange={() => {}}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
</section>
|
||||
<section className="table section">
|
||||
<TableContainer className={classes.tableMain}>
|
||||
|
@ -394,7 +404,7 @@ const BrowseWorkflow = () => {
|
|||
</div>
|
||||
</TableCell>
|
||||
|
||||
{/* Menu */}
|
||||
{/* Menu Cell */}
|
||||
<TableCell />
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
|
|
|
@ -1,39 +1,60 @@
|
|||
import { makeStyles } from '@material-ui/core';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
// 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: '9.375rem',
|
||||
marginLeft: theme.spacing(1.25),
|
||||
paddingBottom: theme.spacing(2.5),
|
||||
|
||||
// Form Select Properties
|
||||
formControl: {
|
||||
margin: theme.spacing(0.5),
|
||||
marginRight: theme.spacing(2.5),
|
||||
height: '2.5rem',
|
||||
minWidth: '9rem',
|
||||
},
|
||||
select1: {
|
||||
width: '10.375rem',
|
||||
marginLeft: theme.spacing(1.25),
|
||||
paddingBottom: theme.spacing(2.5),
|
||||
|
||||
selectText: {
|
||||
height: '2.5rem',
|
||||
padding: theme.spacing(0.5),
|
||||
},
|
||||
headerText: {
|
||||
marginLeft: theme.spacing(3.75),
|
||||
color: theme.palette.text.disabled,
|
||||
paddingBottom: theme.spacing(0.625),
|
||||
|
||||
selectDate: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
height: '2.5rem',
|
||||
minWidth: '9rem',
|
||||
border: '1.7px solid',
|
||||
borderRadius: 4,
|
||||
borderColor: theme.palette.secondary.main,
|
||||
marginRight: theme.spacing(3.75),
|
||||
},
|
||||
displayDate: {
|
||||
marginLeft: theme.spacing(1.875),
|
||||
paddingTop: theme.spacing(0.75),
|
||||
width: '100%',
|
||||
},
|
||||
|
||||
// Table and Table Data Properties
|
||||
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.219rem',
|
||||
},
|
||||
|
@ -42,10 +63,22 @@ const useStyles = makeStyles((theme) => ({
|
|||
},
|
||||
headerStatus: {
|
||||
paddingLeft: theme.spacing(10),
|
||||
color: 'rgba(0, 0, 0, 0.4)',
|
||||
color: theme.palette.customColors.black(0.4),
|
||||
},
|
||||
headerStatus1: {
|
||||
paddingLeft: theme.spacing(8),
|
||||
workflowName: {
|
||||
borderRight: `1px solid ${theme.palette.customColors.black(0.1)}`,
|
||||
color: theme.palette.customColors.black(0.4),
|
||||
},
|
||||
sortDiv: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
paddingLeft: theme.spacing(1.25),
|
||||
},
|
||||
headData: {
|
||||
color: theme.palette.customColors.black(0.4),
|
||||
},
|
||||
tableDataStatus: {
|
||||
paddingLeft: theme.spacing(8.5),
|
||||
},
|
||||
progressBar: {
|
||||
width: '6.5rem',
|
||||
|
@ -53,23 +86,13 @@ const useStyles = makeStyles((theme) => ({
|
|||
steps: {
|
||||
marginLeft: theme.spacing(5.625),
|
||||
},
|
||||
menuItem: {
|
||||
paddingLeft: theme.spacing(1.75),
|
||||
},
|
||||
workflowName: {
|
||||
borderRight: '1px solid rgba(0,0,0,0.1)',
|
||||
color: theme.palette.customColors.black(0.4),
|
||||
},
|
||||
workflowNameData: {
|
||||
borderRight: '1px solid rgba(0,0,0,0.1)',
|
||||
borderRight: `1px solid ${theme.palette.customColors.black(0.1)}`,
|
||||
},
|
||||
targetCluster: {
|
||||
paddingLeft: theme.spacing(3.75),
|
||||
color: theme.palette.customColors.black(0.4),
|
||||
},
|
||||
headData: {
|
||||
color: theme.palette.customColors.black(0.4),
|
||||
},
|
||||
clusterName: {
|
||||
marginLeft: theme.spacing(3.75),
|
||||
},
|
||||
|
@ -86,11 +109,6 @@ const useStyles = makeStyles((theme) => ({
|
|||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
sortDiv: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
paddingLeft: theme.spacing(1.25),
|
||||
},
|
||||
}));
|
||||
|
||||
export default useStyles;
|
||||
|
|
|
@ -11,47 +11,52 @@ import {
|
|||
import MoreVertIcon from '@material-ui/icons/MoreVert';
|
||||
import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
|
||||
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
|
||||
import moment from 'moment';
|
||||
import useStyles from './styles';
|
||||
import LinearProgressBar from '../../ReturningHome/ProgressBar/LinearProgressBar';
|
||||
import { history } from '../../../../redux/configureStore';
|
||||
import { WorkflowRun } from '../../../../models/workflowData';
|
||||
|
||||
interface TableDataProps {
|
||||
data: any;
|
||||
data: WorkflowRun;
|
||||
deleteRow: (workflowId: string) => void;
|
||||
}
|
||||
|
||||
const TableData: React.FC<TableDataProps> = ({ data }) => {
|
||||
const TableData: React.FC<TableDataProps> = ({ data, deleteRow }) => {
|
||||
const classes = useStyles();
|
||||
|
||||
// States for PopOver to display Experiment Weights
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const [popAnchorEl, setPopAnchorEl] = React.useState<null | HTMLElement>(
|
||||
null
|
||||
);
|
||||
const open = Boolean(anchorEl);
|
||||
|
||||
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
const isOpen = Boolean(popAnchorEl);
|
||||
const id = isOpen ? 'simple-popover' : undefined;
|
||||
const handlePopOverClose = () => {
|
||||
setPopAnchorEl(null);
|
||||
};
|
||||
|
||||
const handlePopOverClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setPopAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
const handleMenu = () => {};
|
||||
|
||||
// Function to convert UNIX time in format of DD MMM YYY
|
||||
const formatDate = (date: any) => {
|
||||
const updated = new Date(date * 1000).toString();
|
||||
const day = updated.slice(8, 10);
|
||||
const month = updated.slice(4, 7);
|
||||
const year = updated.slice(11, 15);
|
||||
const resDate = `${day} ${month} ${year}`;
|
||||
const resDate = moment(updated).format('DD MMM YYYY');
|
||||
return resDate;
|
||||
};
|
||||
|
||||
// Dummy Experiment Weights
|
||||
const exWeight = [
|
||||
{
|
||||
name: 'Node add test',
|
||||
|
@ -103,7 +108,7 @@ const TableData: React.FC<TableDataProps> = ({ data }) => {
|
|||
<KeyboardArrowDownIcon className={classes.expInfo} />
|
||||
</div>
|
||||
) : (
|
||||
<div className={classes.expDiv1}>
|
||||
<div className={classes.expDiv}>
|
||||
<Typography>
|
||||
<strong>Show Experiment</strong>
|
||||
</Typography>
|
||||
|
@ -180,7 +185,10 @@ const TableData: React.FC<TableDataProps> = ({ data }) => {
|
|||
<Typography className={classes.btnText}>Edit Schedule</Typography>
|
||||
</div>
|
||||
</MenuItem>
|
||||
<MenuItem value="Analysis" onClick={handleMenu}>
|
||||
<MenuItem
|
||||
value="Analysis"
|
||||
onClick={() => deleteRow(data.workflow_id)}
|
||||
>
|
||||
<div className={classes.expDiv}>
|
||||
<img
|
||||
src="/icons/deleteSchedule.svg"
|
||||
|
|
|
@ -16,13 +16,27 @@ import {
|
|||
TableCell,
|
||||
TableBody,
|
||||
TablePagination,
|
||||
IconButton,
|
||||
} from '@material-ui/core';
|
||||
import ExpandLessIcon from '@material-ui/icons/ExpandLess';
|
||||
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
||||
import { useSelector } from 'react-redux';
|
||||
import useStyles from './styles';
|
||||
import { WORKFLOW_DETAILS } from '../../../../graphql';
|
||||
import TableData from './TableData';
|
||||
import { Workflow, WorkflowDataVars } from '../../../../models/workflowData';
|
||||
import {
|
||||
Workflow,
|
||||
WorkflowDataVars,
|
||||
WorkflowRun,
|
||||
ExecutionData,
|
||||
} from '../../../../models/workflowData';
|
||||
import Loader from '../../../Loader';
|
||||
import {
|
||||
sortAlphaAsc,
|
||||
sortAlphaDesc,
|
||||
sortNumAsc,
|
||||
sortNumDesc,
|
||||
} from '../../../../utils/sort';
|
||||
import { UserData } from '../../../../models/user';
|
||||
import { RootState } from '../../../../redux/reducers';
|
||||
|
||||
|
@ -36,6 +50,11 @@ interface PaginationData {
|
|||
rowsPerPage: number;
|
||||
}
|
||||
|
||||
interface SortData {
|
||||
startDate: { sort: boolean; ascending: boolean };
|
||||
name: { sort: boolean; ascending: boolean };
|
||||
}
|
||||
|
||||
const ScheduleWorkflow = () => {
|
||||
const classes = useStyles();
|
||||
const userData: UserData = useSelector((state: RootState) => state.userData);
|
||||
|
@ -59,6 +78,12 @@ const ScheduleWorkflow = () => {
|
|||
rowsPerPage: 5,
|
||||
});
|
||||
|
||||
// State for sorting
|
||||
const [sortData, setSortData] = useState<SortData>({
|
||||
name: { sort: false, ascending: true },
|
||||
startDate: { sort: true, ascending: true },
|
||||
});
|
||||
|
||||
const filteredData = data?.getWorkFlowRuns
|
||||
.filter((dataRow) =>
|
||||
dataRow.workflow_name.toLowerCase().includes(filter.search)
|
||||
|
@ -68,12 +93,41 @@ const ScheduleWorkflow = () => {
|
|||
? true
|
||||
: dataRow.cluster_name.toLowerCase().includes(filter.cluster)
|
||||
)
|
||||
.reverse();
|
||||
.sort((a: WorkflowRun, b: WorkflowRun) => {
|
||||
// Sorting based on unique fields
|
||||
if (sortData.name.sort) {
|
||||
const x = a.workflow_name;
|
||||
const y = b.workflow_name;
|
||||
|
||||
return sortData.name.ascending
|
||||
? sortAlphaAsc(x, y)
|
||||
: sortAlphaDesc(x, y);
|
||||
}
|
||||
if (sortData.startDate.sort) {
|
||||
const x = parseInt(
|
||||
(JSON.parse(a.execution_data) as ExecutionData).startedAt,
|
||||
10
|
||||
);
|
||||
|
||||
const y = parseInt(
|
||||
(JSON.parse(b.execution_data) as ExecutionData).startedAt,
|
||||
10
|
||||
);
|
||||
|
||||
return sortData.startDate.ascending
|
||||
? sortNumAsc(y, x)
|
||||
: sortNumDesc(y, x);
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
const deleteRow = () => {
|
||||
// Delete Mutation Here
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<section className="Heading section">
|
||||
<div className={classes.headerSection}>
|
||||
{/* Search Field */}
|
||||
<InputBase
|
||||
id="input-with-icon-adornment"
|
||||
placeholder="Search"
|
||||
|
@ -88,59 +142,126 @@ const ScheduleWorkflow = () => {
|
|||
</InputAdornment>
|
||||
}
|
||||
/>
|
||||
<FormControl className={classes.select}>
|
||||
<InputLabel id="demo-simple-select-outlined-label">
|
||||
{/* Select Cluster */}
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
className={classes.formControl}
|
||||
color="secondary"
|
||||
focused
|
||||
>
|
||||
<InputLabel className={classes.selectText}>
|
||||
Target Cluster
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId="demo-simple-select-outlined-label"
|
||||
id="demo-simple-select-outlined"
|
||||
value={filter.cluster}
|
||||
onChange={(event) =>
|
||||
setFilter({ ...filter, cluster: event.target.value as string })
|
||||
}
|
||||
disableUnderline
|
||||
label="Target Cluster"
|
||||
className={classes.selectText}
|
||||
>
|
||||
<MenuItem value="All">
|
||||
<Typography className={classes.menuItem}>All</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem value="Predefined">
|
||||
<Typography className={classes.menuItem}>
|
||||
Cluset pre-defined
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem value="Kubernetes">
|
||||
<Typography className={classes.menuItem}>
|
||||
Kubernetes Cluster
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem value="All">All</MenuItem>
|
||||
<MenuItem value="Predefined">Cluset pre-defined</MenuItem>
|
||||
<MenuItem value="Kubernetes">Kubernetes Cluster</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</div>
|
||||
</section>
|
||||
<section className="table section">
|
||||
{/* Table Header */}
|
||||
<TableContainer className={classes.tableMain}>
|
||||
<Table stickyHeader aria-label="simple table">
|
||||
<TableHead>
|
||||
<TableRow className={classes.tableHead}>
|
||||
{/* WorkFlow Name */}
|
||||
<TableCell className={classes.workflowName}>
|
||||
<Typography style={{ paddingLeft: 65 }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
||||
<Typography style={{ paddingLeft: 65, paddingTop: 10 }}>
|
||||
Workflow Name
|
||||
</Typography>
|
||||
<div className={classes.sortDiv}>
|
||||
<IconButton
|
||||
aria-label="sort name ascending"
|
||||
size="small"
|
||||
onClick={() =>
|
||||
setSortData({
|
||||
...sortData,
|
||||
name: { sort: true, ascending: true },
|
||||
startDate: { sort: false, ascending: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandLessIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
aria-label="sort name descending"
|
||||
size="small"
|
||||
onClick={() =>
|
||||
setSortData({
|
||||
...sortData,
|
||||
name: { sort: true, ascending: false },
|
||||
startDate: { sort: false, ascending: false },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandMoreIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</TableCell>
|
||||
|
||||
{/* Starting Date */}
|
||||
<TableCell className={classes.headerStatus}>
|
||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
||||
<Typography style={{ paddingTop: 10 }}>
|
||||
Starting Date
|
||||
</Typography>
|
||||
<div className={classes.sortDiv}>
|
||||
<IconButton
|
||||
aria-label="sort last run ascending"
|
||||
size="small"
|
||||
onClick={() =>
|
||||
setSortData({
|
||||
...sortData,
|
||||
startDate: { sort: true, ascending: true },
|
||||
name: { sort: false, ascending: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandLessIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
aria-label="sort last run descending"
|
||||
size="small"
|
||||
onClick={() =>
|
||||
setSortData({
|
||||
...sortData,
|
||||
startDate: { sort: true, ascending: false },
|
||||
name: { sort: false, ascending: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandMoreIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</TableCell>
|
||||
|
||||
{/* Regularity */}
|
||||
<TableCell>
|
||||
<Typography className={classes.regularity}>
|
||||
Regularity
|
||||
</Typography>
|
||||
</TableCell>
|
||||
|
||||
{/* Cluster */}
|
||||
<TableCell>
|
||||
<Typography className={classes.targetCluster}>
|
||||
Cluster
|
||||
</Typography>
|
||||
</TableCell>
|
||||
|
||||
{/* Show Experiments */}
|
||||
<TableCell>
|
||||
<Typography className={classes.showExp}>
|
||||
Show Experiments
|
||||
|
@ -169,9 +290,9 @@ const ScheduleWorkflow = () => {
|
|||
paginationData.pageNo * paginationData.rowsPerPage +
|
||||
paginationData.rowsPerPage
|
||||
)
|
||||
.map((data: any) => (
|
||||
.map((data) => (
|
||||
<TableRow key={data.workflow_run_id}>
|
||||
<TableData data={data} />
|
||||
<TableData data={data} deleteRow={deleteRow} />
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
|
@ -184,6 +305,8 @@ const ScheduleWorkflow = () => {
|
|||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
|
||||
{/* Pagination Section */}
|
||||
<TablePagination
|
||||
rowsPerPageOptions={[5, 10, 25]}
|
||||
component="div"
|
||||
|
|
|
@ -1,27 +1,39 @@
|
|||
import { makeStyles } from '@material-ui/core';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
// 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;
|
||||
|
|
Loading…
Reference in New Issue