chore: (litmus-portal) Logs for workflows (#2131)
* fixed redux to fetch the information required for logs and removed redux logger * completed subcription and display of logs * fixed workflowDetails page styling * Fixed Tab Switching Signed-off-by: arkajyotiMukherjee <arkajyoti.mukherjee@mayadata.io> Co-authored-by: Sayan Mondal <sayan.mondal@mayadata.io>
This commit is contained in:
parent
43b82de5ce
commit
8c3bd228a5
|
|
@ -7682,11 +7682,6 @@
|
||||||
"integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=",
|
"integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"deep-diff": {
|
|
||||||
"version": "0.3.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz",
|
|
||||||
"integrity": "sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ="
|
|
||||||
},
|
|
||||||
"deep-equal": {
|
"deep-equal": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
|
||||||
|
|
@ -18613,14 +18608,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz",
|
"resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz",
|
||||||
"integrity": "sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg=="
|
"integrity": "sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg=="
|
||||||
},
|
},
|
||||||
"redux-logger": {
|
|
||||||
"version": "3.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-3.0.6.tgz",
|
|
||||||
"integrity": "sha1-91VZZvMJjzyIYExEnPC69XeCdL8=",
|
|
||||||
"requires": {
|
|
||||||
"deep-diff": "^0.3.5"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"redux-persist": {
|
"redux-persist": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,6 @@
|
||||||
"react-simple-maps": "^2.1.2",
|
"react-simple-maps": "^2.1.2",
|
||||||
"redux": "^4.0.1",
|
"redux": "^4.0.1",
|
||||||
"redux-devtools-extension": "^2.13.8",
|
"redux-devtools-extension": "^2.13.8",
|
||||||
"redux-logger": "^3.0.6",
|
|
||||||
"redux-persist": "^6.0.0",
|
"redux-persist": "^6.0.0",
|
||||||
"redux-thunk": "^2.3.0",
|
"redux-thunk": "^2.3.0",
|
||||||
"subscriptions-transport-ws": "^0.9.17",
|
"subscriptions-transport-ws": "^0.9.17",
|
||||||
|
|
@ -89,7 +88,6 @@
|
||||||
"@types/react-redux": "^7.1.7",
|
"@types/react-redux": "^7.1.7",
|
||||||
"@types/react-router-dom": "^5.1.3",
|
"@types/react-router-dom": "^5.1.3",
|
||||||
"@types/react-simple-maps": "^1.0.3",
|
"@types/react-simple-maps": "^1.0.3",
|
||||||
"@types/redux-logger": "^3.0.7",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^3.5.0",
|
"@typescript-eslint/eslint-plugin": "^3.5.0",
|
||||||
"@typescript-eslint/parser": "^3.5.0",
|
"@typescript-eslint/parser": "^3.5.0",
|
||||||
"cross-env": "^5.2.0",
|
"cross-env": "^5.2.0",
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import React from 'react';
|
|
||||||
import Typography from '@material-ui/core/Typography';
|
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
import { useTheme } from '@material-ui/core/styles';
|
import { useTheme } from '@material-ui/core/styles';
|
||||||
import useStyles from './styles';
|
import Typography from '@material-ui/core/Typography';
|
||||||
|
import React from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
import { RootState } from '../../redux/reducers';
|
import { RootState } from '../../redux/reducers';
|
||||||
import formatCount from '../../utils/formatCount';
|
import formatCount from '../../utils/formatCount';
|
||||||
|
import useStyles from './styles';
|
||||||
|
|
||||||
interface CardValueData {
|
interface CardValueData {
|
||||||
color: string;
|
color: string;
|
||||||
|
|
@ -51,6 +51,7 @@ const InfoFilledWrap: React.FC = () => {
|
||||||
const cardArray = cardData.map((individualCard) => {
|
const cardArray = cardData.map((individualCard) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
key={individualCard.value}
|
||||||
style={{ backgroundColor: `${individualCard.color}` }}
|
style={{ backgroundColor: `${individualCard.color}` }}
|
||||||
className={classes.infoFilledDiv}
|
className={classes.infoFilledDiv}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
import React from 'react';
|
|
||||||
import Modal from '@material-ui/core/Modal';
|
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
|
import Modal from '@material-ui/core/Modal';
|
||||||
|
import React from 'react';
|
||||||
import useStyles from './styles';
|
import useStyles from './styles';
|
||||||
/* DelUser, NewUserModal, ResetModal need to be shifted */
|
/* DelUser, NewUserModal, ResetModal need to be shifted */
|
||||||
|
|
||||||
interface UnimodalProps {
|
interface UnimodalProps {
|
||||||
children?: React.ReactNode;
|
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
handleClose: () => void;
|
handleClose: () => void;
|
||||||
hasCloseBtn: boolean;
|
hasCloseBtn: boolean;
|
||||||
|
|
@ -21,7 +20,8 @@ const Unimodal: React.FC<UnimodalProps> = ({
|
||||||
isDark,
|
isDark,
|
||||||
textAlign,
|
textAlign,
|
||||||
}) => {
|
}) => {
|
||||||
const styleProps = { textAlign, isDark };
|
const isDarkBg = isDark ?? false;
|
||||||
|
const styleProps = { textAlign, isDarkBg };
|
||||||
const classes = useStyles(styleProps);
|
const classes = useStyles(styleProps);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||||
width: '70%',
|
width: '70%',
|
||||||
padding: '1rem',
|
padding: '1rem',
|
||||||
margin: '2rem auto',
|
margin: '2rem auto',
|
||||||
background: props.isDark
|
background: props.isDarkBg
|
||||||
? theme.palette.editorBackground
|
? theme.palette.editorBackground
|
||||||
: theme.palette.common.white,
|
: theme.palette.common.white,
|
||||||
borderRadius: 3,
|
borderRadius: 3,
|
||||||
|
|
@ -30,14 +30,14 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||||
minHeight: 0,
|
minHeight: 0,
|
||||||
minWidth: 0,
|
minWidth: 0,
|
||||||
borderRadius: 3,
|
borderRadius: 3,
|
||||||
color: props.isDark
|
color: props.isDarkBg
|
||||||
? theme.palette.secondary.contrastText
|
? theme.palette.secondary.contrastText
|
||||||
: theme.palette.customColors.black(0.4),
|
: theme.palette.customColors.black(0.4),
|
||||||
border: '1px solid',
|
border: '1px solid',
|
||||||
borderColor: props.isDark
|
borderColor: props.isDarkBg
|
||||||
? theme.palette.customColors.white(0.2)
|
? theme.palette.customColors.white(0.2)
|
||||||
: theme.palette.customColors.black(0.4),
|
: theme.palette.customColors.black(0.4),
|
||||||
marginLeft: props.isDark ? '82.5%' : '60%',
|
marginLeft: props.isDarkBg ? '82.5%' : '60%',
|
||||||
marginTop: theme.spacing(5),
|
marginTop: theme.spacing(5),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ export const WORKFLOW_DETAILS = gql`
|
||||||
cluster_name
|
cluster_name
|
||||||
last_updated
|
last_updated
|
||||||
cluster_type
|
cluster_type
|
||||||
|
cluster_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
/* eslint-disable import/prefer-default-export */
|
|
||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
export const WORKFLOW_EVENTS = gql`
|
export const WORKFLOW_EVENTS = gql`
|
||||||
|
|
@ -11,6 +10,15 @@ export const WORKFLOW_EVENTS = gql`
|
||||||
project_id
|
project_id
|
||||||
cluster_name
|
cluster_name
|
||||||
last_updated
|
last_updated
|
||||||
|
cluster_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const WORKFLOW_LOGS = gql`
|
||||||
|
subscription podLog($podDetails: PodLogRequest!) {
|
||||||
|
getPodLog(podDetails: $podDetails) {
|
||||||
|
log
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
export interface PodLogRequest {
|
||||||
|
cluster_id: string;
|
||||||
|
workflow_run_id: string;
|
||||||
|
pod_name: string;
|
||||||
|
pod_namespace: string;
|
||||||
|
pod_type: string;
|
||||||
|
exp_pod?: string;
|
||||||
|
runner_pod?: string;
|
||||||
|
chaos_namespace?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PodLogResponse {
|
||||||
|
workflow_run_id: string;
|
||||||
|
pod_name: string;
|
||||||
|
pod_type: string;
|
||||||
|
log: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PodLogVars {
|
||||||
|
podDetails: PodLogRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PodLog {
|
||||||
|
getPodLog: PodLogResponse;
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,7 @@ export interface ChaosData {
|
||||||
export interface Node {
|
export interface Node {
|
||||||
children: string[] | null;
|
children: string[] | null;
|
||||||
finishedAt: string;
|
finishedAt: string;
|
||||||
|
message: string;
|
||||||
name: string;
|
name: string;
|
||||||
phase: string;
|
phase: string;
|
||||||
startedAt: string;
|
startedAt: string;
|
||||||
|
|
@ -47,6 +48,7 @@ export interface WorkflowRun {
|
||||||
workflow_name: string;
|
workflow_name: string;
|
||||||
workflow_run_id: string;
|
workflow_run_id: string;
|
||||||
cluster_type: string;
|
cluster_type: string;
|
||||||
|
cluster_id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Workflow {
|
export interface Workflow {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
import { Node } from '../graphql/workflowData';
|
import { Node } from '../graphql/workflowData';
|
||||||
|
|
||||||
|
export interface SelectedNode extends Node {
|
||||||
|
pod_name: string;
|
||||||
|
}
|
||||||
|
|
||||||
export enum NodeSelectionActions {
|
export enum NodeSelectionActions {
|
||||||
SELECT_NODE = 'SELECT_NODE',
|
SELECT_NODE = 'SELECT_NODE',
|
||||||
}
|
}
|
||||||
|
|
@ -11,5 +15,5 @@ interface NodeSelectionActionType<T, P> {
|
||||||
|
|
||||||
export type NodeSelectionAction = NodeSelectionActionType<
|
export type NodeSelectionAction = NodeSelectionActionType<
|
||||||
typeof NodeSelectionActions.SELECT_NODE,
|
typeof NodeSelectionActions.SELECT_NODE,
|
||||||
Node
|
SelectedNode
|
||||||
>;
|
>;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
export interface TabState {
|
export interface TabState {
|
||||||
workflows: number;
|
workflows: number;
|
||||||
settings: number;
|
settings: number;
|
||||||
|
node: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum TabActions {
|
export enum TabActions {
|
||||||
CHANGE_WORKFLOWS_TAB = 'CHANGE_WORKFLOWS_TAB',
|
CHANGE_WORKFLOWS_TAB = 'CHANGE_WORKFLOWS_TAB',
|
||||||
CHANGE_SETTINGS_TAB = 'CHANGE_SETTINGS_TAB',
|
CHANGE_SETTINGS_TAB = 'CHANGE_SETTINGS_TAB',
|
||||||
|
CHANGE_WORKFLOW_DETAILS_TAB = 'CHANGE_WORKFLOW_DETAILS_TAB',
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TabActionType<T, P> {
|
interface TabActionType<T, P> {
|
||||||
|
|
@ -15,4 +17,5 @@ interface TabActionType<T, P> {
|
||||||
|
|
||||||
export type TabAction =
|
export type TabAction =
|
||||||
| TabActionType<typeof TabActions.CHANGE_WORKFLOWS_TAB, number>
|
| TabActionType<typeof TabActions.CHANGE_WORKFLOWS_TAB, number>
|
||||||
| TabActionType<typeof TabActions.CHANGE_SETTINGS_TAB, number>;
|
| TabActionType<typeof TabActions.CHANGE_SETTINGS_TAB, number>
|
||||||
|
| TabActionType<typeof TabActions.CHANGE_WORKFLOW_DETAILS_TAB, number>;
|
||||||
|
|
|
||||||
|
|
@ -91,10 +91,10 @@ const TopNavButtons: React.FC<Props> = ({ isToggled, setIsToggled }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.button}>
|
<div className={classes.button}>
|
||||||
<div className={classes.buttonLeft}>
|
<div>
|
||||||
<BackButton isDisabled={false} />
|
<BackButton isDisabled={false} />
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.buttonRight}>
|
<div>
|
||||||
{AnalyticsButton()}
|
{AnalyticsButton()}
|
||||||
{ExportButton()}
|
{ExportButton()}
|
||||||
{InfoButton()}
|
{InfoButton()}
|
||||||
|
|
|
||||||
|
|
@ -11,19 +11,18 @@ import Scaffold from '../../containers/layouts/Scaffold';
|
||||||
import { WORKFLOW_DETAILS, WORKFLOW_EVENTS } from '../../graphql';
|
import { WORKFLOW_DETAILS, WORKFLOW_EVENTS } from '../../graphql';
|
||||||
import {
|
import {
|
||||||
ExecutionData,
|
ExecutionData,
|
||||||
Node,
|
|
||||||
Workflow,
|
Workflow,
|
||||||
WorkflowDataVars,
|
WorkflowDataVars,
|
||||||
WorkflowSubscription,
|
WorkflowSubscription,
|
||||||
} from '../../models/graphql/workflowData';
|
} from '../../models/graphql/workflowData';
|
||||||
|
import useActions from '../../redux/actions';
|
||||||
|
import * as TabActions from '../../redux/actions/tabs';
|
||||||
import { RootState } from '../../redux/reducers';
|
import { RootState } from '../../redux/reducers';
|
||||||
import ArgoWorkflow from '../../views/WorkflowDetails/ArgoWorkflow';
|
import ArgoWorkflow from '../../views/WorkflowDetails/ArgoWorkflow';
|
||||||
import WorkflowInfo from '../../views/WorkflowDetails/WorkflowInfo';
|
import WorkflowInfo from '../../views/WorkflowDetails/WorkflowInfo';
|
||||||
|
import WorkflowNodeInfo from '../../views/WorkflowDetails/WorkflowNodeInfo';
|
||||||
import useStyles from './styles';
|
import useStyles from './styles';
|
||||||
import TopNavButtons from './TopNavButtons';
|
import TopNavButtons from './TopNavButtons';
|
||||||
import WorkflowNodeInfo from '../../views/WorkflowDetails/NodeInfo';
|
|
||||||
import ButtonFilled from '../../components/Button/ButtonFilled';
|
|
||||||
import ButtonOutline from '../../components/Button/ButtonOutline';
|
|
||||||
|
|
||||||
interface TopNavButtonsProps {
|
interface TopNavButtonsProps {
|
||||||
isAnalyticsToggled: boolean;
|
isAnalyticsToggled: boolean;
|
||||||
|
|
@ -40,6 +39,7 @@ const WorkflowDetails: React.FC = () => {
|
||||||
isInfoToggled: false,
|
isInfoToggled: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const tabs = useActions(TabActions);
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
// Getting the workflow nome from the pathname
|
// Getting the workflow nome from the pathname
|
||||||
const workflowRunId = pathname.split('/')[3];
|
const workflowRunId = pathname.split('/')[3];
|
||||||
|
|
@ -49,8 +49,9 @@ const WorkflowDetails: React.FC = () => {
|
||||||
const selectedProjectID = useSelector(
|
const selectedProjectID = useSelector(
|
||||||
(state: RootState) => state.userData.selectedProjectID
|
(state: RootState) => state.userData.selectedProjectID
|
||||||
);
|
);
|
||||||
// get Selected Node
|
const workflowDetailsTabValue = useSelector(
|
||||||
const selectedNode = useSelector((state: RootState) => state.selectedNode);
|
(state: RootState) => state.tabNumber.node
|
||||||
|
);
|
||||||
|
|
||||||
// Query to get workflows
|
// Query to get workflows
|
||||||
const { subscribeToMore, data, error } = useQuery<Workflow, WorkflowDataVars>(
|
const { subscribeToMore, data, error } = useQuery<Workflow, WorkflowDataVars>(
|
||||||
|
|
@ -97,12 +98,17 @@ const WorkflowDetails: React.FC = () => {
|
||||||
}
|
}
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
const [value, setValue] = React.useState(0);
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => {
|
const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => {
|
||||||
setValue(newValue);
|
tabs.changeWorkflowDetailsTabs(newValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// On fresh screen refresh 'Workflow' Tab would be selected
|
||||||
|
useEffect(() => {
|
||||||
|
tabs.changeWorkflowDetailsTabs(0);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Scaffold>
|
<Scaffold>
|
||||||
<TopNavButtons isToggled={isToggled} setIsToggled={setIsToggled} />
|
<TopNavButtons isToggled={isToggled} setIsToggled={setIsToggled} />
|
||||||
|
|
@ -116,75 +122,60 @@ const WorkflowDetails: React.FC = () => {
|
||||||
<Typography>{t('workflowDetails.detailedLog')}</Typography>
|
<Typography>{t('workflowDetails.detailedLog')}</Typography>
|
||||||
|
|
||||||
{/* Argo Workflow DAG Graph */}
|
{/* Argo Workflow DAG Graph */}
|
||||||
{isToggled.isInfoToggled ? (
|
<ArgoWorkflow
|
||||||
<div className={classes.w100}>
|
nodes={
|
||||||
<ArgoWorkflow
|
(JSON.parse(workflow.execution_data) as ExecutionData).nodes
|
||||||
nodes={
|
}
|
||||||
(JSON.parse(workflow.execution_data) as ExecutionData).nodes
|
/>
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className={classes.w140}>
|
|
||||||
<ArgoWorkflow
|
|
||||||
nodes={
|
|
||||||
(JSON.parse(workflow.execution_data) as ExecutionData).nodes
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
{isToggled.isInfoToggled ? (
|
{isToggled.isInfoToggled ? (
|
||||||
<div>
|
<div className={classes.workflowSideBar}>
|
||||||
<>
|
<AppBar
|
||||||
<AppBar
|
position="static"
|
||||||
position="static"
|
color="default"
|
||||||
color="default"
|
className={classes.appBar}
|
||||||
className={classes.appBar}
|
>
|
||||||
|
<Tabs
|
||||||
|
value={workflowDetailsTabValue || 0}
|
||||||
|
onChange={handleChange}
|
||||||
|
TabIndicatorProps={{
|
||||||
|
style: {
|
||||||
|
backgroundColor: theme.palette.secondary.dark,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
variant="fullWidth"
|
||||||
>
|
>
|
||||||
<Tabs
|
<StyledTab label="Workflow" />
|
||||||
value={value}
|
<StyledTab label="Nodes" />
|
||||||
onChange={handleChange}
|
</Tabs>
|
||||||
TabIndicatorProps={{
|
</AppBar>
|
||||||
style: {
|
<TabPanel value={workflowDetailsTabValue} index={0}>
|
||||||
backgroundColor: theme.palette.secondary.dark,
|
<div data-cy="browseWorkflow">
|
||||||
},
|
<WorkflowInfo
|
||||||
}}
|
workflow_name={workflow.workflow_name}
|
||||||
variant="fullWidth"
|
execution_data={
|
||||||
>
|
JSON.parse(workflow?.execution_data) as ExecutionData
|
||||||
<StyledTab label="Workflow" />
|
}
|
||||||
<StyledTab label="Nodes" />
|
cluster_name={workflow.cluster_name}
|
||||||
</Tabs>
|
/>
|
||||||
</AppBar>
|
</div>
|
||||||
<TabPanel value={value} index={0}>
|
</TabPanel>
|
||||||
<div data-cy="browseWorkflow">
|
<TabPanel
|
||||||
<WorkflowInfo
|
data-cy="scheduleWorkflow"
|
||||||
workflow_name={workflow.workflow_name}
|
value={workflowDetailsTabValue}
|
||||||
execution_data={
|
index={1}
|
||||||
JSON.parse(workflow?.execution_data) as ExecutionData
|
>
|
||||||
}
|
<div data-cy="browseWorkflow">
|
||||||
cluster_name={workflow.cluster_name}
|
<WorkflowNodeInfo
|
||||||
/>
|
cluster_id={workflow.cluster_id}
|
||||||
</div>
|
workflow_run_id={workflow.workflow_run_id}
|
||||||
</TabPanel>
|
pod_namespace={
|
||||||
<TabPanel data-cy="scheduleWorkflow" value={value} index={1}>
|
(JSON.parse(workflow.execution_data) as ExecutionData)
|
||||||
<div data-cy="browseWorkflow">
|
.namespace
|
||||||
<WorkflowNodeInfo nodeDetails={selectedNode as Node} />
|
}
|
||||||
</div>
|
/>
|
||||||
</TabPanel>
|
</div>
|
||||||
</>
|
</TabPanel>
|
||||||
<div className={classes.footerButton}>
|
|
||||||
<ButtonFilled
|
|
||||||
isPrimary
|
|
||||||
isDisabled={false}
|
|
||||||
handleClick={() => {}}
|
|
||||||
>
|
|
||||||
Events
|
|
||||||
</ButtonFilled>
|
|
||||||
<ButtonOutline isDisabled={false} handleClick={() => {}}>
|
|
||||||
Logs
|
|
||||||
</ButtonOutline>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<></>
|
<></>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { makeStyles, Theme } from '@material-ui/core/styles';
|
||||||
const useStyles = makeStyles((theme: Theme) => ({
|
const useStyles = makeStyles((theme: Theme) => ({
|
||||||
root: {
|
root: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
marginTop: theme.spacing(3),
|
marginTop: theme.spacing(3),
|
||||||
height: '75vh',
|
height: '75vh',
|
||||||
},
|
},
|
||||||
|
|
@ -10,25 +11,11 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||||
margin: theme.spacing(-0.4, 1),
|
margin: theme.spacing(-0.4, 1),
|
||||||
width: '1rem',
|
width: '1rem',
|
||||||
},
|
},
|
||||||
w100: {
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
},
|
|
||||||
w140: {
|
|
||||||
width: '141%',
|
|
||||||
height: '100%',
|
|
||||||
},
|
|
||||||
button: {
|
button: {
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
margin: theme.spacing(4, 4, 0, 4),
|
justifyContent: 'space-between',
|
||||||
},
|
margin: theme.spacing(4, 0, 0, 4),
|
||||||
buttonLeft: {
|
|
||||||
float: 'left',
|
|
||||||
},
|
|
||||||
buttonRight: {
|
|
||||||
position: 'absolute',
|
|
||||||
right: '4%',
|
|
||||||
},
|
},
|
||||||
heading: {
|
heading: {
|
||||||
fontSize: '2rem',
|
fontSize: '2rem',
|
||||||
|
|
@ -36,7 +23,10 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||||
},
|
},
|
||||||
workflowGraph: {
|
workflowGraph: {
|
||||||
padding: '0 3rem',
|
padding: '0 3rem',
|
||||||
width: '70%',
|
width: '100%',
|
||||||
|
},
|
||||||
|
workflowSideBar: {
|
||||||
|
width: '20rem',
|
||||||
},
|
},
|
||||||
loaderDiv: {
|
loaderDiv: {
|
||||||
height: '100%',
|
height: '100%',
|
||||||
|
|
@ -48,12 +38,6 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
},
|
},
|
||||||
footerButton: {
|
|
||||||
marginLeft: 'auto',
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
padding: theme.spacing(3, 4, 4, 0),
|
|
||||||
},
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default useStyles;
|
export default useStyles;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
/* eslint-disable import/prefer-default-export */
|
/* eslint-disable import/prefer-default-export */
|
||||||
import { Node } from '../../models/graphql/workflowData';
|
|
||||||
import {
|
import {
|
||||||
NodeSelectionAction,
|
NodeSelectionAction,
|
||||||
NodeSelectionActions,
|
NodeSelectionActions,
|
||||||
|
SelectedNode,
|
||||||
} from '../../models/redux/nodeSelection';
|
} from '../../models/redux/nodeSelection';
|
||||||
|
|
||||||
export function selectNode(node: Node): NodeSelectionAction {
|
export function selectNode(node: SelectedNode): NodeSelectionAction {
|
||||||
return {
|
return {
|
||||||
type: NodeSelectionActions.SELECT_NODE,
|
type: NodeSelectionActions.SELECT_NODE,
|
||||||
payload: node,
|
payload: node,
|
||||||
|
|
|
||||||
|
|
@ -13,3 +13,10 @@ export function changeSettingsTabs(tabNumber: number): TabAction {
|
||||||
payload: tabNumber,
|
payload: tabNumber,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function changeWorkflowDetailsTabs(tabNumber: number): TabAction {
|
||||||
|
return {
|
||||||
|
type: TabActions.CHANGE_WORKFLOW_DETAILS_TAB,
|
||||||
|
payload: tabNumber,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import { createBrowserHistory } from 'history'; // eslint-disable-line import/no
|
||||||
import * as localforage from 'localforage';
|
import * as localforage from 'localforage';
|
||||||
import { applyMiddleware, createStore } from 'redux';
|
import { applyMiddleware, createStore } from 'redux';
|
||||||
import { composeWithDevTools } from 'redux-devtools-extension';
|
import { composeWithDevTools } from 'redux-devtools-extension';
|
||||||
import { createLogger } from 'redux-logger';
|
|
||||||
import { PersistConfig, persistReducer, persistStore } from 'redux-persist';
|
import { PersistConfig, persistReducer, persistStore } from 'redux-persist';
|
||||||
import thunk from 'redux-thunk';
|
import thunk from 'redux-thunk';
|
||||||
import rootReducer from './reducers';
|
import rootReducer from './reducers';
|
||||||
|
|
@ -14,12 +13,11 @@ const persistConfig: PersistConfig<any> = {
|
||||||
blacklist: [],
|
blacklist: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
const logger = (createLogger as any)();
|
|
||||||
const history = createBrowserHistory();
|
const history = createBrowserHistory();
|
||||||
|
|
||||||
const dev = process.env.NODE_ENV === 'development';
|
const dev = process.env.NODE_ENV === 'development';
|
||||||
|
|
||||||
let middleware = dev ? applyMiddleware(thunk, logger) : applyMiddleware(thunk);
|
let middleware = applyMiddleware(thunk);
|
||||||
|
|
||||||
if (dev) {
|
if (dev) {
|
||||||
middleware = composeWithDevTools(middleware);
|
middleware = composeWithDevTools(middleware);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { combineReducers } from 'redux';
|
import { combineReducers } from 'redux';
|
||||||
import { Node } from '../../models/graphql/workflowData';
|
|
||||||
import { CommunityData } from '../../models/redux/analytics';
|
import { CommunityData } from '../../models/redux/analytics';
|
||||||
|
import { SelectedNode } from '../../models/redux/nodeSelection';
|
||||||
import { TabState } from '../../models/redux/tabs';
|
import { TabState } from '../../models/redux/tabs';
|
||||||
import { TemplateData } from '../../models/redux/template';
|
import { TemplateData } from '../../models/redux/template';
|
||||||
import { UserData } from '../../models/redux/user';
|
import { UserData } from '../../models/redux/user';
|
||||||
|
|
@ -16,7 +16,7 @@ export interface RootState {
|
||||||
communityData: CommunityData;
|
communityData: CommunityData;
|
||||||
userData: UserData;
|
userData: UserData;
|
||||||
workflowData: WorkflowData;
|
workflowData: WorkflowData;
|
||||||
selectedNode: Node;
|
selectedNode: SelectedNode;
|
||||||
tabNumber: TabState;
|
tabNumber: TabState;
|
||||||
selectTemplate: TemplateData;
|
selectTemplate: TemplateData;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,26 @@
|
||||||
import { Node } from '../../models/graphql/workflowData';
|
|
||||||
import {
|
import {
|
||||||
NodeSelectionAction,
|
NodeSelectionAction,
|
||||||
NodeSelectionActions,
|
NodeSelectionActions,
|
||||||
|
SelectedNode,
|
||||||
} from '../../models/redux/nodeSelection';
|
} from '../../models/redux/nodeSelection';
|
||||||
import createReducer from './createReducer';
|
import createReducer from './createReducer';
|
||||||
|
|
||||||
const initialState: Node = {
|
const initialState: SelectedNode = {
|
||||||
children: null,
|
children: null,
|
||||||
finishedAt: '',
|
finishedAt: '',
|
||||||
|
message: '',
|
||||||
name: '',
|
name: '',
|
||||||
|
pod_name: '',
|
||||||
phase: '',
|
phase: '',
|
||||||
startedAt: '',
|
startedAt: '',
|
||||||
type: '',
|
type: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const selectedNode = createReducer<Node>(initialState, {
|
export const selectedNode = createReducer<SelectedNode>(initialState, {
|
||||||
[NodeSelectionActions.SELECT_NODE](state: Node, action: NodeSelectionAction) {
|
[NodeSelectionActions.SELECT_NODE](
|
||||||
|
state: SelectedNode,
|
||||||
|
action: NodeSelectionAction
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
...action.payload,
|
...action.payload,
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import createReducer from './createReducer';
|
||||||
const initialState: TabState = {
|
const initialState: TabState = {
|
||||||
workflows: 0,
|
workflows: 0,
|
||||||
settings: 0,
|
settings: 0,
|
||||||
|
node: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const tabNumber = createReducer<TabState>(initialState, {
|
export const tabNumber = createReducer<TabState>(initialState, {
|
||||||
|
|
@ -20,6 +21,12 @@ export const tabNumber = createReducer<TabState>(initialState, {
|
||||||
settings: action.payload,
|
settings: action.payload,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
[TabActions.CHANGE_WORKFLOW_DETAILS_TAB](state: TabState, action: TabAction) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
node: action.payload,
|
||||||
|
};
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default tabNumber;
|
export default tabNumber;
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,13 @@ import {
|
||||||
} from '@material-ui/core';
|
} from '@material-ui/core';
|
||||||
import MoreVertIcon from '@material-ui/icons/MoreVert';
|
import MoreVertIcon from '@material-ui/icons/MoreVert';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import LinearProgressBar from '../../../components/ProgressBar/LinearProgressBar';
|
||||||
import {
|
import {
|
||||||
ExecutionData,
|
ExecutionData,
|
||||||
WorkflowRun,
|
WorkflowRun,
|
||||||
} from '../../../models/graphql/workflowData';
|
} from '../../../models/graphql/workflowData';
|
||||||
import { history } from '../../../redux/configureStore';
|
import { history } from '../../../redux/configureStore';
|
||||||
import timeDifferenceForDate from '../../../utils/datesModifier';
|
import timeDifferenceForDate from '../../../utils/datesModifier';
|
||||||
import LinearProgressBar from '../../../components/ProgressBar/LinearProgressBar';
|
|
||||||
import CustomStatus from '../CustomStatus/Status';
|
import CustomStatus from '../CustomStatus/Status';
|
||||||
import useStyles from './styles';
|
import useStyles from './styles';
|
||||||
|
|
||||||
|
|
@ -109,9 +109,9 @@ const TableData: React.FC<TableDataProps> = ({ data, exeData }) => {
|
||||||
>
|
>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
value="Workflow"
|
value="Workflow"
|
||||||
onClick={() =>
|
onClick={() => {
|
||||||
history.push(`/workflows/details/${data.workflow_run_id}`)
|
history.push(`/workflows/details/${data.workflow_run_id}`);
|
||||||
}
|
}}
|
||||||
>
|
>
|
||||||
<div className={classes.expDiv} data-cy="workflowDetails">
|
<div className={classes.expDiv} data-cy="workflowDetails">
|
||||||
<img
|
<img
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import DagreGraph, { d3Link, d3Node } from '../../../components/DagreGraph';
|
||||||
import { Nodes } from '../../../models/graphql/workflowData';
|
import { Nodes } from '../../../models/graphql/workflowData';
|
||||||
import useActions from '../../../redux/actions';
|
import useActions from '../../../redux/actions';
|
||||||
import * as NodeSelectionActions from '../../../redux/actions/nodeSelection';
|
import * as NodeSelectionActions from '../../../redux/actions/nodeSelection';
|
||||||
|
import * as TabActions from '../../../redux/actions/tabs';
|
||||||
import useStyles from './styles';
|
import useStyles from './styles';
|
||||||
|
|
||||||
interface GraphData {
|
interface GraphData {
|
||||||
|
|
@ -17,12 +18,18 @@ const ArgoWorkflow: React.FC<ArgoWorkflowProps> = ({ nodes }) => {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
// Redux action call for updating selected node
|
// Redux action call for updating selected node
|
||||||
const nodeSelection = useActions(NodeSelectionActions);
|
const nodeSelection = useActions(NodeSelectionActions);
|
||||||
|
const tabs = useActions(TabActions);
|
||||||
|
|
||||||
const [graphData, setGraphData] = useState<GraphData>({
|
const [graphData, setGraphData] = useState<GraphData>({
|
||||||
nodes: [],
|
nodes: [],
|
||||||
links: [],
|
links: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Get the selected Node
|
||||||
|
const [selectedNodeID, setSelectedNodeID] = useState<string>(
|
||||||
|
Object.keys(nodes)[0]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const data: GraphData = {
|
const data: GraphData = {
|
||||||
nodes: [],
|
nodes: [],
|
||||||
|
|
@ -58,6 +65,13 @@ const ArgoWorkflow: React.FC<ArgoWorkflowProps> = ({ nodes }) => {
|
||||||
});
|
});
|
||||||
}, [nodes]);
|
}, [nodes]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
nodeSelection.selectNode({
|
||||||
|
...nodes[selectedNodeID],
|
||||||
|
pod_name: selectedNodeID,
|
||||||
|
});
|
||||||
|
}, [selectedNodeID]);
|
||||||
|
|
||||||
return graphData.nodes.length ? (
|
return graphData.nodes.length ? (
|
||||||
<DagreGraph
|
<DagreGraph
|
||||||
className={classes.dagreGraph}
|
className={classes.dagreGraph}
|
||||||
|
|
@ -74,7 +88,8 @@ const ArgoWorkflow: React.FC<ArgoWorkflowProps> = ({ nodes }) => {
|
||||||
const nodeID = Object.keys(nodes).filter(
|
const nodeID = Object.keys(nodes).filter(
|
||||||
(key) => key === original?.id
|
(key) => key === original?.id
|
||||||
)[0];
|
)[0];
|
||||||
nodeSelection.selectNode(nodes[nodeID]);
|
setSelectedNodeID(nodeID);
|
||||||
|
tabs.changeWorkflowDetailsTabs(1);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
import { Typography } from '@material-ui/core';
|
|
||||||
import React from 'react';
|
|
||||||
import { Node } from '../../../models/graphql/workflowData';
|
|
||||||
import timeDifference from '../../../utils/datesModifier';
|
|
||||||
import useStyles from './styles';
|
|
||||||
|
|
||||||
interface NodeInfoProps {
|
|
||||||
nodeDetails: Node;
|
|
||||||
}
|
|
||||||
|
|
||||||
const WorkflowNodeInfo: React.FC<NodeInfoProps> = ({ nodeDetails }) => {
|
|
||||||
const classes = useStyles();
|
|
||||||
return (
|
|
||||||
<div className={classes.root}>
|
|
||||||
{/* Node Name */}
|
|
||||||
<div className={classes.heightMaintainer}>
|
|
||||||
<Typography className={classes.nodeSpacing}>
|
|
||||||
<span className={classes.bold}>Node name:</span>
|
|
||||||
<br />
|
|
||||||
{nodeDetails.name}
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
{/* Node Phase */}
|
|
||||||
<div className={classes.nodeSpacing}>
|
|
||||||
<div className={classes.heightMaintainer}>
|
|
||||||
<Typography>
|
|
||||||
<span className={classes.bold}>Phase:</span> {nodeDetails.phase}
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
{/* Node Durations */}
|
|
||||||
<div className={classes.nodeSpacing}>
|
|
||||||
<div className={classes.heightMaintainer}>
|
|
||||||
<Typography>
|
|
||||||
<span className={classes.bold}>Start time:</span>{' '}
|
|
||||||
{timeDifference(nodeDetails.startedAt)}
|
|
||||||
</Typography>
|
|
||||||
<Typography>
|
|
||||||
<span className={classes.bold}>End time:</span>{' '}
|
|
||||||
{timeDifference(nodeDetails.finishedAt)}
|
|
||||||
</Typography>
|
|
||||||
<Typography>
|
|
||||||
<span className={classes.bold}>Duration: </span>{' '}
|
|
||||||
{(
|
|
||||||
(parseInt(nodeDetails.finishedAt, 10) -
|
|
||||||
parseInt(nodeDetails.startedAt, 10)) /
|
|
||||||
60
|
|
||||||
).toFixed(1)}{' '}
|
|
||||||
minutes
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default WorkflowNodeInfo;
|
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { useSubscription } from '@apollo/client';
|
||||||
|
import { Typography } from '@material-ui/core';
|
||||||
|
import React from 'react';
|
||||||
|
import Unimodal from '../../../containers/layouts/Unimodal';
|
||||||
|
import { WORKFLOW_LOGS } from '../../../graphql';
|
||||||
|
import {
|
||||||
|
PodLog,
|
||||||
|
PodLogRequest,
|
||||||
|
PodLogVars,
|
||||||
|
} from '../../../models/graphql/podLog';
|
||||||
|
import useStyles from './styles';
|
||||||
|
|
||||||
|
interface NodeLogsProps extends PodLogRequest {
|
||||||
|
logsOpen: boolean;
|
||||||
|
handleClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NodeLogs: React.FC<NodeLogsProps> = ({
|
||||||
|
logsOpen,
|
||||||
|
handleClose,
|
||||||
|
cluster_id,
|
||||||
|
workflow_run_id,
|
||||||
|
pod_namespace,
|
||||||
|
pod_name,
|
||||||
|
pod_type,
|
||||||
|
}) => {
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
|
const { data } = useSubscription<PodLog, PodLogVars>(WORKFLOW_LOGS, {
|
||||||
|
variables: {
|
||||||
|
podDetails: {
|
||||||
|
cluster_id,
|
||||||
|
workflow_run_id,
|
||||||
|
pod_name,
|
||||||
|
pod_namespace,
|
||||||
|
pod_type,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Unimodal
|
||||||
|
isOpen={logsOpen}
|
||||||
|
handleClose={handleClose}
|
||||||
|
hasCloseBtn
|
||||||
|
textAlign="left"
|
||||||
|
>
|
||||||
|
<div className={classes.root}>
|
||||||
|
{data !== undefined ? (
|
||||||
|
<Typography variant="h5">{data.getPodLog.log}</Typography>
|
||||||
|
) : (
|
||||||
|
<Typography variant="h5">Fetching Logs...</Typography>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Unimodal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NodeLogs;
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { makeStyles, Theme } from '@material-ui/core/styles';
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => ({
|
||||||
|
root: {
|
||||||
|
width: '100%',
|
||||||
|
height: '90%',
|
||||||
|
background: theme.palette.common.black,
|
||||||
|
color: theme.palette.common.white,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default useStyles;
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
||||||
// TODO: remove this after creating UI for node details sidebar
|
|
||||||
import { Typography } from '@material-ui/core';
|
import { Typography } from '@material-ui/core';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
import { ExecutionData } from '../../../models/graphql/workflowData';
|
import { ExecutionData } from '../../../models/graphql/workflowData';
|
||||||
import { RootState } from '../../../redux/reducers';
|
|
||||||
import timeDifference from '../../../utils/datesModifier';
|
import timeDifference from '../../../utils/datesModifier';
|
||||||
import useStyles from './styles';
|
import useStyles from './styles';
|
||||||
|
|
||||||
|
|
@ -26,7 +22,6 @@ const WorkflowInfo: React.FC<WorkflowInfoProps> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
// Get selected node data from redux
|
// Get selected node data from redux
|
||||||
const selectedNode = useSelector((state: RootState) => state.selectedNode);
|
|
||||||
|
|
||||||
const [duration, setDuration] = useState<number>(0);
|
const [duration, setDuration] = useState<number>(0);
|
||||||
const [data, setData] = useState<SidebarState>({
|
const [data, setData] = useState<SidebarState>({
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
import { Typography } from '@material-ui/core';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import ButtonOutline from '../../../components/Button/ButtonOutline';
|
||||||
|
import { RootState } from '../../../redux/reducers';
|
||||||
|
import timeDifference from '../../../utils/datesModifier';
|
||||||
|
import NodeLogs from '../NodeLogs';
|
||||||
|
import useStyles from './styles';
|
||||||
|
|
||||||
|
interface WorkflowNodeInfoProps {
|
||||||
|
cluster_id: string;
|
||||||
|
workflow_run_id: string;
|
||||||
|
pod_namespace: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const WorkflowNodeInfo: React.FC<WorkflowNodeInfoProps> = ({
|
||||||
|
cluster_id,
|
||||||
|
workflow_run_id,
|
||||||
|
pod_namespace,
|
||||||
|
}) => {
|
||||||
|
const classes = useStyles();
|
||||||
|
const [logsOpen, setLogsOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
|
// Get the nelected node from redux
|
||||||
|
const { name, phase, pod_name, type, startedAt, finishedAt } = useSelector(
|
||||||
|
(state: RootState) => state.selectedNode
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setLogsOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.root}>
|
||||||
|
{/* Logs Modal */}
|
||||||
|
{logsOpen ? (
|
||||||
|
<NodeLogs
|
||||||
|
logsOpen={logsOpen}
|
||||||
|
handleClose={handleClose}
|
||||||
|
cluster_id={cluster_id}
|
||||||
|
workflow_run_id={workflow_run_id}
|
||||||
|
pod_namespace={pod_namespace}
|
||||||
|
pod_name={pod_name}
|
||||||
|
pod_type={type}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Node Name */}
|
||||||
|
<div className={classes.heightMaintainer}>
|
||||||
|
<Typography className={classes.nodeSpacing}>
|
||||||
|
<span className={classes.bold}>Name:</span>
|
||||||
|
<br />
|
||||||
|
{pod_name}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
{/* Node Type */}
|
||||||
|
<div className={classes.heightMaintainer}>
|
||||||
|
<Typography className={classes.nodeSpacing}>
|
||||||
|
<span className={classes.bold}>Type:</span> {type}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
{/* Node Phase */}
|
||||||
|
<div className={classes.nodeSpacing}>
|
||||||
|
<div className={classes.heightMaintainer}>
|
||||||
|
<Typography>
|
||||||
|
<span className={classes.bold}>Phase:</span> {phase}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
{/* Node Durations */}
|
||||||
|
<div className={classes.nodeSpacing}>
|
||||||
|
<div className={classes.heightMaintainer}>
|
||||||
|
<Typography>
|
||||||
|
<span className={classes.bold}>Start time:</span>{' '}
|
||||||
|
{timeDifference(startedAt)}
|
||||||
|
</Typography>
|
||||||
|
<Typography>
|
||||||
|
<span className={classes.bold}>End time:</span>{' '}
|
||||||
|
{timeDifference(finishedAt)}
|
||||||
|
</Typography>
|
||||||
|
<Typography>
|
||||||
|
<span className={classes.bold}>Duration: </span>{' '}
|
||||||
|
{(
|
||||||
|
(parseInt(finishedAt, 10) - parseInt(startedAt, 10)) /
|
||||||
|
60
|
||||||
|
).toFixed(1)}{' '}
|
||||||
|
minutes
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
{/* Node Name */}
|
||||||
|
<div className={classes.nodeSpacing}>
|
||||||
|
<div className={classes.heightMaintainer}>
|
||||||
|
<Typography>
|
||||||
|
<span className={classes.bold}>Node Name:</span> {name}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={classes.footerButton}>
|
||||||
|
<ButtonOutline isDisabled={false} handleClick={() => setLogsOpen(true)}>
|
||||||
|
Logs
|
||||||
|
</ButtonOutline>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WorkflowNodeInfo;
|
||||||
|
|
@ -17,6 +17,12 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||||
heightMaintainer: {
|
heightMaintainer: {
|
||||||
lineHeight: '2rem',
|
lineHeight: '2rem',
|
||||||
},
|
},
|
||||||
|
footerButton: {
|
||||||
|
marginLeft: 'auto',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
padding: theme.spacing(3, 4, 4, 0),
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default useStyles;
|
export default useStyles;
|
||||||
Loading…
Reference in New Issue