Added Disable Schedule feature + InputField Fixes + Modifications (#2400)

* Fixed InputFields + Dropdowns
* Added Disable Schedule feature + Modifications
* Fixes DeepScan issue
* Fixed Radio Buttons
* Added to Translation
Signed-off-by: Sayan Mondal <sayan.mondal@mayadata.io>
This commit is contained in:
Sayan Mondal 2021-01-15 10:34:17 +05:30 committed by GitHub
parent cdab6d7798
commit 616eea5a73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 499 additions and 339 deletions

View File

@ -121,7 +121,7 @@ schedule:
backBtn: Go Back backBtn: Go Back
workflowUnderground: workflowUnderground:
heading: 'Click on test to see detailed log of your workflow' heading: 'Click on Info to see details of your workflow'
community: community:
title: 'Community' title: 'Community'
@ -164,7 +164,7 @@ error:
backHome: Go back home backHome: Go back home
workflowDetails: workflowDetails:
detailedLog: Click on test to see detailed log of your workflow detailedLog: Click on Info to see details of your workflow
fetchError: An error has occurred while fetching the data fetchError: An error has occurred while fetching the data
###################################### ######################################
############ Views ############# ############ Views #############
@ -181,6 +181,9 @@ chaosWorkflows:
comparativeResults: Comparative results of selected workflows which ran successfully comparativeResults: Comparative results of selected workflows which ran successfully
once: Once once: Once
seeAnalytics: See analytics seeAnalytics: See analytics
browseSchedule:
regularityOnce: Once
showExperiment: Show Experiment
settings: settings:
accountsTab: accountsTab:
@ -358,7 +361,7 @@ workflowDetailsView:
startTime: Start Time startTime: Start Time
endTime: End Time endTime: End Time
duration: Duration duration: Duration
nodeName: Node Name nodeName: Step Name
button: button:
logs: Logs logs: Logs
@ -383,6 +386,7 @@ createWorkflow:
saved: Name saved as saved: Name saved as
modalHeading: Create your modalHeading: Create your
modalHeadingStrong: workflow name modalHeadingStrong: workflow name
validate: Workflow name can only contain - and alphanumeric characters
label: label:
workflowName: Workflow name workflowName: Workflow name
desc: Description desc: Description
@ -447,6 +451,7 @@ createWorkflow:
info: Before committing the workflow changes to your, verify and if needed go back to a corresponding section of the wizard to modify. info: Before committing the workflow changes to your, verify and if needed go back to a corresponding section of the wizard to modify.
error: Invalid Workflow CRD found ! Please correct the errors. error: Invalid Workflow CRD found ! Please correct the errors.
errYaml: Error in CRD Yaml. errYaml: Error in CRD Yaml.
codeIsFine: Your code is fine. You can move on !
button: button:
edit: Edit edit: Edit
viewYaml: View YAML viewYaml: View YAML
@ -455,6 +460,7 @@ createWorkflow:
workflowName: Workflow name workflowName: Workflow name
desc: Description desc: Description
schedule: Schedule schedule: Schedule
disabled: Currently Disabled
schedulingNow: Scheduling now schedulingNow: Scheduling now
adjustedWeights: Adjusted Weights adjustedWeights: Adjusted Weights
clustername: Cluster Name clustername: Cluster Name

View File

@ -21,7 +21,13 @@ const CustomBreadcrumbs: React.FC<CustomBreadcrumbsProps> = ({ location }) => {
// If Template/Workflow Name is clicked [Workflow / Workflow-name / Template] // If Template/Workflow Name is clicked [Workflow / Workflow-name / Template]
// it would redirect to /workflows // it would redirect to /workflows
if (pathname[2] === 'template' && path === pathname[3]) { if (pathname[2] === 'template' && path === pathname[3]) {
return <span>{path}</span>; return <span key="path">{path}</span>;
}
if (
pathname[2] === 'schedule' &&
(path === pathname[3] || path === pathname[4])
) {
return <span key="schedule">{path}</span>;
} }
const link = ( const link = (
<Link <Link

View File

@ -8,14 +8,16 @@ import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { Link, useLocation } from 'react-router-dom'; import { Link, useLocation } from 'react-router-dom';
import useActions from '../../redux/actions';
import * as TabActions from '../../redux/actions/tabs';
import { history } from '../../redux/configureStore'; import { history } from '../../redux/configureStore';
import { RootState } from '../../redux/reducers'; import { RootState } from '../../redux/reducers';
import { ReactComponent as CommunityIcon } from '../../svg/community.svg'; import { ReactComponent as CommunityIcon } from '../../svg/community.svg';
import { ReactComponent as HomeIcon } from '../../svg/home.svg'; import { ReactComponent as HomeIcon } from '../../svg/home.svg';
import { ReactComponent as MyHubIcon } from '../../svg/myhub.svg';
import { ReactComponent as SettingsIcon } from '../../svg/settings.svg'; import { ReactComponent as SettingsIcon } from '../../svg/settings.svg';
import { ReactComponent as TargetsIcon } from '../../svg/targets.svg'; import { ReactComponent as TargetsIcon } from '../../svg/targets.svg';
import { ReactComponent as WorkflowsIcon } from '../../svg/workflows.svg'; import { ReactComponent as WorkflowsIcon } from '../../svg/workflows.svg';
import { ReactComponent as MyHubIcon } from '../../svg/myhub.svg';
import useStyles from './styles'; import useStyles from './styles';
interface CustomisedListItemProps { interface CustomisedListItemProps {
@ -47,6 +49,7 @@ const CustomisedListItem: React.FC<CustomisedListItemProps> = ({
const SideBar: React.FC = () => { const SideBar: React.FC = () => {
const classes = useStyles(); const classes = useStyles();
const userRole = useSelector((state: RootState) => state.userData.userRole); const userRole = useSelector((state: RootState) => state.userData.userRole);
const tabs = useActions(TabActions);
const { t } = useTranslation(); const { t } = useTranslation();
const pathName = useLocation().pathname.split('/')[1]; const pathName = useLocation().pathname.split('/')[1];
@ -89,6 +92,7 @@ const SideBar: React.FC = () => {
key="workflow" key="workflow"
handleClick={() => { handleClick={() => {
history.push('/workflows'); history.push('/workflows');
tabs.changeWorkflowsTabs(0);
}} }}
label="Workflows" label="Workflows"
selected={pathName === 'workflows'} selected={pathName === 'workflows'}

View File

@ -22,6 +22,7 @@ import * as TemplateSelectionActions from '../../redux/actions/template';
import * as WorkflowActions from '../../redux/actions/workflow'; import * as WorkflowActions from '../../redux/actions/workflow';
import { history } from '../../redux/configureStore'; import { history } from '../../redux/configureStore';
import { RootState } from '../../redux/reducers'; import { RootState } from '../../redux/reducers';
import { validateWorkflowName } from '../../utils/validate';
import { cronWorkflow, workflowOnce } from '../../utils/workflowTemplate'; import { cronWorkflow, workflowOnce } from '../../utils/workflowTemplate';
import parsed from '../../utils/yamlUtils'; import parsed from '../../utils/yamlUtils';
import ChooseWorkflow from '../../views/CreateWorkflow/ChooseWorkflow/index'; import ChooseWorkflow from '../../views/CreateWorkflow/ChooseWorkflow/index';
@ -431,7 +432,11 @@ const CustomStepper = () => {
<Typography>Back</Typography> <Typography>Back</Typography>
</ButtonOutline> </ButtonOutline>
{activeStep === steps.length - 1 ? ( {activeStep === steps.length - 1 ? (
<ButtonFilled handleClick={handleOpen} isPrimary> <ButtonFilled
isDisabled={validateWorkflowName(name)}
handleClick={handleOpen}
isPrimary
>
<div>Finish</div> <div>Finish</div>
</ButtonFilled> </ButtonFilled>
) : ( ) : (

View File

@ -101,13 +101,12 @@ const Routes: React.FC<RoutesProps> = ({ isOwner, isProjectAvailable }) => {
/> />
{/* Redirects */} {/* Redirects */}
<Redirect exact path="/login" to="/" /> <Redirect exact path="/login" to="/" />
<Redirect exact path="/workflows/details" to="/workflows" />
<Redirect exact path="/workflows/schedule" to="/workflows" /> <Redirect exact path="/workflows/schedule" to="/workflows" />
<Redirect exact path="/workflows/template" to="/workflows" /> <Redirect exact path="/workflows/template" to="/workflows" />
<Redirect exact path="/workflows/analytics" to="/workflows" /> <Redirect exact path="/workflows/analytics" to="/workflows" />
<Route <Route
exact exact
path="/workflows/details/:workflowRunId" path="/workflows/:workflowRunId"
component={WorkflowDetails} component={WorkflowDetails}
/> />
<Route <Route

View File

@ -0,0 +1,17 @@
export interface InfoButtonData {
isInfoToggled: boolean;
}
export enum InfoToggledActions {
TOGGLE_BUTTON = 'TOGGLE_BUTTON',
}
interface InfoToggledActionType<T, P> {
type: T;
payload: P;
}
export type InfoToggledAction = InfoToggledActionType<
typeof InfoToggledActions.TOGGLE_BUTTON,
InfoButtonData
>;

View File

@ -36,6 +36,7 @@ export interface WorkflowData {
weights: experimentMap[]; weights: experimentMap[];
isCustomWorkflow: boolean; isCustomWorkflow: boolean;
isRecurring: boolean; isRecurring: boolean;
isDisabled: boolean;
namespace: string; namespace: string;
workflow_id?: string; workflow_id?: string;
clustername: string; clustername: string;

View File

@ -1,9 +1,9 @@
/* eslint-disable react/no-danger */ /* eslint-disable react/no-danger */
import { Typography } from '@material-ui/core'; import { Typography } from '@material-ui/core';
import { InputField } from 'kubera-ui';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import ButtonFilled from '../../components/Button/ButtonFilled'; import ButtonFilled from '../../components/Button/ButtonFilled';
import InputField from '../../components/InputField';
import Loader from '../../components/Loader'; import Loader from '../../components/Loader';
import config from '../../config'; import config from '../../config';
import useActions from '../../redux/actions'; import useActions from '../../redux/actions';
@ -96,9 +96,13 @@ const LoginPage = () => {
? 'Should not start with an empty space' ? 'Should not start with an empty space'
: '' : ''
} }
validationError={validateStartEmptySpacing(authData.username)} variant={
validateStartEmptySpacing(authData.username)
? 'error'
: 'primary'
}
required required
handleChange={(e) => onChange={(e) =>
setAuthData({ setAuthData({
username: e.target.value, username: e.target.value,
password: authData.password, password: authData.password,
@ -106,6 +110,7 @@ const LoginPage = () => {
} }
/> />
</div> </div>
<div aria-details="spacer" style={{ margin: '0.4rem 0' }} />
<div className={classes.inputValue} data-cy="inputPassword"> <div className={classes.inputValue} data-cy="inputPassword">
<InputField <InputField
label="Password" label="Password"
@ -117,8 +122,8 @@ const LoginPage = () => {
? 'Wrong Credentials - Try again with correct username or password' ? 'Wrong Credentials - Try again with correct username or password'
: '' : ''
} }
validationError={isError} variant={isError ? 'error' : 'primary'}
handleChange={(e) => onChange={(e) =>
setAuthData({ setAuthData({
username: authData.username, username: authData.username,
password: e.target.value, password: e.target.value,

View File

@ -63,6 +63,7 @@ const useStyles = makeStyles((theme) => ({
}, },
inputValue: { inputValue: {
marginLeft: theme.spacing(2),
width: '25rem', width: '25rem',
}, },

View File

@ -32,6 +32,7 @@ import * as TemplateSelectionActions from '../../redux/actions/template';
import * as WorkflowActions from '../../redux/actions/workflow'; import * as WorkflowActions from '../../redux/actions/workflow';
import { history } from '../../redux/configureStore'; import { history } from '../../redux/configureStore';
import { RootState } from '../../redux/reducers'; import { RootState } from '../../redux/reducers';
import { validateWorkflowName } from '../../utils/validate';
import parsed from '../../utils/yamlUtils'; import parsed from '../../utils/yamlUtils';
import ChooseWorkflow from '../../views/CreateWorkflow/ChooseWorkflow/index'; import ChooseWorkflow from '../../views/CreateWorkflow/ChooseWorkflow/index';
import ReliablityScore from '../../views/CreateWorkflow/ReliabilityScore'; import ReliablityScore from '../../views/CreateWorkflow/ReliabilityScore';
@ -202,6 +203,7 @@ const EditScheduledWorkflow = () => {
weights, weights,
description, description,
isCustomWorkflow, isCustomWorkflow,
isDisabled,
cronSyntax, cronSyntax,
name, name,
clusterid, clusterid,
@ -274,10 +276,12 @@ const EditScheduledWorkflow = () => {
} }
if ( if (
oldParsedYaml.kind === 'CronWorkflow' && oldParsedYaml.kind === 'CronWorkflow' &&
scheduleType.scheduleOnce !== 'now' scheduleType.scheduleOnce !== 'now' &&
!isDisabled
) { ) {
const newParsedYaml = YAML.parse(yaml); const newParsedYaml = YAML.parse(yaml);
newParsedYaml.spec.schedule = cronSyntax; newParsedYaml.spec.schedule = cronSyntax;
newParsedYaml.spec.suspend = false;
delete newParsedYaml.metadata.generateName; delete newParsedYaml.metadata.generateName;
newParsedYaml.metadata.name = workflowData.name; newParsedYaml.metadata.name = workflowData.name;
newParsedYaml.metadata.labels = { workflow_id: workflowData.workflow_id }; newParsedYaml.metadata.labels = { workflow_id: workflowData.workflow_id };
@ -293,6 +297,21 @@ const EditScheduledWorkflow = () => {
yaml: NewYaml, yaml: NewYaml,
}); });
} }
if (oldParsedYaml.kind === 'CronWorkflow' && isDisabled === true) {
const newParsedYaml = YAML.parse(yaml);
newParsedYaml.spec.suspend = true;
const tz = {
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC',
};
Object.entries(tz).forEach(([key, value]) => {
newParsedYaml.spec[key] = value;
});
NewYaml = YAML.stringify(newParsedYaml);
workflow.setWorkflowDetails({
link: NewLink,
yaml: NewYaml,
});
}
} }
const handleNext = () => { const handleNext = () => {
@ -375,7 +394,6 @@ const EditScheduledWorkflow = () => {
cluster_id: clusterid, cluster_id: clusterid,
}; };
// console.log(chaosWorkFlowInputs);
createChaosWorkFlow({ createChaosWorkFlow({
variables: { ChaosWorkFlowInput: chaosWorkFlowInputs }, variables: { ChaosWorkFlowInput: chaosWorkFlowInputs },
}); });
@ -518,7 +536,11 @@ const EditScheduledWorkflow = () => {
</ButtonOutline> </ButtonOutline>
) : null} ) : null}
{activeStep === steps.length - 1 ? ( {activeStep === steps.length - 1 ? (
<ButtonFilled handleClick={handleOpen} isPrimary> <ButtonFilled
isDisabled={validateWorkflowName(name)}
handleClick={handleOpen}
isPrimary
>
<div>{t('workflowStepper.finish')}</div> <div>{t('workflowStepper.finish')}</div>
</ButtonFilled> </ButtonFilled>
) : ( ) : (

View File

@ -1,93 +1,20 @@
import React from 'react'; import React from 'react';
import { useSelector } from 'react-redux';
import BackButton from '../../components/Button/BackButton'; import BackButton from '../../components/Button/BackButton';
import ButtonFilled from '../../components/Button/ButtonFilled'; import ButtonFilled from '../../components/Button/ButtonFilled';
import ButtonOutline from '../../components/Button/ButtonOutline'; import ButtonOutline from '../../components/Button/ButtonOutline';
import useActions from '../../redux/actions';
import * as ToggleButtonAction from '../../redux/actions/button';
import { RootState } from '../../redux/reducers';
import useStyles from './styles'; import useStyles from './styles';
interface TopNavButtonsProps { const TopNavButtons: React.FC = () => {
isAnalyticsToggled: boolean;
isExportToggled: boolean;
isInfoToggled: boolean;
}
interface Props {
isToggled: TopNavButtonsProps;
setIsToggled: React.Dispatch<React.SetStateAction<TopNavButtonsProps>>;
}
const TopNavButtons: React.FC<Props> = ({ isToggled, setIsToggled }) => {
const classes = useStyles(); const classes = useStyles();
const setFilledButtonState = (buttonName: string, buttonIcon: string) => { const isInfoToggled = useSelector(
return ( (state: RootState) => state.toggleInfoButton.isInfoToggled
<ButtonFilled );
styles={{ height: '2.2rem' }} const toggleButtonAction = useActions(ToggleButtonAction);
isPrimary
handleClick={() =>
setIsToggled({
isAnalyticsToggled: false,
isExportToggled: false,
isInfoToggled: false,
})
}
>
<img
src={`/icons/${buttonIcon}.svg`}
alt={`${buttonName} Icon`}
className={classes.icon}
/>
{buttonName}
</ButtonFilled>
);
};
const setButtonOutlinedState = (
buttonName: string,
buttonIcon: string,
setIsToggleValues: TopNavButtonsProps
) => {
return (
<ButtonOutline
styles={{ height: '2.2rem' }}
isDisabled={false}
handleClick={() => setIsToggled(setIsToggleValues)}
>
<img
src={`/icons/${buttonIcon}.svg`}
alt={`${buttonName} Icon`}
className={classes.icon}
/>
{buttonName}
</ButtonOutline>
);
};
/*
const AnalyticsButton = () =>
isToggled.isAnalyticsToggled
? setFilledButtonState('Analytics', 'show-analytics')
: setButtonOutlinedState('Analytics', 'show-analytics', {
isAnalyticsToggled: true,
isExportToggled: false,
isInfoToggled: false,
});
const ExportButton = () =>
isToggled.isExportToggled
? setFilledButtonState('Export', 'export')
: setButtonOutlinedState('Export', 'export', {
isAnalyticsToggled: false,
isExportToggled: true,
isInfoToggled: false,
});
*/
const InfoButton = () =>
isToggled.isInfoToggled
? setFilledButtonState('Info', 'alignment')
: setButtonOutlinedState('Info', 'alignment', {
isAnalyticsToggled: false,
isExportToggled: false,
isInfoToggled: true,
});
return ( return (
<div className={classes.button}> <div className={classes.button}>
@ -95,9 +22,41 @@ const TopNavButtons: React.FC<Props> = ({ isToggled, setIsToggled }) => {
<BackButton isDisabled={false} /> <BackButton isDisabled={false} />
</div> </div>
<div> <div>
{/* AnalyticsButton() */} {isInfoToggled ? (
{/* ExportButton() */} <ButtonFilled
{InfoButton()} styles={{ height: '2.2rem' }}
isPrimary
handleClick={() =>
toggleButtonAction.toggleInfoButton({
isInfoToggled: false,
})
}
>
<img
src="./icons/alignment.svg"
alt="Info Icon"
className={classes.icon}
/>
Info
</ButtonFilled>
) : (
<ButtonOutline
styles={{ height: '2.2rem' }}
isDisabled={false}
handleClick={() =>
toggleButtonAction.toggleInfoButton({
isInfoToggled: true,
})
}
>
<img
src="./icons/alignment.svg"
alt="Info Icon"
className={classes.icon}
/>
Info
</ButtonOutline>
)}
</div> </div>
</div> </div>
); );

View File

@ -24,31 +24,22 @@ import WorkflowNodeInfo from '../../views/WorkflowDetails/WorkflowNodeInfo';
import useStyles from './styles'; import useStyles from './styles';
import TopNavButtons from './TopNavButtons'; import TopNavButtons from './TopNavButtons';
interface TopNavButtonsProps {
isAnalyticsToggled: boolean;
isExportToggled: boolean;
isInfoToggled: boolean;
}
const WorkflowDetails: React.FC = () => { const WorkflowDetails: React.FC = () => {
const classes = useStyles(); const classes = useStyles();
const [isToggled, setIsToggled] = React.useState<TopNavButtonsProps>({
isAnalyticsToggled: false,
isExportToggled: false,
isInfoToggled: false,
});
const tabs = useActions(TabActions); 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('/')[2];
const { t } = useTranslation(); const { t } = useTranslation();
// get ProjectID // get ProjectID
const selectedProjectID = useSelector( const selectedProjectID = useSelector(
(state: RootState) => state.userData.selectedProjectID (state: RootState) => state.userData.selectedProjectID
); );
const isInfoToggled = useSelector(
(state: RootState) => state.toggleInfoButton.isInfoToggled
);
const workflowDetailsTabValue = useSelector( const workflowDetailsTabValue = useSelector(
(state: RootState) => state.tabNumber.node (state: RootState) => state.tabNumber.node
); );
@ -111,7 +102,7 @@ const WorkflowDetails: React.FC = () => {
return ( return (
<Scaffold> <Scaffold>
<TopNavButtons isToggled={isToggled} setIsToggled={setIsToggled} /> <TopNavButtons />
{/* If workflow data is present then display the workflow details */} {/* If workflow data is present then display the workflow details */}
{workflow ? ( {workflow ? (
<div className={classes.root}> <div className={classes.root}>
@ -128,7 +119,7 @@ const WorkflowDetails: React.FC = () => {
} }
/> />
</div> </div>
{isToggled.isInfoToggled ? ( {isInfoToggled ? (
<div className={classes.workflowSideBar}> <div className={classes.workflowSideBar}>
<AppBar <AppBar
position="static" position="static"
@ -146,7 +137,7 @@ const WorkflowDetails: React.FC = () => {
variant="fullWidth" variant="fullWidth"
> >
<StyledTab label="Workflow" /> <StyledTab label="Workflow" />
<StyledTab label="Nodes" /> <StyledTab label="Steps" />
</Tabs> </Tabs>
</AppBar> </AppBar>
<TabPanel value={workflowDetailsTabValue} index={0}> <TabPanel value={workflowDetailsTabValue} index={0}>

View File

@ -0,0 +1,14 @@
import {
InfoButtonData,
InfoToggledAction,
InfoToggledActions,
} from '../../models/redux/button';
export const toggleInfoButton = (data: InfoButtonData): InfoToggledAction => {
return {
type: InfoToggledActions.TOGGLE_BUTTON,
payload: data,
};
};
export default toggleInfoButton;

View File

@ -0,0 +1,25 @@
/* eslint-disable import/prefer-default-export */
import {
InfoButtonData,
InfoToggledAction,
InfoToggledActions,
} from '../../models/redux/button';
import createReducer from './createReducer';
const initialState: InfoButtonData = {
isInfoToggled: false,
};
export const toggleInfoButton = createReducer<InfoButtonData>(initialState, {
[InfoToggledActions.TOGGLE_BUTTON](
state: InfoButtonData,
action: InfoToggledAction
) {
return {
...state,
...action.payload,
};
},
});
export default toggleInfoButton;

View File

@ -1,24 +1,27 @@
import { combineReducers } from 'redux'; import { combineReducers } from 'redux';
import { AnalyticsData } from '../../models/redux/analytics'; import { AnalyticsData } from '../../models/redux/analytics';
import { InfoButtonData } from '../../models/redux/button';
import { HubDetails } from '../../models/redux/myhub';
import { SelectedNode } from '../../models/redux/nodeSelection'; 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';
import { WorkflowData } from '../../models/redux/workflow'; import { WorkflowData } from '../../models/redux/workflow';
import * as analyticsReducer from './analytics'; import * as analyticsReducer from './analytics';
import * as infoButtonReducer from './button';
import * as hubDetails from './myhub';
import * as nodeSelectionReducer from './nodeSelection'; import * as nodeSelectionReducer from './nodeSelection';
import * as tabsReducer from './tabs'; import * as tabsReducer from './tabs';
import * as templateReducer from './template'; import * as templateReducer from './template';
import * as userReducer from './user'; import * as userReducer from './user';
import * as workflowReducer from './workflow'; import * as workflowReducer from './workflow';
import * as hubDetails from './myhub';
import { HubDetails } from '../../models/redux/myhub';
export interface RootState { export interface RootState {
communityData: AnalyticsData; communityData: AnalyticsData;
userData: UserData; userData: UserData;
workflowData: WorkflowData; workflowData: WorkflowData;
selectedNode: SelectedNode; selectedNode: SelectedNode;
toggleInfoButton: InfoButtonData;
tabNumber: TabState; tabNumber: TabState;
selectTemplate: TemplateData; selectTemplate: TemplateData;
hubDetails: HubDetails; hubDetails: HubDetails;
@ -32,5 +35,6 @@ export default () =>
...nodeSelectionReducer, ...nodeSelectionReducer,
...tabsReducer, ...tabsReducer,
...templateReducer, ...templateReducer,
...infoButtonReducer,
...hubDetails, ...hubDetails,
}); });

View File

@ -15,6 +15,7 @@ const initialState: WorkflowData = {
weights: [], weights: [],
isCustomWorkflow: false, isCustomWorkflow: false,
isRecurring: false, isRecurring: false,
isDisabled: false,
namespace: 'litmus', namespace: 'litmus',
clusterid: '', clusterid: '',
cronSyntax: '', cronSyntax: '',

View File

@ -20,7 +20,7 @@ export const validateEmail = (value: string) => {
}; };
export const validateWorkflowName = (value: string) => { export const validateWorkflowName = (value: string) => {
const workflowValid = /^[a-z0-9._-]+$/g; const workflowValid = /(^[a-z0-9-]{0,55}$)/;
if (value.length > 0) { if (value.length > 0) {
if (value.match(workflowValid)) return false; if (value.match(workflowValid)) return false;
return true; return true;

View File

@ -111,12 +111,28 @@ const TableData: React.FC<TableDataProps> = ({ data, deleteRow }) => {
<> <>
<TableCell className={classes.workflowNameData}> <TableCell className={classes.workflowNameData}>
<Typography> <Typography>
<strong>{data.workflow_name}</strong> <span
className={
YAML.parse(data.workflow_manifest).spec.suspend === true
? classes.dark
: ''
}
>
<strong>{data.workflow_name}</strong>
</span>
</Typography> </Typography>
</TableCell> </TableCell>
<TableCell> <TableCell>
<Typography className={classes.clusterStartDate}> <Typography className={classes.clusterStartDate}>
{formatDate(data.created_at)} <span
className={
YAML.parse(data.workflow_manifest).spec.suspend === true
? classes.dark
: ''
}
>
{formatDate(data.created_at)}
</span>
</Typography> </Typography>
</TableCell> </TableCell>
<TableCell> <TableCell>
@ -124,33 +140,65 @@ const TableData: React.FC<TableDataProps> = ({ data, deleteRow }) => {
<div className={classes.expDiv}> <div className={classes.expDiv}>
<img src="/icons/calender.svg" alt="Calender" /> <img src="/icons/calender.svg" alt="Calender" />
<Typography style={{ paddingLeft: 10 }}> <Typography style={{ paddingLeft: 10 }}>
{data.cronSyntax === '' <span
? 'Once' className={
: cronstrue.toString(data.cronSyntax)} YAML.parse(data.workflow_manifest).spec.suspend === true
? classes.dark
: ''
}
>
{data.cronSyntax === '' ? (
<>{t('chaosWorkflows.browseSchedule.regularityOnce')}</>
) : (
cronstrue.toString(data.cronSyntax)
)}
</span>
</Typography> </Typography>
</div> </div>
</div> </div>
</TableCell> </TableCell>
<TableCell> <TableCell>
<Typography>{data.cluster_name}</Typography> <Typography>
<span
className={
YAML.parse(data.workflow_manifest).spec.suspend === true
? classes.dark
: ''
}
>
{data.cluster_name}
</span>
</Typography>
</TableCell> </TableCell>
<TableCell> <TableCell>
<Button onClick={handlePopOverClick} style={{ textTransform: 'none' }}> <Button onClick={handlePopOverClick} style={{ textTransform: 'none' }}>
{isOpen ? ( <span
<div className={classes.expDiv}> className={
<Typography className={classes.expInfoActive}> YAML.parse(data.workflow_manifest).spec.suspend === true
<strong>Show Experiment</strong> ? classes.dark
</Typography> : ''
<KeyboardArrowDownIcon className={classes.expInfoActiveIcon} /> }
</div> >
) : ( {isOpen ? (
<div className={classes.expDiv}> <div className={classes.expDiv}>
<Typography className={classes.expInfo}> <Typography className={classes.expInfoActive}>
<strong>Show Experiment</strong> <strong>
</Typography> {t('chaosWorkflows.browseSchedule.showExperiment')}
<ChevronRightIcon /> </strong>
</div> </Typography>
)} <KeyboardArrowDownIcon className={classes.expInfoActiveIcon} />
</div>
) : (
<div className={classes.expDiv}>
<Typography className={classes.expInfo}>
<strong>
{t('chaosWorkflows.browseSchedule.showExperiment')}
</strong>
</Typography>
<ChevronRightIcon />
</div>
)}
</span>
</Button> </Button>
<Popover <Popover
id={id} id={id}

View File

@ -166,6 +166,9 @@ const useStyles = makeStyles((theme) => ({
width: '15.1875rem', width: '15.1875rem',
padding: theme.spacing(3.125, 2.6), padding: theme.spacing(3.125, 2.6),
}, },
dark: {
color: theme.palette.text.disabled,
},
weightInfo: { weightInfo: {
display: 'flex', display: 'flex',
flexDirection: 'row', flexDirection: 'row',

View File

@ -110,7 +110,7 @@ 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/${data.workflow_run_id}`);
}} }}
> >
<div className={classes.expDiv} data-cy="workflowDetails"> <div className={classes.expDiv} data-cy="workflowDetails">

View File

@ -1,11 +1,11 @@
import { Typography } from '@material-ui/core'; import { Typography } from '@material-ui/core';
import Divider from '@material-ui/core/Divider'; import Divider from '@material-ui/core/Divider';
import { InputField } from 'kubera-ui';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import ButtonFilled from '../../../components/Button/ButtonFilled'; import ButtonFilled from '../../../components/Button/ButtonFilled';
import ButtonOutline from '../../../components/Button/ButtonOutline'; import ButtonOutline from '../../../components/Button/ButtonOutline';
import InputField from '../../../components/InputField';
import PredifinedWorkflows from '../../../components/PredifinedWorkflows'; import PredifinedWorkflows from '../../../components/PredifinedWorkflows';
import workflowsList from '../../../components/PredifinedWorkflows/data'; import workflowsList from '../../../components/PredifinedWorkflows/data';
import Unimodal from '../../../containers/layouts/Unimodal'; import Unimodal from '../../../containers/layouts/Unimodal';
@ -15,7 +15,7 @@ import * as TemplateSelectionActions from '../../../redux/actions/template';
import * as WorkflowActions from '../../../redux/actions/workflow'; import * as WorkflowActions from '../../../redux/actions/workflow';
import { RootState } from '../../../redux/reducers'; import { RootState } from '../../../redux/reducers';
import { validateWorkflowName } from '../../../utils/validate'; import { validateWorkflowName } from '../../../utils/validate';
import useStyles, { CssTextField } from './styles'; import useStyles from './styles';
// import { getWkfRunCount } from "../../utils"; // import { getWkfRunCount } from "../../utils";
@ -220,42 +220,36 @@ const ChooseWorkflow: React.FC<ChooseWorkflowProps> = ({ isEditable }) => {
<InputField <InputField
// id="filled-workflowname-input" // id="filled-workflowname-input"
label={t('createWorkflow.chooseWorkflow.label.workflowName')} label={t('createWorkflow.chooseWorkflow.label.workflowName')}
disabled={!isEditable}
styles={{
width: '100%',
}}
data-cy="inputWorkflow" data-cy="inputWorkflow"
fullWidth
helperText={ helperText={
validateWorkflowName(workflowDetails.workflowName) validateWorkflowName(workflowDetails.workflowName)
? 'Should not contain spaces or upper case letters' ? t('createWorkflow.chooseWorkflow.validate')
: '' : ''
} }
success={isSuccess.current} variant={
validationError={validateWorkflowName( validateWorkflowName(workflowDetails.workflowName)
workflowDetails.workflowName ? 'error'
)} : 'primary'
// className={classes.textfieldworkflowname} }
handleChange={WorkflowNameChangeHandler} disabled={!isEditable}
onChange={WorkflowNameChangeHandler}
value={workflowDetails.workflowName} value={workflowDetails.workflowName}
/> />
<div className={classes.inputAreaDescription}> <div aria-details="spacer" style={{ margin: '1rem 0' }} />
<CssTextField <InputField
id="filled-workflowdescription-input" id="filled-workflowdescription-input"
label={t('createWorkflow.chooseWorkflow.label.desc')} label={t('createWorkflow.chooseWorkflow.label.desc')}
InputProps={{ fullWidth
disableUnderline: true, InputProps={{
classes: { disableUnderline: true,
input: classes.resize, }}
}, data-cy="inputWorkflowDescription"
}} value={workflowDetails.workflowDesc}
data-cy="inputWorkflowDescription" onChange={WorkflowDescriptionChangeHandler}
className={classes.textfieldworkflowdescription} multiline
value={workflowDetails.workflowDesc} rows={12}
onChange={WorkflowDescriptionChangeHandler} />
multiline
rows={12}
/>
</div>
</div> </div>
<div className={classes.buttons}> <div className={classes.buttons}>
<div className={classes.cancelButton}> <div className={classes.cancelButton}>

View File

@ -1,4 +1,4 @@
import { makeStyles, TextField, withStyles } from '@material-ui/core'; import { makeStyles } from '@material-ui/core';
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
root: { root: {
@ -50,46 +50,11 @@ const useStyles = makeStyles((theme) => ({
marginTop: theme.spacing(5), marginTop: theme.spacing(5),
}, },
inputArea: {
marginTop: theme.spacing(3),
display: 'flex',
textDecoration: 'none',
flexDirection: 'column',
paddingLeft: theme.spacing(2),
paddingBottom: theme.spacing(2.2),
borderRadius: 3,
color: 'rgba(0, 0, 0, 0.2)',
border: '1px solid rgba(0, 0, 0, 0.2)',
},
inputAreaDescription: {
marginTop: theme.spacing(3),
marginBottom: theme.spacing(3),
display: 'flex',
textDecoration: 'none',
flexDirection: 'column',
paddingLeft: theme.spacing(2),
paddingBottom: theme.spacing(2.2),
borderRadius: 3,
color: 'rgba(0, 0, 0, 0.2)',
border: '1px solid rgba(0, 0, 0, 0.2)',
},
textfieldworkflowname: {
marginTop: theme.spacing(1),
paddingLeft: theme.spacing(2),
},
totalWorkflows: { totalWorkflows: {
fontSize: '1rem', fontSize: '1rem',
fontWeight: 500, fontWeight: 500,
}, },
textfieldworkflowdescription: {
marginTop: theme.spacing(2),
paddingLeft: theme.spacing(2),
},
inputDiv: { inputDiv: {
marginTop: theme.spacing(5), marginTop: theme.spacing(5),
}, },
@ -110,10 +75,6 @@ const useStyles = makeStyles((theme) => ({
paddingRight: theme.spacing(1.25), paddingRight: theme.spacing(1.25),
}, },
resize: {
fontSize: '1rem',
},
resizeName: { resizeName: {
fontSize: '0.875rem', fontSize: '0.875rem',
}, },
@ -142,13 +103,4 @@ const useStyles = makeStyles((theme) => ({
}, },
})); }));
export const CssTextField = withStyles({
root: {
'& label.MuiInputLabel-root': {
color: 'rgba(0, 0, 0, 0.6)',
fontSize: 16,
},
},
})(TextField);
export default useStyles; export default useStyles;

View File

@ -17,12 +17,12 @@ import {
Typography, Typography,
} from '@material-ui/core'; } from '@material-ui/core';
import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown'; import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown';
import { InputField } from 'kubera-ui';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import YAML from 'yaml'; import YAML from 'yaml';
import ButtonFilled from '../../../../components/Button/ButtonFilled'; import ButtonFilled from '../../../../components/Button/ButtonFilled';
import InputField from '../../../../components/InputField';
import Loader from '../../../../components/Loader'; import Loader from '../../../../components/Loader';
import { GET_CHARTS_DATA, GET_HUB_STATUS } from '../../../../graphql'; import { GET_CHARTS_DATA, GET_HUB_STATUS } from '../../../../graphql';
import { GET_EXPERIMENT_YAML } from '../../../../graphql/queries'; import { GET_EXPERIMENT_YAML } from '../../../../graphql/queries';
@ -34,8 +34,9 @@ import * as TemplateSelectionActions from '../../../../redux/actions/template';
import * as WorkflowActions from '../../../../redux/actions/workflow'; import * as WorkflowActions from '../../../../redux/actions/workflow';
import { history } from '../../../../redux/configureStore'; import { history } from '../../../../redux/configureStore';
import { RootState } from '../../../../redux/reducers'; import { RootState } from '../../../../redux/reducers';
import { validateWorkflowName } from '../../../../utils/validate';
import BackButton from '../BackButton'; import BackButton from '../BackButton';
import useStyles, { CustomTextField, MenuProps } from './styles'; import useStyles, { MenuProps } from './styles';
interface WorkflowDetails { interface WorkflowDetails {
workflow_name: string; workflow_name: string;
@ -238,12 +239,19 @@ const CreateWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
</Typography> </Typography>
<InputField <InputField
label="Workflow Name" label="Workflow Name"
styles={{ fullWidth
width: '100%',
}}
data-cy="inputWorkflowName" data-cy="inputWorkflowName"
validationError={false} variant={
handleChange={(e) => { validateWorkflowName(workflowData.workflow_name)
? 'error'
: 'primary'
}
helperText={
validateWorkflowName(workflowData.workflow_name)
? t('createWorkflow.chooseWorkflow.validate')
: ''
}
onChange={(e) => {
setWorkflowData({ setWorkflowData({
workflow_name: e.target.value, workflow_name: e.target.value,
workflow_desc: workflowData.workflow_desc, workflow_desc: workflowData.workflow_desc,
@ -257,14 +265,11 @@ const CreateWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
<Typography variant="h6" className={classes.titleText}> <Typography variant="h6" className={classes.titleText}>
{t('customWorkflow.createWorkflow.workflowDesc')}: {t('customWorkflow.createWorkflow.workflowDesc')}:
</Typography> </Typography>
<CustomTextField <InputField
label="Description" label="Description"
data-cy="inputWorkflowDesc" data-cy="inputWorkflowDesc"
InputProps={{ InputProps={{
disableUnderline: true, disableUnderline: true,
classes: {
input: classes.resize,
},
}} }}
onChange={(e) => { onChange={(e) => {
setWorkflowData({ setWorkflowData({
@ -297,7 +302,14 @@ const CreateWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
> >
<FormControlLabel <FormControlLabel
value="construct" value="construct"
control={<Radio />} control={
<Radio
classes={{
root: classes.radio,
checked: classes.checked,
}}
/>
}
disabled={data?.getHubStatus.length === 0} disabled={data?.getHubStatus.length === 0}
label={ label={
<Typography className={classes.radioText}> <Typography className={classes.radioText}>
@ -374,7 +386,7 @@ const CreateWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
}} }}
edge="end" edge="end"
> >
<ArrowDropDownIcon /> <ArrowDropDownIcon color="secondary" />
</IconButton> </IconButton>
</InputAdornment> </InputAdornment>
} }
@ -463,7 +475,11 @@ const CreateWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
<FormControlLabel <FormControlLabel
value="upload" value="upload"
control={<Radio />} control={
<Radio
classes={{ root: classes.radio, checked: classes.checked }}
/>
}
disabled={workflowDetails.customWorkflows.length !== 0} disabled={workflowDetails.customWorkflows.length !== 0}
label={ label={
<Typography className={classes.radioText}> <Typography className={classes.radioText}>

View File

@ -44,7 +44,17 @@ const useStyles = makeStyles((theme) => ({
height: '2.5rem', height: '2.5rem',
fontSize: '0.875rem', fontSize: '0.875rem',
padding: theme.spacing(0.5), padding: theme.spacing(0.5),
'& .MuiSelect-icon': {
color: theme.palette.text.secondary,
},
}, },
radio: {
color: theme.palette.primary.dark,
'&$checked': {
color: theme.palette.primary.dark,
},
},
checked: {},
nextArrow: { nextArrow: {
marginLeft: theme.spacing(2.5), marginLeft: theme.spacing(2.5),
}, },

View File

@ -1,11 +1,11 @@
import { useLazyQuery } from '@apollo/client'; import { useLazyQuery } from '@apollo/client';
import { Typography } from '@material-ui/core'; import { Typography } from '@material-ui/core';
import { InputField } from 'kubera-ui';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import YAML from 'yaml'; import YAML from 'yaml';
import ButtonFilled from '../../../../components/Button/ButtonFilled'; import ButtonFilled from '../../../../components/Button/ButtonFilled';
import InputField from '../../../../components/InputField';
import Loader from '../../../../components/Loader'; import Loader from '../../../../components/Loader';
import { GET_ENGINE_YAML } from '../../../../graphql/queries'; import { GET_ENGINE_YAML } from '../../../../graphql/queries';
import useActions from '../../../../redux/actions'; import useActions from '../../../../redux/actions';
@ -70,14 +70,14 @@ const TuneCustomWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
const changeKey = ( const changeKey = (
index: number, index: number,
event: React.ChangeEvent<HTMLInputElement> event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>
) => { ) => {
overrideEnvs[index].name = event.target.value; overrideEnvs[index].name = event.target.value;
setOverrideEnvs([...overrideEnvs]); setOverrideEnvs([...overrideEnvs]);
}; };
const changeValue = ( const changeValue = (
index: number, index: number,
event: React.ChangeEvent<HTMLInputElement> event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>
) => { ) => {
overrideEnvs[index].value = event.target.value; overrideEnvs[index].value = event.target.value;
setOverrideEnvs([...overrideEnvs]); setOverrideEnvs([...overrideEnvs]);
@ -88,7 +88,7 @@ const TuneCustomWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
const changeOriginalEnvValue = ( const changeOriginalEnvValue = (
index: number, index: number,
event: React.ChangeEvent<HTMLInputElement> event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>
) => { ) => {
env[index].value = event.target.value; env[index].value = event.target.value;
setEnv([...env]); setEnv([...env]);
@ -261,12 +261,9 @@ const TuneCustomWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
<div className={classes.inputField}> <div className={classes.inputField}>
<InputField <InputField
label="appns" label="appns"
styles={{
width: '100%',
}}
data-cy="inputWorkflow" data-cy="inputWorkflow"
validationError={false} variant="primary"
handleChange={(event) => onChange={(event) =>
setAppInfo({ setAppInfo({
...appInfo, ...appInfo,
appns: event.target.value.toLowerCase(), appns: event.target.value.toLowerCase(),
@ -277,6 +274,7 @@ const TuneCustomWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
</div> </div>
</div> </div>
) : null} ) : null}
<div aria-details="spacer" style={{ margin: '1rem' }} />
{YAML.parse(yaml).spec.appinfo?.applabel ? ( {YAML.parse(yaml).spec.appinfo?.applabel ? (
<div className={classes.appInfoDiv}> <div className={classes.appInfoDiv}>
<Typography className={classes.appInfoText}> <Typography className={classes.appInfoText}>
@ -285,12 +283,9 @@ const TuneCustomWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
<div className={classes.inputField}> <div className={classes.inputField}>
<InputField <InputField
label="applabel" label="applabel"
styles={{
width: '100%',
}}
data-cy="inputWorkflow" data-cy="inputWorkflow"
validationError={false} variant="primary"
handleChange={(event) => onChange={(event) =>
setAppInfo({ setAppInfo({
...appInfo, ...appInfo,
applabel: event.target.value.toLowerCase(), applabel: event.target.value.toLowerCase(),
@ -301,6 +296,7 @@ const TuneCustomWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
</div> </div>
</div> </div>
) : null} ) : null}
<div aria-details="spacer" style={{ margin: '1rem' }} />
{YAML.parse(yaml).spec.appinfo?.appkind ? ( {YAML.parse(yaml).spec.appinfo?.appkind ? (
<div className={classes.appKind}> <div className={classes.appKind}>
<Typography className={classes.appInfoText}> <Typography className={classes.appInfoText}>
@ -309,12 +305,9 @@ const TuneCustomWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
<div className={classes.inputField}> <div className={classes.inputField}>
<InputField <InputField
label="appkind" label="appkind"
styles={{
width: '100%',
}}
data-cy="inputWorkflow" data-cy="inputWorkflow"
validationError={false} variant="primary"
handleChange={(event) => onChange={(event) =>
setAppInfo({ setAppInfo({
...appInfo, ...appInfo,
appkind: event.target.value.toLowerCase(), appkind: event.target.value.toLowerCase(),
@ -325,6 +318,7 @@ const TuneCustomWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
</div> </div>
</div> </div>
) : null} ) : null}
<div aria-details="spacer" style={{ margin: '1rem' }} />
{YAML.parse(yaml).spec.annotationCheck ? ( {YAML.parse(yaml).spec.annotationCheck ? (
<div className={classes.appKind}> <div className={classes.appKind}>
<Typography className={classes.appInfoText}> <Typography className={classes.appInfoText}>
@ -333,12 +327,9 @@ const TuneCustomWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
<div className={classes.inputField}> <div className={classes.inputField}>
<InputField <InputField
label="annotationCheck" label="annotationCheck"
styles={{
width: '100%',
}}
data-cy="inputWorkflow" data-cy="inputWorkflow"
validationError={false} variant="primary"
handleChange={(event) => setAnnotation(event.target.value)} onChange={(event) => setAnnotation(event.target.value)}
value={annotation} value={annotation}
/> />
</div> </div>
@ -353,12 +344,9 @@ const TuneCustomWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
<Typography className={classes.envName}>{data.name}</Typography> <Typography className={classes.envName}>{data.name}</Typography>
<InputField <InputField
label="Value" label="Value"
styles={{
width: '40%',
}}
data-cy="inputWorkflow" data-cy="inputWorkflow"
validationError={false} variant="primary"
handleChange={(event) => changeOriginalEnvValue(index, event)} onChange={(event) => changeOriginalEnvValue(index, event)}
value={data.value} value={data.value}
/> />
</div> </div>
@ -374,22 +362,16 @@ const TuneCustomWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
<div className={classes.inputDivEnv}> <div className={classes.inputDivEnv}>
<InputField <InputField
label="Key" label="Key"
styles={{
width: '40%',
}}
data-cy="inputWorkflow" data-cy="inputWorkflow"
validationError={false} variant="primary"
handleChange={(event) => changeKey(index, event)} onChange={(event) => changeKey(index, event)}
value={data.name} value={data.name}
/> />
<InputField <InputField
label="Value" label="Value"
styles={{
width: '40%',
}}
data-cy="inputWorkflow" data-cy="inputWorkflow"
validationError={false} variant="primary"
handleChange={(event) => changeValue(index, event)} onChange={(event) => changeValue(index, event)}
value={data.value} value={data.value}
/> />
{overrideEnvs[index + 1] ? null : ( {overrideEnvs[index + 1] ? null : (

View File

@ -91,9 +91,9 @@ const useStyles = makeStyles((theme) => ({
}, },
inputDivEnv: { inputDivEnv: {
display: 'flex', display: 'flex',
width: '100%',
flexDirection: 'row', flexDirection: 'row',
marginTop: theme.spacing(1.25), marginTop: theme.spacing(1.25),
marginLeft: theme.spacing(-2.5),
}, },
horizontalLineHeader: { horizontalLineHeader: {
border: `1px solid ${theme.palette.border.main}`, border: `1px solid ${theme.palette.border.main}`,

View File

@ -23,7 +23,7 @@ const useStyles = makeStyles((theme) => ({
marginLeft: theme.spacing(15), marginLeft: theme.spacing(15),
marginBottom: theme.spacing(10), marginBottom: theme.spacing(10),
fontSize: '1.6rem', fontSize: '1.6rem',
color: 'red', color: theme.palette.error.main,
}, },
description: { description: {
width: '50rem', width: '50rem',

View File

@ -307,7 +307,7 @@ const ScheduleWorkflow: React.FC = () => {
<div className={classes.scHeader}> <div className={classes.scHeader}>
{/* Upper segment */} {/* Upper segment */}
<div className={classes.scSegments}> <div className={classes.scSegments}>
<div aria-details="content wrapper" style={{ width: '90%' }}> <div>
<Typography className={classes.headerText}> <Typography className={classes.headerText}>
<strong>{t('createWorkflow.scheduleWorkflow.header')}</strong> <strong>{t('createWorkflow.scheduleWorkflow.header')}</strong>
</Typography> </Typography>
@ -319,7 +319,7 @@ const ScheduleWorkflow: React.FC = () => {
</div> </div>
</div> </div>
<img <img
src="./icons/calendar.svg" src="/icons/calendar.svg"
alt="calendar" alt="calendar"
className={classes.calIcon} className={classes.calIcon}
/> />
@ -339,17 +339,33 @@ const ScheduleWorkflow: React.FC = () => {
{!workflowData.isRecurring ? ( {!workflowData.isRecurring ? (
<FormControlLabel <FormControlLabel
value="now" value="now"
control={<Radio />} control={
<Radio
classes={{
root: classes.radio,
checked: classes.checked,
}}
/>
}
label={ label={
<Typography className={classes.radioText}> <Typography className={classes.radioText}>
{t('createWorkflow.scheduleWorkflow.radio.now')} {t('createWorkflow.scheduleWorkflow.radio.now')}
</Typography> </Typography>
} }
/> />
) : YAML.parse(workflowData.yaml).spec.suspend === false ? ( ) : YAML.parse(workflowData.yaml).spec.suspend === true ? (
<></>
) : !workflowData.isDisabled ? (
<FormControlLabel <FormControlLabel
value="disable" value="disable"
control={<Radio />} control={
<Radio
classes={{
root: classes.radio,
checked: classes.checked,
}}
/>
}
label={ label={
<Typography className={classes.radioText}> <Typography className={classes.radioText}>
Disable Schedule Disable Schedule
@ -362,7 +378,7 @@ const ScheduleWorkflow: React.FC = () => {
{/* <FormControlLabel {/* <FormControlLabel
value="specificTime" value="specificTime"
disabled disabled
control={<Radio />} control={<Radio classes={{ root: classes.radio, checked: classes.checked }}/>}
label={ label={
<Typography className={classes.radioText}> <Typography className={classes.radioText}>
{t('createWorkflow.scheduleWorkflow.radio.specific')} {t('createWorkflow.scheduleWorkflow.radio.specific')}
@ -397,7 +413,11 @@ const ScheduleWorkflow: React.FC = () => {
)} )}
<FormControlLabel <FormControlLabel
value="recurringSchedule" value="recurringSchedule"
control={<Radio />} control={
<Radio
classes={{ root: classes.radio, checked: classes.checked }}
/>
}
label={ label={
<Typography className={classes.radioText}> <Typography className={classes.radioText}>
{t('createWorkflow.scheduleWorkflow.radio.recurr')} {t('createWorkflow.scheduleWorkflow.radio.recurr')}
@ -423,7 +443,14 @@ const ScheduleWorkflow: React.FC = () => {
> >
<FormControlLabel <FormControlLabel
value="everyHr" value="everyHr"
control={<Radio />} control={
<Radio
classes={{
root: classes.radio,
checked: classes.checked,
}}
/>
}
label={t('createWorkflow.scheduleWorkflow.every.hr')} label={t('createWorkflow.scheduleWorkflow.every.hr')}
/> />
{valueDef === 'everyHr' ? ( {valueDef === 'everyHr' ? (
@ -468,7 +495,14 @@ const ScheduleWorkflow: React.FC = () => {
)} )}
<FormControlLabel <FormControlLabel
value="everyDay" value="everyDay"
control={<Radio />} control={
<Radio
classes={{
root: classes.radio,
checked: classes.checked,
}}
/>
}
label={t('createWorkflow.scheduleWorkflow.every.day')} label={t('createWorkflow.scheduleWorkflow.every.day')}
/> />
{valueDef === 'everyDay' ? ( {valueDef === 'everyDay' ? (
@ -504,7 +538,14 @@ const ScheduleWorkflow: React.FC = () => {
)} )}
<FormControlLabel <FormControlLabel
value="everyWeek" value="everyWeek"
control={<Radio />} control={
<Radio
classes={{
root: classes.radio,
checked: classes.checked,
}}
/>
}
label={t( label={t(
'createWorkflow.scheduleWorkflow.every.week' 'createWorkflow.scheduleWorkflow.every.week'
)} )}
@ -579,7 +620,14 @@ const ScheduleWorkflow: React.FC = () => {
)} )}
<FormControlLabel <FormControlLabel
value="everyMonth" value="everyMonth"
control={<Radio />} control={
<Radio
classes={{
root: classes.radio,
checked: classes.checked,
}}
/>
}
label={t( label={t(
'createWorkflow.scheduleWorkflow.every.month' 'createWorkflow.scheduleWorkflow.every.month'
)} )}

View File

@ -60,6 +60,14 @@ const useStyles = makeStyles((theme: Theme) => ({
marginTop: theme.spacing(5), marginTop: theme.spacing(5),
}, },
radio: {
color: theme.palette.primary.dark,
'&$checked': {
color: theme.palette.primary.dark,
},
},
checked: {},
/* For recurring schedule options */ /* For recurring schedule options */
scRandom: { scRandom: {
display: 'flex', display: 'flex',

View File

@ -47,6 +47,7 @@ const VerifyCommit: React.FC<VerifyCommitProps> = ({
description, description,
weights, weights,
cronSyntax, cronSyntax,
isDisabled,
clustername, clustername,
} = workflowData; } = workflowData;
@ -97,9 +98,9 @@ const VerifyCommit: React.FC<VerifyCommitProps> = ({
annotations: editorValidations.annotations, annotations: editorValidations.annotations,
}; };
if (stateObject.annotations.length > 0) { if (stateObject.annotations.length > 0) {
setYamlStatus('Error in CRHeloD Yaml.'); setYamlStatus(`${t('createWorkflow.verifyCommit.errYaml')}`);
} else { } else {
setYamlStatus('Your code is fine. You can move on !'); setYamlStatus(`${t('createWorkflow.verifyCommit.codeIsFine')}`);
} }
}, [modified]); }, [modified]);
@ -192,7 +193,11 @@ const VerifyCommit: React.FC<VerifyCommitProps> = ({
ampm ampm
disabled={edit} disabled={edit}
/> */} /> */}
{cronSyntax === '' ? ( {isDisabled ? (
<Typography className={classes.schedule}>
{t('createWorkflow.verifyCommit.summary.disabled')}
</Typography>
) : cronSyntax === '' ? (
<Typography className={classes.schedule}> <Typography className={classes.schedule}>
{t('createWorkflow.verifyCommit.summary.schedulingNow')} {t('createWorkflow.verifyCommit.summary.schedulingNow')}
</Typography> </Typography>

View File

@ -1,18 +1,18 @@
import Slider from '@material-ui/core/Slider'; import Slider from '@material-ui/core/Slider';
import { withStyles } from '@material-ui/core/styles'; import { Theme, withStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography'; import Typography from '@material-ui/core/Typography';
import React from 'react'; import React from 'react';
import useStyles from './styles'; import useStyles from './styles';
const PrettoSlider = withStyles({ const PrettoSlider = withStyles((theme: Theme) => ({
root: { root: {
backgroundColor: 'null', background: 'transparent',
height: 8, height: '0.5rem',
}, },
track: { track: {
background: background:
'linear-gradient(90deg, #5B44BA 0%, #858CDD 49.48%, #109B67 100%)', 'linear-gradient(90deg, #5B44BA 0%, #858CDD 49.48%, #109B67 100%)',
height: 38, height: '2.374rem',
borderRadius: 4, borderRadius: 4,
borderTopRightRadius: 13, borderTopRightRadius: 13,
borderBottomRightRadius: 13, borderBottomRightRadius: 13,
@ -21,29 +21,54 @@ const PrettoSlider = withStyles({
borderBottomRightRadius: 4, borderBottomRightRadius: 4,
}, },
}, },
rail: { thumb: {
height: 38, opacity: 0,
background: '#C9C9CA',
borderRadius: 4,
}, },
mark: { mark: {
marginLeft: theme.spacing(-0.85),
paddingTop: theme.spacing(0.225),
backgroundImage: `url(${'./icons/arrow.svg'})`,
backgroundColor: 'transparent',
'&[data-index="9"]': {
background: 'transparent',
},
backgroundSize: 'cover', backgroundSize: 'cover',
height: 40, height: '2.4375rem',
width: 10, width: '0.75rem',
marginTop: -2, marginTop: theme.spacing(-0.25),
}, },
markActive: { markActive: {
backgroundImage: `url(${'./icons/arrow.svg'})`,
background: 'transparent',
opacity: 1, opacity: 1,
}, },
rail: {
height: '2.375rem',
background: '#C9C9CA',
borderRadius: 4,
},
valueLabel: { valueLabel: {
top: -22, top: -22,
'& *': { '& *': {
background: 'transparent', background: 'transparent',
color: '#000', color: theme.palette.background.paper,
}, },
}, },
})(Slider); markLabel: {
fontFamily: 'Ubuntu',
fontSize: '0.9375rem',
marginTop: theme.spacing(-0.625),
marginLeft: '-5%',
color: 'black',
opacity: 0.4,
},
markLabelActive: {
fontFamily: 'Ubuntu',
fontSize: '0.9375rem',
color: theme.palette.background.paper,
opacity: 1,
},
}))(Slider);
const marks = [ const marks = [
{ {
value: 1, value: 1,
@ -86,14 +111,12 @@ const marks = [
label: '10', label: '10',
}, },
]; ];
interface CustomSliderProps { interface CustomSliderProps {
testName: string; testName: string;
weight: number; weight: number;
index: number; index: number;
handleChange: (newValue: number, index: number) => void; handleChange: (newValue: number, index: number) => void;
} }
const WeightSlider: React.FC<CustomSliderProps> = ({ const WeightSlider: React.FC<CustomSliderProps> = ({
testName, testName,
weight, weight,
@ -125,5 +148,4 @@ const WeightSlider: React.FC<CustomSliderProps> = ({
</div> </div>
); );
}; };
export default WeightSlider; export default WeightSlider;

View File

@ -22,6 +22,10 @@ const useStyles = makeStyles((theme: Theme) => ({
selectText: { selectText: {
height: '2.9rem', height: '2.9rem',
paddingLeft: theme.spacing(1), paddingLeft: theme.spacing(1),
'& .MuiSelect-icon': {
color: theme.palette.text.primary,
},
}, },
formControl: { formControl: {
marginRight: theme.spacing(2.5), marginRight: theme.spacing(2.5),

View File

@ -1,24 +1,24 @@
import { Typography } from '@material-ui/core';
import React, { useState } from 'react';
import { useMutation } from '@apollo/client'; import { useMutation } from '@apollo/client';
import { useSelector } from 'react-redux'; import { Typography } from '@material-ui/core';
import { InputField } from 'kubera-ui';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import BackButton from '../../../components/Button/BackButton'; import BackButton from '../../../components/Button/BackButton';
import ButtonFilled from '../../../components/Button/ButtonFilled'; import ButtonFilled from '../../../components/Button/ButtonFilled';
import InputField from '../../../components/InputField'; import Loader from '../../../components/Loader';
import QuickActionCard from '../../../components/QuickActionCard'; import QuickActionCard from '../../../components/QuickActionCard';
import VideoCarousel from '../../../components/VideoCarousel'; import VideoCarousel from '../../../components/VideoCarousel';
import Scaffold from '../../../containers/layouts/Scaffold'; import Scaffold from '../../../containers/layouts/Scaffold';
import Unimodal from '../../../containers/layouts/Unimodal'; import Unimodal from '../../../containers/layouts/Unimodal';
import { ADD_MY_HUB } from '../../../graphql/mutations';
import { history } from '../../../redux/configureStore';
import { RootState } from '../../../redux/reducers';
import { import {
isValidWebUrl, isValidWebUrl,
validateStartEmptySpacing, validateStartEmptySpacing,
} from '../../../utils/validate'; } from '../../../utils/validate';
import useStyles from './styles'; import useStyles from './styles';
import { history } from '../../../redux/configureStore';
import { ADD_MY_HUB } from '../../../graphql/mutations';
import { RootState } from '../../../redux/reducers';
import Loader from '../../../components/Loader';
interface GitHub { interface GitHub {
HubName: string; HubName: string;
@ -88,7 +88,7 @@ const MyHub = () => {
</Typography> </Typography>
<form id="login-form" autoComplete="on" onSubmit={handleSubmit}> <form id="login-form" autoComplete="on" onSubmit={handleSubmit}>
<div className={classes.inputDiv}> <div className={classes.inputDiv}>
<div className={classes.inputField}> <div className={classes.hubNameInput}>
<InputField <InputField
label="Hub Name" label="Hub Name"
value={gitHub.HubName} value={gitHub.HubName}
@ -97,9 +97,13 @@ const MyHub = () => {
? 'Should not start with an empty space' ? 'Should not start with an empty space'
: '' : ''
} }
validationError={validateStartEmptySpacing(gitHub.HubName)} variant={
validateStartEmptySpacing(gitHub.HubName)
? 'error'
: 'primary'
}
required required
handleChange={(e) => onChange={(e) =>
setGitHub({ setGitHub({
HubName: e.target.value, HubName: e.target.value,
GitURL: gitHub.GitURL, GitURL: gitHub.GitURL,
@ -115,9 +119,9 @@ const MyHub = () => {
helperText={ helperText={
!isValidWebUrl(gitHub.GitURL) ? 'Enter a valid URL' : '' !isValidWebUrl(gitHub.GitURL) ? 'Enter a valid URL' : ''
} }
validationError={!isValidWebUrl(gitHub.GitURL)} variant={isValidWebUrl(gitHub.GitURL) ? 'error' : 'primary'}
required required
handleChange={(e) => onChange={(e) =>
setGitHub({ setGitHub({
HubName: gitHub.HubName, HubName: gitHub.HubName,
GitURL: e.target.value, GitURL: e.target.value,
@ -135,9 +139,13 @@ const MyHub = () => {
? 'Should not start with an empty space' ? 'Should not start with an empty space'
: '' : ''
} }
validationError={validateStartEmptySpacing(gitHub.GitBranch)} variant={
validateStartEmptySpacing(gitHub.GitBranch)
? 'error'
: 'primary'
}
required required
handleChange={(e) => onChange={(e) =>
setGitHub({ setGitHub({
HubName: gitHub.HubName, HubName: gitHub.HubName,
GitURL: gitHub.GitURL, GitURL: gitHub.GitURL,

View File

@ -36,9 +36,14 @@ const useStyles = makeStyles((theme) => ({
marginTop: theme.spacing(2.5), marginTop: theme.spacing(2.5),
marginLeft: theme.spacing(-1.25), marginLeft: theme.spacing(-1.25),
}, },
inputField: { hubNameInput: {
marginBottom: theme.spacing(2.5), marginBottom: theme.spacing(2.5),
width: '25rem', marginLeft: theme.spacing(2),
},
inputField: {
marginLeft: theme.spacing(2),
marginBottom: theme.spacing(2.5),
width: '20rem',
}, },
modalDiv: { modalDiv: {
display: 'flex', display: 'flex',

View File

@ -6,6 +6,7 @@ import ButtonOutline from '../../../components/Button/ButtonOutline';
import DagreGraph, { d3Link, d3Node } from '../../../components/DagreGraph'; 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 ToggleButtonAction from '../../../redux/actions/button';
import * as NodeSelectionActions from '../../../redux/actions/nodeSelection'; import * as NodeSelectionActions from '../../../redux/actions/nodeSelection';
import * as TabActions from '../../../redux/actions/tabs'; import * as TabActions from '../../../redux/actions/tabs';
import { createLabel } from './createLabel'; import { createLabel } from './createLabel';
@ -29,6 +30,7 @@ const ArgoWorkflow: React.FC<ArgoWorkflowProps> = ({ nodes }) => {
// 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 tabs = useActions(TabActions);
const toggleButtonAction = useActions(ToggleButtonAction);
const [graphData, setGraphData] = useState<GraphData>({ const [graphData, setGraphData] = useState<GraphData>({
nodes: [], nodes: [],
@ -140,6 +142,9 @@ const ArgoWorkflow: React.FC<ArgoWorkflowProps> = ({ nodes }) => {
)[0]; )[0];
setSelectedNodeID(nodeID); setSelectedNodeID(nodeID);
tabs.changeWorkflowDetailsTabs(1); tabs.changeWorkflowDetailsTabs(1);
toggleButtonAction.toggleInfoButton({
isInfoToggled: true,
});
}} }}
/> />
</> </>

View File

@ -1,12 +1,12 @@
/* eslint-disable */ /* eslint-disable */
import { Typography } from '@material-ui/core'; import { Typography } from '@material-ui/core';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import ButtonOutline from '../../../components/Button/ButtonOutline'; import ButtonOutline from '../../../components/Button/ButtonOutline';
import { RootState } from '../../../redux/reducers'; import { RootState } from '../../../redux/reducers';
import timeDifference from '../../../utils/datesModifier'; import timeDifference from '../../../utils/datesModifier';
import NodeLogs from '../NodeLogs'; import NodeLogs from '../NodeLogs';
import { useTranslation } from 'react-i18next';
import useStyles from './styles'; import useStyles from './styles';
interface WorkflowNodeInfoProps { interface WorkflowNodeInfoProps {
@ -51,16 +51,6 @@ const WorkflowNodeInfo: React.FC<WorkflowNodeInfoProps> = ({
<></> <></>
)} )}
{/* Node Name */}
<div className={classes.heightMaintainer}>
<Typography className={classes.nodeSpacing}>
<span className={classes.bold}>
{t('workflowDetailsView.workflowNodeInfo.name')}:
</span>
<br />
{pod_name}
</Typography>
</div>
{/* Node Type */} {/* Node Type */}
<div className={classes.heightMaintainer}> <div className={classes.heightMaintainer}>
<Typography className={classes.nodeSpacing}> <Typography className={classes.nodeSpacing}>
@ -120,12 +110,12 @@ const WorkflowNodeInfo: React.FC<WorkflowNodeInfoProps> = ({
</div> </div>
</div> </div>
<hr /> <hr />
{/* Node Name */} {/* Step Name */}
<div className={classes.nodeSpacing}> <div className={classes.nodeSpacing}>
<div className={classes.heightMaintainer}> <div className={classes.heightMaintainer}>
<Typography> <Typography>
<span className={classes.bold}> <span className={classes.bold}>
{t('workflowDetailsView.workflowNodeInfo.nodeName')}: {t('workflowDetailsView.workflowNodeInfo.stepName')}:
</span>{' '} </span>{' '}
{name} {name}
</Typography> </Typography>