chore(litmus-portal): Subscription fixes, added functionality to workflow table and created the schedules table. (#1927)
This commit will add the following things: - Subscription fixes in the Workflow Table. - Added search, filter and sort functionality in Workflow Table. - Created the Schedules Table and a separate route to edit the scheduling of a workflow. - Added Types for WorkflowRuns - Changes in GraphQL schema in frontend. Signed-off-by: Amit Kumar Das <amitkumar.das@mayadata.io> Co-authored-by: arkajyotiMukherjee <arkajyoti.mukherjee@mayadata.io>
This commit is contained in:
parent
ade9285b5a
commit
34a68a5669
|
@ -18,6 +18,9 @@
|
|||
}
|
||||
},
|
||||
"rules": {
|
||||
"no-nested-ternary": 0,
|
||||
"no-plusplus": 0,
|
||||
"dot-notation": 0,
|
||||
"camelcase": 0,
|
||||
"jsx-a11y/href-no-hash": ["off"],
|
||||
"react/jsx-props-no-spreading": ["off"],
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.21875 7.29492H3.28125C3.03855 7.29492 2.8418 7.49168 2.8418 7.73438C2.8418 7.97707 3.03855 8.17383 3.28125 8.17383H4.21875C4.46145 8.17383 4.6582 7.97707 4.6582 7.73438C4.6582 7.49168 4.46145 7.29492 4.21875 7.29492Z" fill="black" fill-opacity="0.4"/>
|
||||
<path d="M4.21875 9.16992H3.28125C3.03855 9.16992 2.8418 9.36668 2.8418 9.60938C2.8418 9.85207 3.03855 10.0488 3.28125 10.0488H4.21875C4.46145 10.0488 4.6582 9.85207 4.6582 9.60938C4.6582 9.36668 4.46145 9.16992 4.21875 9.16992Z" fill="black" fill-opacity="0.4"/>
|
||||
<path d="M4.21875 11.0449H3.28125C3.03855 11.0449 2.8418 11.2417 2.8418 11.4844C2.8418 11.7271 3.03855 11.9238 3.28125 11.9238H4.21875C4.46145 11.9238 4.6582 11.7271 4.6582 11.4844C4.6582 11.2417 4.46145 11.0449 4.21875 11.0449Z" fill="black" fill-opacity="0.4"/>
|
||||
<path d="M7.96875 7.29492H7.03125C6.78855 7.29492 6.5918 7.49168 6.5918 7.73438C6.5918 7.97707 6.78855 8.17383 7.03125 8.17383H7.96875C8.21145 8.17383 8.4082 7.97707 8.4082 7.73438C8.4082 7.49168 8.21145 7.29492 7.96875 7.29492Z" fill="black" fill-opacity="0.4"/>
|
||||
<path d="M7.96875 9.16992H7.03125C6.78855 9.16992 6.5918 9.36668 6.5918 9.60938C6.5918 9.85207 6.78855 10.0488 7.03125 10.0488H7.96875C8.21145 10.0488 8.4082 9.85207 8.4082 9.60938C8.4082 9.36668 8.21145 9.16992 7.96875 9.16992Z" fill="black" fill-opacity="0.4"/>
|
||||
<path d="M7.96875 11.0449H7.03125C6.78855 11.0449 6.5918 11.2417 6.5918 11.4844C6.5918 11.7271 6.78855 11.9238 7.03125 11.9238H7.96875C8.21145 11.9238 8.4082 11.7271 8.4082 11.4844C8.4082 11.2417 8.21145 11.0449 7.96875 11.0449Z" fill="black" fill-opacity="0.4"/>
|
||||
<path d="M11.7188 7.29492H10.7812C10.5386 7.29492 10.3418 7.49168 10.3418 7.73438C10.3418 7.97707 10.5386 8.17383 10.7812 8.17383H11.7188C11.9614 8.17383 12.1582 7.97707 12.1582 7.73438C12.1582 7.49168 11.9614 7.29492 11.7188 7.29492Z" fill="black" fill-opacity="0.4"/>
|
||||
<path d="M11.7188 9.16992H10.7812C10.5386 9.16992 10.3418 9.36668 10.3418 9.60938C10.3418 9.85207 10.5386 10.0488 10.7812 10.0488H11.7188C11.9614 10.0488 12.1582 9.85207 12.1582 9.60938C12.1582 9.36668 11.9614 9.16992 11.7188 9.16992Z" fill="black" fill-opacity="0.4"/>
|
||||
<path d="M11.7188 11.0449H10.7812C10.5386 11.0449 10.3418 11.2417 10.3418 11.4844C10.3418 11.7271 10.5386 11.9238 10.7812 11.9238H11.7188C11.9614 11.9238 12.1582 11.7271 12.1582 11.4844C12.1582 11.2417 11.9614 11.0449 11.7188 11.0449Z" fill="black" fill-opacity="0.4"/>
|
||||
<path d="M13.6816 1.9043H12.627V1.17188C12.627 0.92918 12.4302 0.732422 12.1875 0.732422C11.9448 0.732422 11.748 0.92918 11.748 1.17188V1.9043H7.93945V1.17188C7.93945 0.92918 7.7427 0.732422 7.5 0.732422C7.2573 0.732422 7.06055 0.92918 7.06055 1.17188V1.9043H3.25195V1.17188C3.25195 0.92918 3.0552 0.732422 2.8125 0.732422C2.5698 0.732422 2.37305 0.92918 2.37305 1.17188V1.9043H1.31836C0.591416 1.9043 0 2.49571 0 3.22266V12.9492C0 13.6762 0.591416 14.2676 1.31836 14.2676H13.6816C14.4086 14.2676 15 13.6762 15 12.9492C15 12.6656 15 3.46948 15 3.22266C15 2.49571 14.4086 1.9043 13.6816 1.9043ZM0.878906 3.22266C0.878906 2.98034 1.07604 2.7832 1.31836 2.7832H2.37305V3.51562C2.37305 3.75832 2.5698 3.95508 2.8125 3.95508C3.0552 3.95508 3.25195 3.75832 3.25195 3.51562V2.7832H7.06055V3.51562C7.06055 3.75832 7.2573 3.95508 7.5 3.95508C7.7427 3.95508 7.93945 3.75832 7.93945 3.51562V2.7832H11.748V3.51562C11.748 3.75832 11.9448 3.95508 12.1875 3.95508C12.4302 3.95508 12.627 3.75832 12.627 3.51562V2.7832H13.6816C13.924 2.7832 14.1211 2.98034 14.1211 3.22266V4.95117H0.878906V3.22266ZM13.6816 13.3887H1.31836C1.07604 13.3887 0.878906 13.1915 0.878906 12.9492V5.83008H14.1211V12.9492C14.1211 13.1915 13.924 13.3887 13.6816 13.3887Z" fill="black" fill-opacity="0.4"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.7 KiB |
|
@ -0,0 +1,13 @@
|
|||
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M6.3797 3.62305C6.25035 3.62305 6.14551 3.72788 6.14551 3.85724V8.28347C6.14551 8.41273 6.25035 8.51766 6.3797 8.51766C6.50905 8.51766 6.61389 8.41273 6.61389 8.28347V3.85724C6.61389 3.72788 6.50905 3.62305 6.3797 3.62305Z" fill="black"/>
|
||||
<path d="M3.61603 3.62305C3.48667 3.62305 3.38184 3.72788 3.38184 3.85724V8.28347C3.38184 8.41273 3.48667 8.51766 3.61603 8.51766C3.74538 8.51766 3.85022 8.41273 3.85022 8.28347V3.85724C3.85022 3.72788 3.74538 3.62305 3.61603 3.62305Z" fill="black"/>
|
||||
<path d="M1.60166 2.9771V8.74708C1.60166 9.08812 1.72672 9.40839 1.94518 9.63819C2.16263 9.86863 2.46525 9.99945 2.78195 10H7.21293C7.52972 9.99945 7.83234 9.86863 8.0497 9.63819C8.26816 9.40839 8.39321 9.08812 8.39321 8.74708V2.9771C8.82747 2.86183 9.10887 2.4423 9.05078 1.99669C8.9926 1.55118 8.61304 1.21792 8.16369 1.21783H6.96465V0.925086C6.96602 0.678911 6.86868 0.442524 6.69441 0.268619C6.52014 0.0948051 6.28339 -0.00198181 6.03721 3.07708e-05H3.95767C3.71149 -0.00198181 3.47474 0.0948051 3.30047 0.268619C3.1262 0.442524 3.02886 0.678911 3.03023 0.925086V1.21783H1.83119C1.38184 1.21792 1.00228 1.55118 0.9441 1.99669C0.886009 2.4423 1.1674 2.86183 1.60166 2.9771ZM7.21293 9.53162H2.78195C2.38154 9.53162 2.07005 9.18765 2.07005 8.74708V2.99768H7.92483V8.74708C7.92483 9.18765 7.61334 9.53162 7.21293 9.53162ZM3.49861 0.925086C3.49706 0.803142 3.545 0.685772 3.63154 0.599689C3.71799 0.513605 3.83563 0.466309 3.95767 0.468413H6.03721C6.15925 0.466309 6.27689 0.513605 6.36334 0.599689C6.44988 0.685681 6.49782 0.803142 6.49626 0.925086V1.21783H3.49861V0.925086ZM1.83119 1.68621H8.16369C8.39651 1.68621 8.58523 1.87493 8.58523 2.10775C8.58523 2.34057 8.39651 2.5293 8.16369 2.5293H1.83119C1.59837 2.5293 1.40965 2.34057 1.40965 2.10775C1.40965 1.87493 1.59837 1.68621 1.83119 1.68621Z" fill="black"/>
|
||||
<path d="M4.99786 3.62305C4.86851 3.62305 4.76367 3.72788 4.76367 3.85724V8.28347C4.76367 8.41273 4.86851 8.51766 4.99786 8.51766C5.12721 8.51766 5.23205 8.41273 5.23205 8.28347V3.85724C5.23205 3.72788 5.12721 3.62305 4.99786 3.62305Z" fill="black"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="10" height="10" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,12 @@
|
|||
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M1 6.90039L0 10.0004L3.1 9.00039L1 6.90039Z" fill="black"/>
|
||||
<path d="M6.5811 1.30269L1.70215 6.18164L3.82344 8.30293L8.70239 3.42398L6.5811 1.30269Z" fill="black"/>
|
||||
<path d="M9.8498 1.55L8.4498 0.15C8.2498 -0.05 7.9498 -0.05 7.7498 0.15L7.2998 0.6L9.3998 2.7L9.8498 2.25C10.0498 2.05 10.0498 1.75 9.8498 1.55Z" fill="black"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="10" height="10" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 564 B |
|
@ -11,6 +11,7 @@ import CustomStatus from '../CustomStatus/Status';
|
|||
import LinearProgressBar from '../../ReturningHome/ProgressBar/LinearProgressBar';
|
||||
import useStyles from './styles';
|
||||
import timeDifferenceForDate from '../../../../utils/datesModifier';
|
||||
import { history } from '../../../../redux/configureStore';
|
||||
|
||||
interface TableDataProps {
|
||||
data: any;
|
||||
|
@ -35,7 +36,7 @@ const TableData: React.FC<TableDataProps> = ({ data }) => {
|
|||
<TableCell className={classes.headerStatus1}>
|
||||
<CustomStatus status={JSON.parse(data.execution_data).phase} />
|
||||
</TableCell>
|
||||
<TableCell className={classes.workflowName}>
|
||||
<TableCell className={classes.workflowNameData}>
|
||||
<Typography>
|
||||
<strong>{data.workflow_name}</strong>
|
||||
</Typography>
|
||||
|
@ -70,11 +71,7 @@ const TableData: React.FC<TableDataProps> = ({ data }) => {
|
|||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className={classes.timeDiv}>
|
||||
<Typography className={classes.timeData}>
|
||||
{timeDifferenceForDate(data.last_updated)}
|
||||
</Typography>
|
||||
</div>
|
||||
<Typography>{timeDifferenceForDate(data.last_updated)}</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<IconButton
|
||||
|
@ -93,7 +90,15 @@ const TableData: React.FC<TableDataProps> = ({ data }) => {
|
|||
open={open}
|
||||
onClose={handleClose}
|
||||
>
|
||||
<MenuItem value="Workflow" onClick={handleMenu}>
|
||||
<MenuItem
|
||||
value="Workflow"
|
||||
onClick={() =>
|
||||
history.push({
|
||||
pathname: '/workflow-underground',
|
||||
state: data,
|
||||
})
|
||||
}
|
||||
>
|
||||
Show the workflow
|
||||
</MenuItem>
|
||||
<MenuItem value="Analysis" onClick={handleMenu}>
|
||||
|
|
|
@ -1,66 +1,175 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import SearchIcon from '@material-ui/icons/Search';
|
||||
import {
|
||||
InputBase,
|
||||
InputAdornment,
|
||||
FormControl,
|
||||
IconButton,
|
||||
InputAdornment,
|
||||
InputBase,
|
||||
InputLabel,
|
||||
Select,
|
||||
MenuItem,
|
||||
Typography,
|
||||
TableContainer,
|
||||
Select,
|
||||
Table,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableCell,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TablePagination,
|
||||
TableRow,
|
||||
Typography,
|
||||
} from '@material-ui/core';
|
||||
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 { WORKFLOW_DETAILS, WORKFLOW_EVENTS } from '../../../../graphql';
|
||||
import {
|
||||
ExecutionData,
|
||||
Workflow,
|
||||
WorkflowDataVars,
|
||||
WorkflowRun,
|
||||
WorkflowSubscription,
|
||||
} from '../../../../models/workflowData';
|
||||
|
||||
import {
|
||||
sortAlphaAsc,
|
||||
sortAlphaDesc,
|
||||
sortNumAsc,
|
||||
sortNumDesc,
|
||||
} from '../../../../utils/sort';
|
||||
import Loader from '../../../Loader';
|
||||
import useStyles from './styles';
|
||||
import { history } from '../../../../redux/configureStore';
|
||||
import { WORKFLOW_DETAILS, WORKFLOW_EVENTS } from '../../../../schemas';
|
||||
import TableData from './TableData';
|
||||
|
||||
interface FilterOptions {
|
||||
search: string;
|
||||
status: string;
|
||||
cluster: string;
|
||||
}
|
||||
|
||||
interface PaginationData {
|
||||
pageNo: number;
|
||||
rowsPerPage: number;
|
||||
}
|
||||
|
||||
interface SortData {
|
||||
lastRun: { sort: boolean; ascending: boolean };
|
||||
name: { sort: boolean; ascending: boolean };
|
||||
noOfSteps: { sort: boolean; ascending: boolean };
|
||||
}
|
||||
|
||||
const BrowseWorkflow = () => {
|
||||
// Apollo query with subscribeToMore
|
||||
const { subscribeToMore, ...result } = useQuery(WORKFLOW_DETAILS);
|
||||
|
||||
// Default table data
|
||||
const [mainData, setMainData] = useState<any>();
|
||||
|
||||
useEffect(() => {
|
||||
// Get the inital table data
|
||||
setMainData(result.data);
|
||||
// Once Subscription is made, this is called
|
||||
subscribeToMore({
|
||||
document: WORKFLOW_EVENTS,
|
||||
updateQuery: (prev, { subscriptionData }) => {
|
||||
if (!subscriptionData.data) return setMainData(prev);
|
||||
const newData = subscriptionData.data.workflowEventListener;
|
||||
return setMainData({
|
||||
...prev,
|
||||
getWorkFlowRuns: [...prev.getWorkFlowRuns, newData],
|
||||
});
|
||||
},
|
||||
});
|
||||
}, [result.data]);
|
||||
const classes = useStyles();
|
||||
|
||||
const [search, setSearch] = React.useState<String>('');
|
||||
// Query to get workflows
|
||||
const { subscribeToMore, data, loading, error } = useQuery<
|
||||
Workflow,
|
||||
WorkflowDataVars
|
||||
>(WORKFLOW_DETAILS, { variables: { projectID: '00000' } });
|
||||
|
||||
const [status, setStatus] = React.useState<String>('');
|
||||
// Using subscription to get realtime data
|
||||
useEffect(() => {
|
||||
subscribeToMore<WorkflowSubscription>({
|
||||
document: WORKFLOW_EVENTS,
|
||||
variables: { projectID: '00000' },
|
||||
updateQuery: (prev, { subscriptionData }) => {
|
||||
if (!subscriptionData.data) return prev;
|
||||
const modifiedWorkflows = prev.getWorkFlowRuns.slice();
|
||||
const newWorkflow = subscriptionData.data.workflowEventListener;
|
||||
|
||||
const handleStatusChange = (event: React.ChangeEvent<{ value: unknown }>) => {
|
||||
setStatus(event.target.value as String);
|
||||
};
|
||||
// Updating the query data
|
||||
let i = 0;
|
||||
for (; i < modifiedWorkflows.length; i++) {
|
||||
if (
|
||||
modifiedWorkflows[i].workflow_run_id === newWorkflow.workflow_run_id
|
||||
) {
|
||||
modifiedWorkflows[i] = newWorkflow;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i === modifiedWorkflows.length)
|
||||
modifiedWorkflows.unshift(newWorkflow);
|
||||
|
||||
const [cluster, setCluster] = React.useState<String>('');
|
||||
return { ...prev, getWorkFlowRuns: modifiedWorkflows };
|
||||
},
|
||||
});
|
||||
}, [data]);
|
||||
|
||||
// States for filters
|
||||
const [filters, setFilters] = useState<FilterOptions>({
|
||||
search: '',
|
||||
status: 'All',
|
||||
cluster: 'All',
|
||||
});
|
||||
|
||||
// State for sorting
|
||||
const [sortData, setSortData] = useState<SortData>({
|
||||
lastRun: { sort: true, ascending: true },
|
||||
name: { sort: false, ascending: true },
|
||||
noOfSteps: { sort: false, ascending: false },
|
||||
});
|
||||
|
||||
// State for pagination
|
||||
const [paginationData, setPaginationData] = useState<PaginationData>({
|
||||
pageNo: 0,
|
||||
rowsPerPage: 5,
|
||||
});
|
||||
|
||||
const filteredData = data?.getWorkFlowRuns
|
||||
.filter((dataRow) =>
|
||||
dataRow.workflow_name.toLowerCase().includes(filters.search)
|
||||
)
|
||||
.filter((dataRow) =>
|
||||
filters.status === 'All'
|
||||
? true
|
||||
: (JSON.parse(dataRow.execution_data) as ExecutionData).phase.includes(
|
||||
filters.status
|
||||
)
|
||||
)
|
||||
.filter((dataRow) =>
|
||||
filters.cluster === 'All'
|
||||
? true
|
||||
: dataRow.cluster_name.toLowerCase().includes(filters.cluster)
|
||||
)
|
||||
.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.lastRun.sort) {
|
||||
const x = parseInt(a.last_updated, 10);
|
||||
const y = parseInt(b.last_updated, 10);
|
||||
|
||||
return sortData.lastRun.ascending
|
||||
? sortNumAsc(y, x)
|
||||
: sortNumDesc(y, x);
|
||||
}
|
||||
|
||||
return 0;
|
||||
})
|
||||
.sort((a: WorkflowRun, b: WorkflowRun) => {
|
||||
// Sorting based on non-unique fields
|
||||
if (sortData.noOfSteps.sort) {
|
||||
const x = Object.keys(
|
||||
(JSON.parse(a.execution_data) as ExecutionData).nodes
|
||||
).length;
|
||||
|
||||
const y = Object.keys(
|
||||
(JSON.parse(b.execution_data) as ExecutionData).nodes
|
||||
).length;
|
||||
|
||||
return sortData.noOfSteps.ascending
|
||||
? sortNumAsc(x, y)
|
||||
: sortNumDesc(x, y);
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
const handleClusterChange = (
|
||||
event: React.ChangeEvent<{ value: unknown }>
|
||||
) => {
|
||||
setCluster(event.target.value as String);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<section className="Heading section">
|
||||
|
@ -69,8 +178,11 @@ const BrowseWorkflow = () => {
|
|||
id="input-with-icon-adornment"
|
||||
placeholder="Search"
|
||||
className={classes.search}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
value={filters.search}
|
||||
onChange={(e) => {
|
||||
setFilters({ ...filters, search: e.target.value as string });
|
||||
setPaginationData({ ...paginationData, pageNo: 0 });
|
||||
}}
|
||||
startAdornment={
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
|
@ -84,18 +196,24 @@ const BrowseWorkflow = () => {
|
|||
<Select
|
||||
labelId="demo-simple-select-outlined-label"
|
||||
id="demo-simple-select-outlined"
|
||||
value={status}
|
||||
onChange={handleStatusChange}
|
||||
value={filters.status}
|
||||
onChange={(e) => {
|
||||
setFilters({ ...filters, status: e.target.value as string });
|
||||
setPaginationData({ ...paginationData, pageNo: 0 });
|
||||
}}
|
||||
disableUnderline
|
||||
>
|
||||
<MenuItem value="">
|
||||
<Typography className={classes.menuItem}>None</Typography>
|
||||
<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="Completed">
|
||||
<Typography className={classes.menuItem}>Completed</Typography>
|
||||
<MenuItem value="Succeeded">
|
||||
<Typography className={classes.menuItem}>Succeeded</Typography>
|
||||
</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
@ -107,12 +225,15 @@ const BrowseWorkflow = () => {
|
|||
<Select
|
||||
labelId="demo-simple-select-outlined-label"
|
||||
id="demo-simple-select-outlined"
|
||||
value={cluster}
|
||||
onChange={handleClusterChange}
|
||||
value={filters.cluster}
|
||||
onChange={(e) => {
|
||||
setFilters({ ...filters, cluster: e.target.value as string });
|
||||
setPaginationData({ ...paginationData, pageNo: 0 });
|
||||
}}
|
||||
disableUnderline
|
||||
>
|
||||
<MenuItem value="">
|
||||
<Typography className={classes.menuItem}>None</Typography>
|
||||
<MenuItem value="All">
|
||||
<Typography className={classes.menuItem}>All</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem value="Predefined">
|
||||
<Typography className={classes.menuItem}>
|
||||
|
@ -126,9 +247,6 @@ const BrowseWorkflow = () => {
|
|||
</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<Typography variant="subtitle1" className={classes.headerText}>
|
||||
for the period
|
||||
</Typography>
|
||||
<FormControl className={classes.select}>
|
||||
<InputLabel id="demo-simple-select-outlined-label">Date</InputLabel>
|
||||
<Select
|
||||
|
@ -143,44 +261,191 @@ const BrowseWorkflow = () => {
|
|||
</section>
|
||||
<section className="table section">
|
||||
<TableContainer className={classes.tableMain}>
|
||||
<Table aria-label="simple table">
|
||||
<TableHead>
|
||||
<TableRow className={classes.tableHead}>
|
||||
<Table stickyHeader aria-label="simple table">
|
||||
<TableHead className={classes.tableHead}>
|
||||
<TableRow>
|
||||
{/* Status */}
|
||||
<TableCell className={classes.headerStatus}>Status</TableCell>
|
||||
|
||||
{/* Workflow Name */}
|
||||
<TableCell className={classes.workflowName}>
|
||||
Workflow Name
|
||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
||||
<Typography style={{ 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 },
|
||||
lastRun: { sort: false, ascending: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandLessIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
aria-label="sort name descending"
|
||||
size="small"
|
||||
onClick={() =>
|
||||
setSortData({
|
||||
...sortData,
|
||||
name: { sort: true, ascending: false },
|
||||
lastRun: { sort: false, ascending: false },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandMoreIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</TableCell>
|
||||
|
||||
{/* Target Cluster */}
|
||||
<TableCell>
|
||||
<Typography className={classes.targetCluster}>
|
||||
Target Cluster
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>Reliability Details</TableCell>
|
||||
<TableCell># of experiments</TableCell>
|
||||
<TableCell>Last Run</TableCell>
|
||||
|
||||
{/* Reliability */}
|
||||
<TableCell className={classes.headData}>
|
||||
Reliability Details
|
||||
</TableCell>
|
||||
|
||||
{/* No of Experiments */}
|
||||
<TableCell className={classes.headData}>
|
||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
||||
<Typography style={{ paddingTop: 10 }}>
|
||||
# of Steps
|
||||
</Typography>
|
||||
<div className={classes.sortDiv}>
|
||||
<IconButton
|
||||
aria-label="sort no of steps ascending"
|
||||
size="small"
|
||||
onClick={() =>
|
||||
setSortData({
|
||||
...sortData,
|
||||
noOfSteps: { sort: true, ascending: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandLessIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
aria-label="sort no of steps descending"
|
||||
size="small"
|
||||
onClick={() =>
|
||||
setSortData({
|
||||
...sortData,
|
||||
noOfSteps: { sort: true, ascending: false },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandMoreIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</TableCell>
|
||||
|
||||
{/* Last Run */}
|
||||
<TableCell className={classes.headData}>
|
||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
||||
<Typography style={{ paddingTop: 10 }}>Last Run</Typography>
|
||||
<div className={classes.sortDiv}>
|
||||
<IconButton
|
||||
aria-label="sort last run ascending"
|
||||
size="small"
|
||||
onClick={() =>
|
||||
setSortData({
|
||||
...sortData,
|
||||
lastRun: { 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,
|
||||
lastRun: { sort: true, ascending: false },
|
||||
name: { sort: false, ascending: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<ExpandMoreIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</TableCell>
|
||||
|
||||
{/* Menu */}
|
||||
<TableCell />
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
|
||||
{/* Body */}
|
||||
<TableBody>
|
||||
{mainData &&
|
||||
mainData.getWorkFlowRuns
|
||||
.slice(0)
|
||||
.reverse()
|
||||
.map((data: any) => (
|
||||
<TableRow
|
||||
onClick={() =>
|
||||
history.push({
|
||||
pathname: '/workflow-underground',
|
||||
state: data,
|
||||
})
|
||||
}
|
||||
>
|
||||
<TableData data={data} />
|
||||
{loading ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={7}>
|
||||
<Loader />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : error ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={7}>
|
||||
<Typography align="center">Unable to fetch data</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : filteredData && filteredData.length ? (
|
||||
filteredData
|
||||
.slice(
|
||||
paginationData.pageNo * paginationData.rowsPerPage,
|
||||
paginationData.pageNo * paginationData.rowsPerPage +
|
||||
paginationData.rowsPerPage
|
||||
)
|
||||
.map((dataRow) => (
|
||||
<TableRow key={dataRow.workflow_run_id}>
|
||||
<TableData data={dataRow} />
|
||||
</TableRow>
|
||||
))}
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={7}>
|
||||
<Typography align="center">No records available</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
|
||||
{/* Pagination */}
|
||||
<TablePagination
|
||||
rowsPerPageOptions={[5, 10, 25]}
|
||||
component="div"
|
||||
count={filteredData?.length ?? 0}
|
||||
rowsPerPage={paginationData.rowsPerPage}
|
||||
page={paginationData.pageNo}
|
||||
onChangePage={(_, page) =>
|
||||
setPaginationData({ ...paginationData, pageNo: page })
|
||||
}
|
||||
onChangeRowsPerPage={(event) =>
|
||||
setPaginationData({
|
||||
...paginationData,
|
||||
pageNo: 0,
|
||||
rowsPerPage: parseInt(event.target.value, 10),
|
||||
})
|
||||
}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -35,12 +35,14 @@ const useStyles = makeStyles((theme) => ({
|
|||
marginTop: theme.spacing(6.25),
|
||||
border: '1px solid rgba(0,0,0,0.1)',
|
||||
backgroundColor: theme.palette.common.white,
|
||||
height: '29.219rem',
|
||||
},
|
||||
tableHead: {
|
||||
opacity: 0.5,
|
||||
opacity: 1,
|
||||
},
|
||||
headerStatus: {
|
||||
paddingLeft: theme.spacing(10),
|
||||
color: 'rgba(0, 0, 0, 0.4)',
|
||||
},
|
||||
headerStatus1: {
|
||||
paddingLeft: theme.spacing(8),
|
||||
|
@ -56,9 +58,17 @@ const useStyles = makeStyles((theme) => ({
|
|||
},
|
||||
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)',
|
||||
},
|
||||
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),
|
||||
|
@ -69,9 +79,6 @@ const useStyles = makeStyles((theme) => ({
|
|||
stepsData: {
|
||||
paddingLeft: theme.spacing(3.75),
|
||||
},
|
||||
timeData: {
|
||||
paddingTop: theme.spacing(1.25),
|
||||
},
|
||||
optionBtn: {
|
||||
marginLeft: 'auto',
|
||||
},
|
||||
|
@ -79,6 +86,11 @@ const useStyles = makeStyles((theme) => ({
|
|||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
sortDiv: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
paddingLeft: theme.spacing(1.25),
|
||||
},
|
||||
}));
|
||||
|
||||
export default useStyles;
|
||||
|
|
|
@ -0,0 +1,200 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
TableCell,
|
||||
Typography,
|
||||
IconButton,
|
||||
MenuItem,
|
||||
Menu,
|
||||
Popover,
|
||||
Button,
|
||||
} from '@material-ui/core';
|
||||
import MoreVertIcon from '@material-ui/icons/MoreVert';
|
||||
import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
|
||||
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
|
||||
import useStyles from './styles';
|
||||
import LinearProgressBar from '../../ReturningHome/ProgressBar/LinearProgressBar';
|
||||
import { history } from '../../../../redux/configureStore';
|
||||
|
||||
interface TableDataProps {
|
||||
data: any;
|
||||
}
|
||||
|
||||
const TableData: React.FC<TableDataProps> = ({ data }) => {
|
||||
const classes = useStyles();
|
||||
|
||||
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 handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
const handleMenu = () => {};
|
||||
|
||||
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}`;
|
||||
return resDate;
|
||||
};
|
||||
const exWeight = [
|
||||
{
|
||||
name: 'Node add test',
|
||||
value: 10,
|
||||
},
|
||||
{
|
||||
name: 'Networking pod test',
|
||||
value: 7,
|
||||
},
|
||||
{
|
||||
name: 'Config map test',
|
||||
value: 3,
|
||||
},
|
||||
{
|
||||
name: 'Proxy-service-test',
|
||||
value: 5,
|
||||
},
|
||||
];
|
||||
return (
|
||||
<>
|
||||
<TableCell align="center" className={classes.workflowNameData}>
|
||||
<Typography>
|
||||
<strong>{data.workflow_name}</strong>
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Typography className={classes.clusterStartDate}>
|
||||
{formatDate(JSON.parse(data.execution_data).startedAt)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className={classes.regularityData}>
|
||||
<div className={classes.expDiv}>
|
||||
<img src="/icons/calender.svg" alt="Calender" />
|
||||
<Typography style={{ paddingLeft: 10 }}>Once </Typography>
|
||||
</div>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Typography>{data.cluster_name}</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Button onClick={handlePopOverClick} style={{ textTransform: 'none' }}>
|
||||
{isOpen ? (
|
||||
<div className={classes.expDiv}>
|
||||
<Typography className={classes.expInfo}>
|
||||
<strong>Show Experiment</strong>
|
||||
</Typography>
|
||||
<KeyboardArrowDownIcon className={classes.expInfo} />
|
||||
</div>
|
||||
) : (
|
||||
<div className={classes.expDiv1}>
|
||||
<Typography>
|
||||
<strong>Show Experiment</strong>
|
||||
</Typography>
|
||||
<ChevronRightIcon />
|
||||
</div>
|
||||
)}
|
||||
</Button>
|
||||
<Popover
|
||||
id={id}
|
||||
open={isOpen}
|
||||
anchorEl={popAnchorEl}
|
||||
onClose={handlePopOverClose}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
style={{
|
||||
marginTop: 10,
|
||||
}}
|
||||
>
|
||||
<div className={classes.weightDiv}>
|
||||
{exWeight.map((expData) => {
|
||||
return (
|
||||
<div style={{ marginBottom: 10 }}>
|
||||
<div className={classes.weightInfo}>
|
||||
<Typography>{expData.name}</Typography>
|
||||
<Typography style={{ marginLeft: 'auto' }}>
|
||||
{expData.value} points
|
||||
</Typography>
|
||||
</div>
|
||||
<LinearProgressBar value={expData.value} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Popover>
|
||||
</TableCell>
|
||||
<TableCell className={classes.menuCell}>
|
||||
<IconButton
|
||||
aria-label="more"
|
||||
aria-controls="long-menu"
|
||||
aria-haspopup="true"
|
||||
onClick={handleClick}
|
||||
className={classes.optionBtn}
|
||||
>
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
<Menu
|
||||
id="long-menu"
|
||||
anchorEl={anchorEl}
|
||||
keepMounted
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
>
|
||||
<MenuItem
|
||||
value="Workflow"
|
||||
onClick={() =>
|
||||
history.push({
|
||||
pathname: '/schedule',
|
||||
state: data,
|
||||
})
|
||||
}
|
||||
>
|
||||
<div className={classes.expDiv}>
|
||||
<img
|
||||
src="/icons/editSchedule.svg"
|
||||
alt="Edit Schedule"
|
||||
className={classes.btnImg}
|
||||
/>
|
||||
<Typography className={classes.btnText}>Edit Schedule</Typography>
|
||||
</div>
|
||||
</MenuItem>
|
||||
<MenuItem value="Analysis" onClick={handleMenu}>
|
||||
<div className={classes.expDiv}>
|
||||
<img
|
||||
src="/icons/deleteSchedule.svg"
|
||||
alt="Delete Schedule"
|
||||
className={classes.btnImg}
|
||||
/>
|
||||
<Typography className={classes.btnText}>
|
||||
Delete Schedule
|
||||
</Typography>
|
||||
</div>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</TableCell>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default TableData;
|
|
@ -0,0 +1,204 @@
|
|||
import React, { useState } from 'react';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import SearchIcon from '@material-ui/icons/Search';
|
||||
import {
|
||||
InputBase,
|
||||
InputAdornment,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
Select,
|
||||
MenuItem,
|
||||
Typography,
|
||||
TableContainer,
|
||||
Table,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableCell,
|
||||
TableBody,
|
||||
TablePagination,
|
||||
} from '@material-ui/core';
|
||||
import useStyles from './styles';
|
||||
import { WORKFLOW_DETAILS } from '../../../../graphql';
|
||||
import TableData from './TableData';
|
||||
import { Workflow, WorkflowDataVars } from '../../../../models/workflowData';
|
||||
import Loader from '../../../Loader';
|
||||
|
||||
interface FilterOption {
|
||||
search: string;
|
||||
cluster: string;
|
||||
}
|
||||
|
||||
interface PaginationData {
|
||||
pageNo: number;
|
||||
rowsPerPage: number;
|
||||
}
|
||||
|
||||
const ScheduleWorkflow = () => {
|
||||
const classes = useStyles();
|
||||
|
||||
// Apollo query to get the scheduled data
|
||||
const { data, loading, error } = useQuery<Workflow, WorkflowDataVars>(
|
||||
WORKFLOW_DETAILS,
|
||||
{ variables: { projectID: '00000' } }
|
||||
);
|
||||
|
||||
// State for search and filtering
|
||||
const [filter, setFilter] = React.useState<FilterOption>({
|
||||
search: '',
|
||||
cluster: 'All',
|
||||
});
|
||||
|
||||
// State for pagination
|
||||
const [paginationData, setPaginationData] = useState<PaginationData>({
|
||||
pageNo: 0,
|
||||
rowsPerPage: 5,
|
||||
});
|
||||
|
||||
const filteredData = data?.getWorkFlowRuns
|
||||
.filter((dataRow) =>
|
||||
dataRow.workflow_name.toLowerCase().includes(filter.search)
|
||||
)
|
||||
.filter((dataRow) =>
|
||||
filter.cluster === 'All'
|
||||
? true
|
||||
: dataRow.cluster_name.toLowerCase().includes(filter.cluster)
|
||||
)
|
||||
.reverse();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<section className="Heading section">
|
||||
<div className={classes.headerSection}>
|
||||
<InputBase
|
||||
id="input-with-icon-adornment"
|
||||
placeholder="Search"
|
||||
className={classes.search}
|
||||
value={filter.search}
|
||||
onChange={(event) =>
|
||||
setFilter({ ...filter, search: event.target.value as string })
|
||||
}
|
||||
startAdornment={
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
}
|
||||
/>
|
||||
<FormControl className={classes.select}>
|
||||
<InputLabel id="demo-simple-select-outlined-label">
|
||||
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
|
||||
>
|
||||
<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>
|
||||
</div>
|
||||
</section>
|
||||
<section className="table section">
|
||||
<TableContainer className={classes.tableMain}>
|
||||
<Table stickyHeader aria-label="simple table">
|
||||
<TableHead>
|
||||
<TableRow className={classes.tableHead}>
|
||||
<TableCell className={classes.workflowName}>
|
||||
<Typography style={{ paddingLeft: 65 }}>
|
||||
Workflow Name
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell className={classes.headerStatus}>
|
||||
Starting Date
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Typography className={classes.regularity}>
|
||||
Regularity
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Typography className={classes.targetCluster}>
|
||||
Cluster
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Typography className={classes.showExp}>
|
||||
Show Experiments
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell />
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{loading ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={7}>
|
||||
<Loader />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : error ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={7}>
|
||||
<Typography align="center">Unable to fetch data</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : filteredData && filteredData.length ? (
|
||||
filteredData
|
||||
.slice(
|
||||
paginationData.pageNo * paginationData.rowsPerPage,
|
||||
paginationData.pageNo * paginationData.rowsPerPage +
|
||||
paginationData.rowsPerPage
|
||||
)
|
||||
.map((data: any) => (
|
||||
<TableRow key={data.workflow_run_id}>
|
||||
<TableData data={data} />
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={7}>
|
||||
<Typography align="center">No records available</Typography>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<TablePagination
|
||||
rowsPerPageOptions={[5, 10, 25]}
|
||||
component="div"
|
||||
count={filteredData?.length ?? 0}
|
||||
rowsPerPage={paginationData.rowsPerPage}
|
||||
page={paginationData.pageNo}
|
||||
onChangePage={(_, page) =>
|
||||
setPaginationData({ ...paginationData, pageNo: page })
|
||||
}
|
||||
onChangeRowsPerPage={(event) => {
|
||||
setPaginationData({
|
||||
...paginationData,
|
||||
pageNo: 0,
|
||||
rowsPerPage: parseInt(event.target.value, 10),
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ScheduleWorkflow;
|
|
@ -0,0 +1,128 @@
|
|||
import { makeStyles } from '@material-ui/core';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
headerSection: {
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignContent: 'center',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
border: '1px solid ',
|
||||
borderColor: theme.palette.text.hint,
|
||||
backgroundColor: theme.palette.common.white,
|
||||
},
|
||||
search: {
|
||||
marginRight: 'auto',
|
||||
marginLeft: theme.spacing(6.25),
|
||||
},
|
||||
select: {
|
||||
width: '10.375rem',
|
||||
marginLeft: theme.spacing(1.25),
|
||||
paddingBottom: theme.spacing(2.5),
|
||||
marginRight: theme.spacing(3.75),
|
||||
},
|
||||
headerText: {
|
||||
marginLeft: theme.spacing(3.75),
|
||||
color: theme.palette.text.disabled,
|
||||
paddingBottom: theme.spacing(0.625),
|
||||
},
|
||||
tableMain: {
|
||||
marginTop: theme.spacing(6.25),
|
||||
border: '1px solid rgba(0,0,0,0.1)',
|
||||
backgroundColor: theme.palette.common.white,
|
||||
height: '29.180rem',
|
||||
},
|
||||
tableHead: {
|
||||
opacity: 1,
|
||||
height: '4.6875rem',
|
||||
},
|
||||
headerStatus: {
|
||||
paddingLeft: theme.spacing(10),
|
||||
color: theme.palette.customColors.black(0.4),
|
||||
},
|
||||
headerStatus1: {
|
||||
paddingLeft: theme.spacing(8),
|
||||
},
|
||||
progressBar: {
|
||||
width: '6.5rem',
|
||||
},
|
||||
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)',
|
||||
},
|
||||
regularity: {
|
||||
paddingLeft: theme.spacing(3.75),
|
||||
color: theme.palette.customColors.black(0.4),
|
||||
},
|
||||
targetCluster: {
|
||||
color: theme.palette.customColors.black(0.4),
|
||||
},
|
||||
clusterStartDate: {
|
||||
paddingLeft: theme.spacing(8),
|
||||
},
|
||||
regularityData: {
|
||||
maxWidth: '12.5rem',
|
||||
paddingLeft: theme.spacing(4),
|
||||
},
|
||||
stepsData: {
|
||||
paddingLeft: theme.spacing(3.75),
|
||||
},
|
||||
expInfo: {
|
||||
color: theme.palette.primary.dark,
|
||||
},
|
||||
showExp: {
|
||||
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),
|
||||
},
|
||||
optionBtn: {
|
||||
marginLeft: theme.spacing(-6.25),
|
||||
},
|
||||
menuCell: {
|
||||
width: '3.125rem',
|
||||
},
|
||||
timeDiv: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
btnImg: {
|
||||
width: '0.8125rem',
|
||||
height: '0.8125rem',
|
||||
marginTop: theme.spacing(0.375),
|
||||
},
|
||||
btnText: {
|
||||
paddingLeft: theme.spacing(1.625),
|
||||
},
|
||||
}));
|
||||
|
||||
export default useStyles;
|
|
@ -5,9 +5,9 @@ import Tabs from '@material-ui/core/Tabs';
|
|||
import React from 'react';
|
||||
import { history } from '../../../../redux/configureStore';
|
||||
import ButtonFilled from '../../../Button/ButtonFilled';
|
||||
import Loader from '../../../Loader';
|
||||
import BrowseWorkflow from '../BrowseWorkflow';
|
||||
import useStyles from './styles';
|
||||
import ScheduleWorkflow from '../ScheduleWorkflow';
|
||||
import Templates from '../Templates';
|
||||
|
||||
interface TabPanelProps {
|
||||
|
@ -86,7 +86,7 @@ const CenteredTabs = () => {
|
|||
<BrowseWorkflow />
|
||||
</TabPanel>
|
||||
<TabPanel value={value} index={1}>
|
||||
<Loader />
|
||||
<ScheduleWorkflow />
|
||||
</TabPanel>
|
||||
<TabPanel value={value} index={2}>
|
||||
<Templates />
|
||||
|
|
|
@ -11,7 +11,7 @@ import { useSelector } from 'react-redux';
|
|||
import ButtonFilled from '../../../Button/ButtonFilled';
|
||||
import ButtonOutLine from '../../../Button/ButtonOutline';
|
||||
import useStyles from './styles';
|
||||
import { GET_CLUSTER } from '../../../../schemas';
|
||||
import { GET_CLUSTER } from '../../../../graphql';
|
||||
import { RootState } from '../../../../redux/reducers';
|
||||
import { UserData } from '../../../../models/user';
|
||||
import useActions from '../../../../redux/actions';
|
||||
|
|
|
@ -5,8 +5,8 @@ import { useSelector } from 'react-redux';
|
|||
import { Link } from 'react-router-dom';
|
||||
import ButtonFilled from '../Button/ButtonFilled';
|
||||
import config from '../../config';
|
||||
import { CREATE_USER } from '../../graphql';
|
||||
import { RootState } from '../../redux/reducers';
|
||||
import { CREATE_USER } from '../../schemas';
|
||||
import InputField from '../InputField';
|
||||
import ModalPage from './Modalpage';
|
||||
import useStyles from './styles';
|
||||
|
|
|
@ -23,7 +23,7 @@ import { RootState } from '../../redux/reducers';
|
|||
import useActions from '../../redux/actions';
|
||||
import * as WorkflowActions from '../../redux/actions/workflow';
|
||||
import parsed from '../../utils/yamlUtils';
|
||||
import { CREATE_WORKFLOW } from '../../schemas';
|
||||
import { CREATE_WORKFLOW } from '../../graphql';
|
||||
import Unimodal from '../../containers/layouts/Unimodal';
|
||||
import { history } from '../../redux/configureStore';
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ const BrowseTemplate = lazy(() =>
|
|||
const HomePage = lazy(() => import('../../pages/HomePage'));
|
||||
const Community = lazy(() => import('../../pages/Community'));
|
||||
const Settings = lazy(() => import('../../pages/Settings'));
|
||||
|
||||
const SchedulePage = lazy(() => import('../../pages/SchedulePage'));
|
||||
interface RoutesProps {
|
||||
userData: string;
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ const Routes: React.FC<RoutesProps> = ({ userData }) => {
|
|||
<Route exact path="/login" component={LoginPage} />
|
||||
<Route exact path="/workflows" component={Workflows} />
|
||||
<Route exact path="/create-workflow" component={CreateWorkflow} />
|
||||
<Route exact path="/schedule" component={SchedulePage} />
|
||||
<Route
|
||||
exact
|
||||
path="/workflow-underground"
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { gql } from '@apollo/client';
|
||||
|
||||
export const WORKFLOW_DETAILS = gql`
|
||||
query {
|
||||
getWorkFlowRuns(project_id: "00002") {
|
||||
query workflowDetails($projectID: String!) {
|
||||
getWorkFlowRuns(project_id: $projectID) {
|
||||
workflow_id
|
||||
workflow_name
|
||||
workflow_run_id
|
||||
|
@ -15,8 +15,8 @@ export const WORKFLOW_DETAILS = gql`
|
|||
`;
|
||||
|
||||
export const WORKFLOW_EVENTS = gql`
|
||||
subscription {
|
||||
workflowEventListener(project_id: "00002") {
|
||||
subscription workflowEvents($projectID: String!) {
|
||||
workflowEventListener(project_id: $projectID) {
|
||||
workflow_id
|
||||
workflow_name
|
||||
workflow_run_id
|
|
@ -0,0 +1,42 @@
|
|||
export interface Node {
|
||||
children: string[] | null;
|
||||
finishedAt: string;
|
||||
name: string;
|
||||
phase: string;
|
||||
startedAt: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface ExecutionData {
|
||||
event_type: string;
|
||||
uid: string;
|
||||
namespace: string;
|
||||
name: string;
|
||||
creationTimestamp: string;
|
||||
phase: string;
|
||||
startedAt: string;
|
||||
finishedAt: string;
|
||||
nodes: object;
|
||||
}
|
||||
|
||||
export interface WorkflowRun {
|
||||
cluster_name: string;
|
||||
execution_data: string;
|
||||
last_updated: string;
|
||||
project_id: string;
|
||||
workflow_id: string;
|
||||
workflow_name: string;
|
||||
workflow_run_id: string;
|
||||
}
|
||||
|
||||
export interface Workflow {
|
||||
getWorkFlowRuns: WorkflowRun[];
|
||||
}
|
||||
|
||||
export interface WorkflowSubscription {
|
||||
workflowEventListener: WorkflowRun;
|
||||
}
|
||||
|
||||
export interface WorkflowDataVars {
|
||||
projectID: string;
|
||||
}
|
|
@ -8,10 +8,10 @@ import InfoFilledWrap from '../../components/InfoFilled';
|
|||
import QuickActionCard from '../../components/QuickActionCard';
|
||||
import WelcomeModal from '../../components/WelcomeModal';
|
||||
import Scaffold from '../../containers/layouts/Scaffold';
|
||||
import { GET_PROJECT, GET_USER } from '../../graphql';
|
||||
import useActions from '../../redux/actions';
|
||||
import * as UserActions from '../../redux/actions/user';
|
||||
import { RootState } from '../../redux/reducers';
|
||||
import { GET_PROJECT, GET_USER } from '../../schemas';
|
||||
import useStyles from './style';
|
||||
|
||||
const CreateWorkflowCard = () => {
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
import {
|
||||
Button,
|
||||
Fade,
|
||||
FormControl,
|
||||
Menu,
|
||||
MenuItem,
|
||||
TextField,
|
||||
} from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import useStyles from './styles';
|
||||
|
||||
interface SetTimeProps {
|
||||
start: number;
|
||||
end: number;
|
||||
interval: number;
|
||||
label: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
// dropdown menu component for setting time
|
||||
const SetTime: React.FC<SetTimeProps> = ({
|
||||
start,
|
||||
end,
|
||||
interval,
|
||||
label,
|
||||
type,
|
||||
}) => {
|
||||
const classes = useStyles();
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const [age, setAge] = React.useState(0);
|
||||
const open = Boolean(anchorEl);
|
||||
const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => {
|
||||
setAge((event.target.value as unknown) as number);
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
const size = (end - start) / interval;
|
||||
|
||||
const names: number[] = [start];
|
||||
for (let i = 1; i <= size; i += 1) {
|
||||
names[i] = names[i - 1] + interval;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FormControl>
|
||||
<TextField
|
||||
className={classes.textField}
|
||||
id="outlined-basic"
|
||||
variant="outlined"
|
||||
value={age}
|
||||
onChange={handleChange}
|
||||
color="secondary"
|
||||
inputProps={{
|
||||
style: {
|
||||
fontSize: '0.75rem',
|
||||
color: '#000000',
|
||||
paddingLeft: 27,
|
||||
height: '0.425rem',
|
||||
},
|
||||
'aria-label': 'change-time',
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<Button
|
||||
className={classes.button}
|
||||
onClick={(event) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
}}
|
||||
endIcon={<img src="./icons/down-arrow.svg" alt="arrow" />}
|
||||
disableRipple
|
||||
disableFocusRipple
|
||||
>
|
||||
{label}
|
||||
</Button>
|
||||
<Menu
|
||||
id="fade-menu"
|
||||
anchorEl={anchorEl}
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
TransitionComponent={Fade}
|
||||
>
|
||||
{names.map((name) => (
|
||||
<MenuItem
|
||||
key={name}
|
||||
value={name}
|
||||
onClick={() => {
|
||||
setAge(name);
|
||||
setAnchorEl(null);
|
||||
}}
|
||||
>
|
||||
{name} {type}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default SetTime;
|
|
@ -0,0 +1,20 @@
|
|||
import { makeStyles, Theme } from '@material-ui/core/styles';
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => ({
|
||||
button: {
|
||||
fontSize: '0.75rem',
|
||||
'&:hover': {
|
||||
focusVisible: 'none',
|
||||
background: theme.palette.secondary.contrastText,
|
||||
},
|
||||
marginTop: theme.spacing(0.7),
|
||||
textTransform: 'none',
|
||||
fontWeight: 'normal',
|
||||
},
|
||||
textField: {
|
||||
width: '4.4375rem',
|
||||
height: '2.75rem',
|
||||
marginLeft: theme.spacing(1.875),
|
||||
},
|
||||
}));
|
||||
export default useStyles;
|
|
@ -0,0 +1,373 @@
|
|||
import {
|
||||
Divider,
|
||||
FormControl,
|
||||
FormControlLabel,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Select,
|
||||
Typography,
|
||||
} from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import CustomDate from '../../components/DateTime/CustomDate/index';
|
||||
import CustomTime from '../../components/DateTime/CustomTime/index';
|
||||
import SetTime from './SetTime/index';
|
||||
import useStyles from './styles';
|
||||
import Scaffold from '../../containers/layouts/Scaffold';
|
||||
import ButtonFilled from '../../components/Button/ButtonFilled';
|
||||
import ButtonOutline from '../../components/Button/ButtonOutline';
|
||||
|
||||
// To be changed to a Location Generic
|
||||
interface WorkflowScheduleProps {
|
||||
location: any;
|
||||
}
|
||||
|
||||
const SchedulePage: React.FC<WorkflowScheduleProps> = () => {
|
||||
const start = 0;
|
||||
const end = 10;
|
||||
const interval = 2;
|
||||
|
||||
const classes = useStyles();
|
||||
// controls radio buttons
|
||||
const [value, setValue] = React.useState('now');
|
||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setValue(event.target.value);
|
||||
};
|
||||
|
||||
// controls inner radio buttons of recurring schedule
|
||||
const [valueDef, setValueDef] = React.useState('');
|
||||
const handleChangeInstance = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setValueDef(event.target.value);
|
||||
};
|
||||
|
||||
// sets weekdays
|
||||
const [days, setDays] = React.useState('Monday');
|
||||
|
||||
// sets dates
|
||||
const [dates, setDates] = React.useState(1);
|
||||
|
||||
// stores dates in an array
|
||||
const names: number[] = [1];
|
||||
for (let i = 1; i <= 30; i += 1) {
|
||||
names[i] = i + 1;
|
||||
}
|
||||
|
||||
const weekdays: string[] = [
|
||||
'Monday',
|
||||
'Tuesday',
|
||||
'Wednesday',
|
||||
'Thursday',
|
||||
'Friday',
|
||||
'Saturday',
|
||||
'Sunday',
|
||||
];
|
||||
|
||||
return (
|
||||
<Scaffold>
|
||||
<div className={classes.rootContainer}>
|
||||
<Typography className={classes.mainHeader}>
|
||||
Schedule a workflow
|
||||
</Typography>
|
||||
<Typography className={classes.headerDesc}>
|
||||
Click on test to see detailed log of your workflow
|
||||
</Typography>
|
||||
<div className={classes.root}>
|
||||
<div className={classes.scHeader}>
|
||||
{/* Upper segment */}
|
||||
<div className={classes.scSegments}>
|
||||
<div>
|
||||
<Typography className={classes.headerText}>
|
||||
<strong>Schedule details:</strong>
|
||||
</Typography>
|
||||
|
||||
<div className={classes.schBody}>
|
||||
<Typography align="left" className={classes.description}>
|
||||
Choose the right time to first workflow. Below you can find
|
||||
any option convenient for you.
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
<img
|
||||
src="./icons/calendar.svg"
|
||||
alt="calendar"
|
||||
className={classes.calIcon}
|
||||
/>
|
||||
</div>
|
||||
<Divider />
|
||||
|
||||
{/* Lower segment */}
|
||||
<div className={classes.scFormControl}>
|
||||
<FormControl component="fieldset" className={classes.formControl}>
|
||||
<RadioGroup
|
||||
aria-label="schedule"
|
||||
name="schedule"
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
>
|
||||
{/* options to choose schedule */}
|
||||
<FormControlLabel
|
||||
value="now"
|
||||
control={<Radio />}
|
||||
label={
|
||||
<Typography className={classes.radioText}>
|
||||
Schedule now
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
|
||||
<FormControlLabel
|
||||
value="afterSometime"
|
||||
control={<Radio />}
|
||||
label={
|
||||
<Typography className={classes.radioText}>
|
||||
Schedule after some time
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
{value === 'afterSometime' ? (
|
||||
<div className={classes.schLater}>
|
||||
<Typography className={classes.captionText}>
|
||||
Choose the minutes, hours, or days when you want to
|
||||
start workflow
|
||||
</Typography>
|
||||
<div className={classes.wtDateTime}>
|
||||
<Typography
|
||||
variant="body2"
|
||||
className={classes.captionText}
|
||||
>
|
||||
After
|
||||
</Typography>
|
||||
|
||||
<SetTime
|
||||
start={start}
|
||||
end={end}
|
||||
interval={interval}
|
||||
label="Days"
|
||||
type="days"
|
||||
/>
|
||||
<CustomTime ampm disabled={false} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<FormControlLabel
|
||||
value="specificTime"
|
||||
control={<Radio />}
|
||||
label={
|
||||
<Typography className={classes.radioText}>
|
||||
Schedule at a specific time
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
|
||||
{value === 'specificTime' ? (
|
||||
<div className={classes.schLater}>
|
||||
<Typography className={classes.captionText}>
|
||||
Select date and time to start workflow in future
|
||||
</Typography>
|
||||
<div className={classes.innerSpecific}>
|
||||
<CustomDate disabled={false} />
|
||||
<CustomTime ampm disabled={false} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<FormControlLabel
|
||||
value="recurringScedule"
|
||||
control={<Radio />}
|
||||
label={
|
||||
<Typography className={classes.radioText}>
|
||||
Recurring Schedule
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
{value === 'recurringScedule' ? (
|
||||
<div className={classes.schLater}>
|
||||
<Typography className={classes.captionText}>
|
||||
Choose the right recurring time to start your workflow
|
||||
</Typography>
|
||||
|
||||
{/* options to select time of recurring schedule */}
|
||||
<div className={classes.innerRecurring}>
|
||||
<FormControl component="fieldset">
|
||||
<RadioGroup
|
||||
aria-label="instanceDef"
|
||||
name="instanceDef"
|
||||
value={valueDef}
|
||||
onChange={handleChangeInstance}
|
||||
>
|
||||
<FormControlLabel
|
||||
value="everyHr"
|
||||
control={<Radio />}
|
||||
label="Every Hour"
|
||||
/>
|
||||
{valueDef === 'everyHr' ? (
|
||||
<div>
|
||||
<div className={classes.scRandom}>
|
||||
<Typography className={classes.scRandsub1}>
|
||||
At
|
||||
</Typography>
|
||||
<SetTime
|
||||
start={start}
|
||||
end={end}
|
||||
interval={interval}
|
||||
label="th"
|
||||
type=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<FormControlLabel
|
||||
value="everyDay"
|
||||
control={<Radio />}
|
||||
label="Every Day "
|
||||
/>
|
||||
{valueDef === 'everyDay' ? (
|
||||
<div>
|
||||
<div className={classes.scRandom}>
|
||||
<Typography className={classes.scRandsub1}>
|
||||
At
|
||||
</Typography>
|
||||
<CustomTime ampm disabled={false} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<FormControlLabel
|
||||
value="everyWeek"
|
||||
control={<Radio />}
|
||||
label="Every Week "
|
||||
/>
|
||||
{valueDef === 'everyWeek' ? (
|
||||
<div>
|
||||
<div className={classes.scRandom}>
|
||||
<Typography className={classes.scRandsub1}>
|
||||
On
|
||||
</Typography>
|
||||
<FormControl
|
||||
className={classes.formControlDT}
|
||||
>
|
||||
<Select
|
||||
className={classes.select}
|
||||
disableUnderline
|
||||
value={days}
|
||||
onChange={(e) => {
|
||||
setDays(
|
||||
(e.target.value as unknown) as string
|
||||
);
|
||||
}}
|
||||
label="days"
|
||||
inputProps={{
|
||||
name: 'days',
|
||||
|
||||
id: 'outlined-age-native-simple',
|
||||
style: {
|
||||
fontSize: '0.75rem',
|
||||
height: 7,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{weekdays.map((day) => (
|
||||
<option
|
||||
className={classes.opt}
|
||||
value={day}
|
||||
>
|
||||
{day}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<Typography className={classes.scRandsub1}>
|
||||
at
|
||||
</Typography>
|
||||
<CustomTime ampm disabled={false} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<FormControlLabel
|
||||
value="everyMonth"
|
||||
control={<Radio />}
|
||||
label="Every Month"
|
||||
/>
|
||||
{valueDef === 'everyMonth' ? (
|
||||
<div>
|
||||
<div className={classes.scRandom}>
|
||||
<Typography className={classes.scRandsub1}>
|
||||
On
|
||||
</Typography>
|
||||
<FormControl
|
||||
className={classes.formControlMonth}
|
||||
>
|
||||
<Select
|
||||
className={classes.select}
|
||||
disableUnderline
|
||||
value={dates}
|
||||
onChange={(e) => {
|
||||
setDates(
|
||||
(e.target.value as unknown) as number
|
||||
);
|
||||
}}
|
||||
label="dates"
|
||||
inputProps={{
|
||||
name: 'dates',
|
||||
id: 'outlined-age-native-simple',
|
||||
style: {
|
||||
fontSize: '0.75rem',
|
||||
height: 7,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{names.map((date) => (
|
||||
<option
|
||||
className={classes.opt}
|
||||
value={date}
|
||||
>
|
||||
{date}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<Typography className={classes.scRandsub1}>
|
||||
at
|
||||
</Typography>
|
||||
<CustomTime ampm disabled={false} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
</div>
|
||||
<Divider />
|
||||
<div className={classes.submitDiv}>
|
||||
<ButtonOutline isDisabled={false} handleClick={() => {}}>
|
||||
<Typography>Cancel</Typography>
|
||||
</ButtonOutline>
|
||||
<div style={{ marginLeft: 'auto' }}>
|
||||
<ButtonFilled isPrimary handleClick={() => {}}>
|
||||
<Typography>Save Changes</Typography>
|
||||
</ButtonFilled>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Scaffold>
|
||||
);
|
||||
};
|
||||
|
||||
export default SchedulePage;
|
|
@ -0,0 +1,166 @@
|
|||
import { makeStyles, Theme } from '@material-ui/core/styles';
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => ({
|
||||
rootContainer: {
|
||||
paddingTop: 50,
|
||||
paddingLeft: 30,
|
||||
},
|
||||
root: {
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.6)',
|
||||
width: '90%',
|
||||
marginTop: 30,
|
||||
border: 1,
|
||||
borderColor: theme.palette.text.disabled,
|
||||
borderRadius: '0.1875rem',
|
||||
},
|
||||
scHeader: {
|
||||
paddingLeft: theme.spacing(3.75),
|
||||
paddingRight: theme.spacing(3.75),
|
||||
paddingTop: theme.spacing(3.75),
|
||||
paddingBottom: theme.spacing(3.75),
|
||||
},
|
||||
|
||||
mainHeader: {
|
||||
marginTop: theme.spacing(1.25),
|
||||
fontSize: '36px',
|
||||
},
|
||||
|
||||
headerDesc: {
|
||||
fontSize: '16px',
|
||||
},
|
||||
/* styles for upper and lower segment */
|
||||
scSegments: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-even',
|
||||
},
|
||||
|
||||
headerText: {
|
||||
marginTop: theme.spacing(1.25),
|
||||
fontSize: '1.5625rem',
|
||||
},
|
||||
schBody: {
|
||||
width: '32.18rem',
|
||||
},
|
||||
captionText: {
|
||||
fontSize: '0.75rem',
|
||||
color: theme.palette.text.disabled,
|
||||
},
|
||||
schLater: {
|
||||
marginLeft: theme.spacing(3.75),
|
||||
},
|
||||
|
||||
radioText: {
|
||||
fontSize: '0.875rem',
|
||||
},
|
||||
description: {
|
||||
width: '32.18rem',
|
||||
marginTop: theme.spacing(3.25),
|
||||
marginBottom: theme.spacing(7.5),
|
||||
fontSize: '1rem',
|
||||
},
|
||||
|
||||
calIcon: {
|
||||
width: '7rem',
|
||||
height: '6.31rem',
|
||||
marginTop: theme.spacing(5),
|
||||
marginLeft: 'auto',
|
||||
},
|
||||
|
||||
scFormControl: {
|
||||
marginTop: theme.spacing(5),
|
||||
},
|
||||
|
||||
/* For recurring schedule options */
|
||||
scRandom: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
marginLeft: theme.spacing(1.625),
|
||||
marginBottom: theme.spacing(4.125),
|
||||
height: '2.75rem',
|
||||
alignItems: 'center',
|
||||
},
|
||||
|
||||
formControl: {
|
||||
margin: theme.spacing(1),
|
||||
},
|
||||
|
||||
/* for option- after sometime */
|
||||
wtDateTime: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'center',
|
||||
width: '19.2rem',
|
||||
height: '2.75rem',
|
||||
marginTop: theme.spacing(2.125),
|
||||
marginBottom: theme.spacing(4.125),
|
||||
},
|
||||
|
||||
/* for option- at specific time */
|
||||
innerSpecific: {
|
||||
marginTop: theme.spacing(2.125),
|
||||
marginBottom: theme.spacing(4.125),
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'stretch',
|
||||
},
|
||||
|
||||
innerRecurring: {
|
||||
marginTop: theme.spacing(2.125),
|
||||
},
|
||||
|
||||
/* for each options of recurring schedule */
|
||||
scRandsub1: {
|
||||
margin: theme.spacing(1.875),
|
||||
fontSize: '0.75rem',
|
||||
color: theme.palette.text.disabled,
|
||||
},
|
||||
|
||||
/* for selecting weekdays */
|
||||
formControlDT: {
|
||||
margin: theme.spacing(1),
|
||||
minWidth: '6.6025rem',
|
||||
minHeight: '2.75rem',
|
||||
'&:select': {
|
||||
focusVisible: 'none',
|
||||
background: theme.palette.secondary.contrastText,
|
||||
},
|
||||
},
|
||||
|
||||
/* for selecting date of every month */
|
||||
formControlMonth: {
|
||||
margin: theme.spacing(1),
|
||||
minWidth: '5.3125rem',
|
||||
minHeight: '2.75rem',
|
||||
},
|
||||
|
||||
/* for each select */
|
||||
select: {
|
||||
padding: theme.spacing(2.5),
|
||||
border: '1px solid #D1D2D7',
|
||||
borderRadius: '0.1875rem',
|
||||
fontSize: '0.75rem',
|
||||
height: '2.75rem',
|
||||
},
|
||||
/* for each option */
|
||||
opt: {
|
||||
marginBottom: theme.spacing(1),
|
||||
marginLeft: theme.spacing(0.2),
|
||||
marginRight: theme.spacing(0.2),
|
||||
paddingLeft: theme.spacing(1),
|
||||
borderRadius: '0.0625rem',
|
||||
'&:hover': {
|
||||
background: '#D1D2D7',
|
||||
},
|
||||
},
|
||||
/* style for submit section */
|
||||
submitDiv: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-even',
|
||||
marginTop: theme.spacing(6.25),
|
||||
},
|
||||
}));
|
||||
|
||||
export default useStyles;
|
|
@ -1,13 +1,11 @@
|
|||
/* eslint-disable camelcase */
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useSubscription } from '@apollo/client';
|
||||
import { Typography } from '@material-ui/core';
|
||||
import Scaffold from '../../containers/layouts/Scaffold';
|
||||
import { WORKFLOW_EVENTS } from '../../schemas';
|
||||
import SideBar from '../../components/Sections/WorkflowUnderground/WorkflowRepresentation';
|
||||
import useStyles from './styles';
|
||||
import Loader from '../../components/Loader';
|
||||
import { WORKFLOW_EVENTS } from '../../graphql';
|
||||
|
||||
interface WorkflowUndergroundProps {
|
||||
location: any;
|
||||
|
|
|
@ -71,13 +71,13 @@ function customTheme(options: ThemeOptions) {
|
|||
white: (opacity: number): string => {
|
||||
let op = opacity;
|
||||
if (op < 0) op = 0;
|
||||
if (op < 100) op = 100;
|
||||
if (op > 100) op = 100;
|
||||
return `rgba(255, 255, 255, ${op})`;
|
||||
},
|
||||
black: (opacity: number): string => {
|
||||
let op = opacity;
|
||||
if (op < 0) op = 0;
|
||||
if (op < 100) op = 100;
|
||||
if (op > 100) op = 100;
|
||||
return `rgba(0, 0, 0, ${op})`;
|
||||
},
|
||||
},
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
const sortAlphaAsc = (a: string, b: string): number =>
|
||||
a === b ? 0 : a < b ? -1 : 1;
|
||||
|
||||
const sortAlphaDesc = (a: string, b: string): number =>
|
||||
a === b ? 0 : a > b ? -1 : 1;
|
||||
|
||||
const sortNumAsc = (a: number, b: number): number =>
|
||||
a === b ? 0 : a < b ? -1 : 1;
|
||||
|
||||
const sortNumDesc = (a: number, b: number): number =>
|
||||
a === b ? 0 : a > b ? -1 : 1;
|
||||
|
||||
export { sortAlphaAsc, sortAlphaDesc, sortNumAsc, sortNumDesc };
|
Loading…
Reference in New Issue