chore(scheduling): Added Edit Schedule Functionality + Fixed a CronWorkflow Bug 🐛 (#2657)
* Added Edit Schedule Functionality + Fixed a CronWorkflow Bug 🐛
* Reverting Sidebar text color
* Resolved comments/suggestions
* Adding missing translation
Signed-off-by: Sayan Mondal <sayan@chaosnative.com>
This commit is contained in:
parent
43d0e02810
commit
1a8e67e61d
|
|
@ -132,7 +132,7 @@ schedule:
|
||||||
headingDesc: 'Click on test to see detailed log of your workflow'
|
headingDesc: 'Click on test to see detailed log of your workflow'
|
||||||
headingText: 'Schedule details:'
|
headingText: 'Schedule details:'
|
||||||
description: 'Choose the right time to schedule your first workflow. Below you can find some options that may be convenient for you.'
|
description: 'Choose the right time to schedule your first workflow. Below you can find some options that may be convenient for you.'
|
||||||
save: 'Save Changes'
|
save: 'Save Changes'
|
||||||
scheduleNow: Schedule now
|
scheduleNow: Schedule now
|
||||||
scheduleAfter: Schedule after some time
|
scheduleAfter: Schedule after some time
|
||||||
scheduleAfterSometime: 'Choose the minutes, hours, or days when you want to start workflow'
|
scheduleAfterSometime: 'Choose the minutes, hours, or days when you want to start workflow'
|
||||||
|
|
@ -154,6 +154,15 @@ schedule:
|
||||||
workflowBtn: Go to Workflow
|
workflowBtn: Go to Workflow
|
||||||
backBtn: Go Back
|
backBtn: Go Back
|
||||||
|
|
||||||
|
editSchedule:
|
||||||
|
title: Edit Schedule
|
||||||
|
verify: Verify
|
||||||
|
cancel: Cancel
|
||||||
|
save: Save Changes
|
||||||
|
edit: Edit
|
||||||
|
theSchedule: The Schedule
|
||||||
|
successfullyCreated: is successfully created
|
||||||
|
|
||||||
community:
|
community:
|
||||||
title: 'Community'
|
title: 'Community'
|
||||||
heading: 'Litmus Insights'
|
heading: 'Litmus Insights'
|
||||||
|
|
@ -794,6 +803,8 @@ createWorkflow:
|
||||||
errYaml: Error in CRD Yaml.
|
errYaml: Error in CRD Yaml.
|
||||||
codeIsFine: Your code is fine.
|
codeIsFine: Your code is fine.
|
||||||
youCanMoveOn: You can move on !
|
youCanMoveOn: You can move on !
|
||||||
|
test: test
|
||||||
|
YAML: YAML
|
||||||
button:
|
button:
|
||||||
edit: Edit
|
edit: Edit
|
||||||
viewYaml: View YAML
|
viewYaml: View YAML
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,8 @@
|
||||||
import { Card, CardActionArea, Typography } from '@material-ui/core';
|
import { Card, CardActionArea, Typography } from '@material-ui/core';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import useActions from '../../redux/actions';
|
|
||||||
import * as TemplateSelectionActions from '../../redux/actions/template';
|
|
||||||
import * as WorkflowActions from '../../redux/actions/workflow';
|
|
||||||
import { history } from '../../redux/configureStore';
|
|
||||||
import { ReactComponent as Arrow } from '../../svg/arrow.svg';
|
import { ReactComponent as Arrow } from '../../svg/arrow.svg';
|
||||||
import { ReactComponent as ArrowDisabled } from '../../svg/arrow_disabled.svg';
|
import { ReactComponent as ArrowDisabled } from '../../svg/arrow_disabled.svg';
|
||||||
import { getProjectID, getProjectRole } from '../../utils/getSearchParams';
|
|
||||||
import useStyles from './styles';
|
import useStyles from './styles';
|
||||||
|
|
||||||
interface CreateWorkflowCardProps {
|
interface CreateWorkflowCardProps {
|
||||||
|
|
@ -19,28 +14,9 @@ const CreateWorkflowCard: React.FC<CreateWorkflowCardProps> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const classes = useStyles({ isDisabled });
|
const classes = useStyles({ isDisabled });
|
||||||
const template = useActions(TemplateSelectionActions);
|
|
||||||
const projectID = getProjectID();
|
|
||||||
const userRole = getProjectRole();
|
|
||||||
const workflowAction = useActions(WorkflowActions);
|
|
||||||
const handleCreateWorkflow = () => {
|
|
||||||
workflowAction.setWorkflowDetails({
|
|
||||||
isCustomWorkflow: false,
|
|
||||||
customWorkflows: [],
|
|
||||||
});
|
|
||||||
template.selectTemplate({ selectedTemplateID: 0, isDisable: true });
|
|
||||||
history.push({
|
|
||||||
pathname: '/create-workflow',
|
|
||||||
search: `?projectID=${projectID}&projectRole=${userRole}`,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card className={classes.createCard} data-cy="createWorkflow">
|
||||||
onClick={isDisabled ? () => {} : handleCreateWorkflow}
|
|
||||||
className={classes.createCard}
|
|
||||||
data-cy="createWorkflow"
|
|
||||||
>
|
|
||||||
<CardActionArea className={classes.createCardAction}>
|
<CardActionArea className={classes.createCardAction}>
|
||||||
<Typography className={classes.createCardHeading}>
|
<Typography className={classes.createCardHeading}>
|
||||||
{t('home.workflow.heading')}
|
{t('home.workflow.heading')}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
import { QuickActionCard } from 'litmus-ui';
|
import { QuickActionCard } from 'litmus-ui';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { constants } from '../../constants';
|
||||||
import { Role } from '../../models/graphql/user';
|
import { Role } from '../../models/graphql/user';
|
||||||
import useActions from '../../redux/actions';
|
import useActions from '../../redux/actions';
|
||||||
import * as TabActions from '../../redux/actions/tabs';
|
import * as TabActions from '../../redux/actions/tabs';
|
||||||
|
|
@ -97,7 +98,7 @@ const LocalQuickActionCard: React.FC<LocalQuickActionCardProps> = ({
|
||||||
? {
|
? {
|
||||||
src: '/icons/survey.svg',
|
src: '/icons/survey.svg',
|
||||||
alt: 'survey',
|
alt: 'survey',
|
||||||
onClick: () => window.open('https://forms.gle/qMuVphRyEWCFqjD56'),
|
onClick: () => window.open(constants.FeedbackForm),
|
||||||
text: t('quickActionCard.quickSurvey'),
|
text: t('quickActionCard.quickSurvey'),
|
||||||
}
|
}
|
||||||
: emptyData,
|
: emptyData,
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,8 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||||
},
|
},
|
||||||
drawerPaper: {
|
drawerPaper: {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
backgroundColor: theme.palette.background.default,
|
backgroundColor: theme.palette.sidebarMenu,
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
color: 'inherit',
|
|
||||||
},
|
},
|
||||||
litmusDiv: {
|
litmusDiv: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
export const constants = {
|
||||||
|
// Litmus HomePage [Component Used in -> LocalQuickActionCard]
|
||||||
|
FeedbackForm: 'https://forms.gle/KQp5qj8MUneMSxLp9',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule & Edit Schedule [Component Used in -> views/ScheduleWorkflow,
|
||||||
|
* pages/EditSchedule/Schedule]
|
||||||
|
* */
|
||||||
|
recurringEveryHour: 'everyHr',
|
||||||
|
recurringEveryDay: 'everyDay',
|
||||||
|
recurringEveryWeek: 'everyWeek',
|
||||||
|
recurringEveryMonth: 'everyMonth',
|
||||||
|
};
|
||||||
|
|
@ -25,6 +25,8 @@ const HomePage = lazy(() => import('../../pages/HomePage'));
|
||||||
const Community = lazy(() => import('../../pages/Community'));
|
const Community = lazy(() => import('../../pages/Community'));
|
||||||
const Settings = lazy(() => import('../../pages/Settings'));
|
const Settings = lazy(() => import('../../pages/Settings'));
|
||||||
const Targets = lazy(() => import('../../pages/Targets'));
|
const Targets = lazy(() => import('../../pages/Targets'));
|
||||||
|
const EditSchedule = lazy(() => import('../../pages/EditSchedule'));
|
||||||
|
const SetNewSchedule = lazy(() => import('../../pages/EditSchedule/Schedule'));
|
||||||
const ConnectTargets = lazy(() => import('../../pages/ConnectTarget'));
|
const ConnectTargets = lazy(() => import('../../pages/ConnectTarget'));
|
||||||
const AnalyticsPage = lazy(() => import('../../pages/AnalyticsPage'));
|
const AnalyticsPage = lazy(() => import('../../pages/AnalyticsPage'));
|
||||||
const AnalyticsDashboard = lazy(
|
const AnalyticsDashboard = lazy(
|
||||||
|
|
@ -194,11 +196,16 @@ const Routes: React.FC = () => {
|
||||||
path="/workflows/:workflowRunId"
|
path="/workflows/:workflowRunId"
|
||||||
component={WorkflowDetails}
|
component={WorkflowDetails}
|
||||||
/>
|
/>
|
||||||
{/* <Route
|
<Route
|
||||||
exact
|
exact
|
||||||
path="/workflows/schedule/:scheduleProjectID/:workflowName" // Check
|
path="/workflows/schedule/:scheduleProjectID/:workflowName"
|
||||||
component={SchedulePage}
|
component={EditSchedule}
|
||||||
/> */}
|
/>
|
||||||
|
<Route
|
||||||
|
exact
|
||||||
|
path="/workflows/schedule/:scheduleProjectID/:workflowName/set"
|
||||||
|
component={SetNewSchedule}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
exact
|
exact
|
||||||
path="/workflows/template/:templateName"
|
path="/workflows/template/:templateName"
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,6 @@ export interface customWorkflow {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WorkflowData {
|
export interface WorkflowData {
|
||||||
id: string;
|
|
||||||
isRecurring: boolean;
|
|
||||||
isDisabled: boolean;
|
|
||||||
chaosEngineChanged: boolean;
|
chaosEngineChanged: boolean;
|
||||||
namespace: string;
|
namespace: string;
|
||||||
workflow_id?: string;
|
workflow_id?: string;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,767 @@
|
||||||
|
import {
|
||||||
|
Divider,
|
||||||
|
FormControl,
|
||||||
|
FormControlLabel,
|
||||||
|
Radio,
|
||||||
|
RadioGroup,
|
||||||
|
Select,
|
||||||
|
Typography,
|
||||||
|
} from '@material-ui/core';
|
||||||
|
import { ButtonFilled, ButtonOutlined } from 'litmus-ui';
|
||||||
|
import localforage from 'localforage';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import YAML from 'yaml';
|
||||||
|
import BackButton from '../../components/Button/BackButton';
|
||||||
|
import CustomDate from '../../components/DateTime/CustomDate/index';
|
||||||
|
import CustomTime from '../../components/DateTime/CustomTime/index';
|
||||||
|
import { constants } from '../../constants';
|
||||||
|
import Scaffold from '../../containers/layouts/Scaffold';
|
||||||
|
import { WorkflowData } from '../../models/redux/workflow';
|
||||||
|
import useActions from '../../redux/actions';
|
||||||
|
import * as TemplateSelectionActions from '../../redux/actions/template';
|
||||||
|
import * as WorkflowActions from '../../redux/actions/workflow';
|
||||||
|
import { history } from '../../redux/configureStore';
|
||||||
|
import { RootState } from '../../redux/reducers';
|
||||||
|
import { getProjectID, getProjectRole } from '../../utils/getSearchParams';
|
||||||
|
import { cronWorkflow, workflowOnce } from '../../utils/workflowTemplate';
|
||||||
|
import { fetchWorkflowNameFromManifest } from '../../utils/yamlUtils';
|
||||||
|
import SetTime from '../../views/CreateWorkflow/ScheduleWorkflow/SetTime';
|
||||||
|
import useStyles from '../../views/CreateWorkflow/ScheduleWorkflow/styles';
|
||||||
|
|
||||||
|
interface ScheduleSyntax {
|
||||||
|
minute: string | undefined;
|
||||||
|
hour: string | undefined;
|
||||||
|
day_month: string | undefined;
|
||||||
|
month: string | undefined;
|
||||||
|
day_week: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ScheduleWorkflow = () => {
|
||||||
|
// Initial Cron State
|
||||||
|
const [cronValue, setCronValue] = useState<ScheduleSyntax>({
|
||||||
|
minute: '*',
|
||||||
|
hour: '*',
|
||||||
|
day_month: '*',
|
||||||
|
month: '*',
|
||||||
|
day_week: '*',
|
||||||
|
});
|
||||||
|
|
||||||
|
const manifest = useSelector(
|
||||||
|
(state: RootState) => state.workflowManifest.manifest
|
||||||
|
);
|
||||||
|
|
||||||
|
// Redux States for Schedule
|
||||||
|
const workflowData: WorkflowData = useSelector(
|
||||||
|
(state: RootState) => state.workflowData
|
||||||
|
);
|
||||||
|
|
||||||
|
const { cronSyntax, scheduleType } = workflowData;
|
||||||
|
|
||||||
|
const workflowAction = useActions(WorkflowActions);
|
||||||
|
const template = useActions(TemplateSelectionActions);
|
||||||
|
// Controls Radio Buttons
|
||||||
|
const [value, setValue] = React.useState(
|
||||||
|
workflowData.scheduleType.scheduleOnce
|
||||||
|
);
|
||||||
|
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setValue(event.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Controls inner radio buttons of Recurring Schedule
|
||||||
|
const [valueDef, setValueDef] = React.useState(
|
||||||
|
workflowData.scheduleType.recurringSchedule
|
||||||
|
);
|
||||||
|
const handleChangeInstance = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setValueDef(event.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const scheduleOnce = workflowOnce;
|
||||||
|
const scheduleMore = cronWorkflow;
|
||||||
|
|
||||||
|
function EditYaml() {
|
||||||
|
const oldParsedYaml = YAML.parse(manifest);
|
||||||
|
let NewYaml: string;
|
||||||
|
if (
|
||||||
|
oldParsedYaml.kind === 'Workflow' &&
|
||||||
|
scheduleType.scheduleOnce !== 'now'
|
||||||
|
) {
|
||||||
|
const oldParsedYaml = YAML.parse(manifest);
|
||||||
|
const newParsedYaml = YAML.parse(scheduleMore);
|
||||||
|
delete newParsedYaml.spec.workflowSpec;
|
||||||
|
newParsedYaml.spec.schedule = cronSyntax;
|
||||||
|
delete newParsedYaml.metadata.generateName;
|
||||||
|
newParsedYaml.metadata.name = fetchWorkflowNameFromManifest(manifest);
|
||||||
|
newParsedYaml.metadata.labels = {
|
||||||
|
workflow_id: workflowData.workflow_id,
|
||||||
|
};
|
||||||
|
newParsedYaml.spec.workflowSpec = oldParsedYaml.spec;
|
||||||
|
const tz = {
|
||||||
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC',
|
||||||
|
};
|
||||||
|
Object.entries(tz).forEach(([key, value]) => {
|
||||||
|
newParsedYaml.spec[key] = value;
|
||||||
|
});
|
||||||
|
NewYaml = YAML.stringify(newParsedYaml);
|
||||||
|
workflowAction.setWorkflowManifest({
|
||||||
|
manifest: NewYaml,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
oldParsedYaml.kind === 'CronWorkflow' &&
|
||||||
|
scheduleType.scheduleOnce === 'now'
|
||||||
|
) {
|
||||||
|
const oldParsedYaml = YAML.parse(manifest);
|
||||||
|
const newParsedYaml = YAML.parse(scheduleOnce);
|
||||||
|
delete newParsedYaml.spec;
|
||||||
|
delete newParsedYaml.metadata.generateName;
|
||||||
|
newParsedYaml.metadata.name = fetchWorkflowNameFromManifest(manifest);
|
||||||
|
newParsedYaml.spec = oldParsedYaml.spec.workflowSpec;
|
||||||
|
newParsedYaml.metadata.labels = {
|
||||||
|
workflow_id: workflowData.workflow_id,
|
||||||
|
};
|
||||||
|
NewYaml = YAML.stringify(newParsedYaml);
|
||||||
|
workflowAction.setWorkflowManifest({
|
||||||
|
manifest: NewYaml,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
oldParsedYaml.kind === 'CronWorkflow' &&
|
||||||
|
scheduleType.scheduleOnce !== 'now'
|
||||||
|
// && !isDisabled
|
||||||
|
) {
|
||||||
|
const newParsedYaml = YAML.parse(manifest);
|
||||||
|
newParsedYaml.spec.schedule = cronSyntax;
|
||||||
|
// newParsedYaml.spec.suspend = false;
|
||||||
|
delete newParsedYaml.metadata.generateName;
|
||||||
|
newParsedYaml.metadata.name = fetchWorkflowNameFromManifest(manifest);
|
||||||
|
newParsedYaml.metadata.labels = { workflow_id: workflowData.workflow_id };
|
||||||
|
const tz = {
|
||||||
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC',
|
||||||
|
};
|
||||||
|
Object.entries(tz).forEach(([key, value]) => {
|
||||||
|
newParsedYaml.spec[key] = value;
|
||||||
|
});
|
||||||
|
NewYaml = YAML.stringify(newParsedYaml);
|
||||||
|
workflowAction.setWorkflowManifest({
|
||||||
|
manifest: 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);
|
||||||
|
// workflowAction.setWorkflowDetails({
|
||||||
|
// link: NewLink,
|
||||||
|
// yaml: NewYaml,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
localforage.setItem('editSchedule', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseEffect to update the cron syntax with changes
|
||||||
|
useEffect(() => {
|
||||||
|
const cronSyntax = `${cronValue.minute} ${cronValue.hour} ${cronValue.day_month} ${cronValue.month} ${cronValue.day_week}`;
|
||||||
|
if (value === 'now')
|
||||||
|
workflowAction.setWorkflowDetails({
|
||||||
|
cronSyntax: '',
|
||||||
|
});
|
||||||
|
else
|
||||||
|
workflowAction.setWorkflowDetails({
|
||||||
|
cronSyntax,
|
||||||
|
});
|
||||||
|
}, [cronValue]);
|
||||||
|
|
||||||
|
const classes = useStyles();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
// Sets individual minutes
|
||||||
|
const [minute, setMinute] = React.useState(
|
||||||
|
workflowData.scheduleInput.hour_interval
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sets Weekdays
|
||||||
|
const [days, setDays] = React.useState(workflowData.scheduleInput.weekday);
|
||||||
|
|
||||||
|
// Sets Day in number
|
||||||
|
const [dates, setDates] = React.useState(workflowData.scheduleInput.day);
|
||||||
|
|
||||||
|
// Sets Time
|
||||||
|
const [selectedTime, setSelectedTime] = React.useState<Date | null>(
|
||||||
|
new Date(workflowData.scheduleInput.time)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sets Date
|
||||||
|
const [selectedDate, setSelectedDate] = React.useState<Date | null>(
|
||||||
|
new Date(workflowData.scheduleInput.date)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Function to validate the date and time for "Specific Time" radio button
|
||||||
|
const validateTime = (time: Date | null, date: Date | null) => {
|
||||||
|
if (
|
||||||
|
value === 'specificTime' &&
|
||||||
|
(time?.setSeconds(0) as number) <= new Date().setSeconds(0) &&
|
||||||
|
(date?.getTime() as number) <= new Date().getTime()
|
||||||
|
) {
|
||||||
|
const newTime = new Date();
|
||||||
|
newTime.setMinutes(newTime.getMinutes() + 5);
|
||||||
|
setSelectedTime(newTime);
|
||||||
|
workflowAction.setWorkflowDetails({
|
||||||
|
scheduleInput: {
|
||||||
|
...workflowData.scheduleInput,
|
||||||
|
date,
|
||||||
|
time: newTime,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
setCronValue({
|
||||||
|
...cronValue,
|
||||||
|
minute: newTime.getMinutes().toString(),
|
||||||
|
hour: newTime.getHours().toString(),
|
||||||
|
day_month: date?.getDate().toString(),
|
||||||
|
month: (date && date.getMonth() + 1)?.toString(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
workflowAction.setWorkflowDetails({
|
||||||
|
scheduleInput: {
|
||||||
|
...workflowData.scheduleInput,
|
||||||
|
date,
|
||||||
|
time,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
setCronValue({
|
||||||
|
...cronValue,
|
||||||
|
minute: time?.getMinutes().toString(),
|
||||||
|
hour: time?.getHours().toString(),
|
||||||
|
day_month: date?.getDate().toString(),
|
||||||
|
month: (date && date.getMonth() + 1)?.toString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTimeChange = (time: Date | null) => {
|
||||||
|
setSelectedTime(time);
|
||||||
|
validateTime(time, selectedDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDateChange = (date: Date | null) => {
|
||||||
|
setSelectedDate(date);
|
||||||
|
validateTime(selectedTime, date);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function for recurring date change
|
||||||
|
const reccuringDateChange = (date: Date | null) => {
|
||||||
|
setSelectedTime(date);
|
||||||
|
setCronValue({
|
||||||
|
...cronValue,
|
||||||
|
minute: date?.getMinutes().toString(),
|
||||||
|
hour: date?.getHours().toString(),
|
||||||
|
});
|
||||||
|
workflowAction.setWorkflowDetails({
|
||||||
|
scheduleInput: {
|
||||||
|
...workflowData.scheduleInput,
|
||||||
|
time: date,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Stores dates in an array
|
||||||
|
const names: number[] = [1];
|
||||||
|
for (let i = 1; i <= 30; i += 1) {
|
||||||
|
names[i] = i + 1;
|
||||||
|
}
|
||||||
|
const mins: number[] = [0];
|
||||||
|
for (let i = 0; i <= 59; i += 1) {
|
||||||
|
mins[i] = i;
|
||||||
|
}
|
||||||
|
// Day names
|
||||||
|
const weekdays: string[] = [
|
||||||
|
'Monday',
|
||||||
|
'Tuesday',
|
||||||
|
'Wednesday',
|
||||||
|
'Thursday',
|
||||||
|
'Friday',
|
||||||
|
'Saturday',
|
||||||
|
'Sunday',
|
||||||
|
];
|
||||||
|
|
||||||
|
// UseEffect to update the values of CronSyntax on radio button change
|
||||||
|
useEffect(() => {
|
||||||
|
if (value === 'now') {
|
||||||
|
setValueDef('');
|
||||||
|
setCronValue({
|
||||||
|
minute: '',
|
||||||
|
hour: '',
|
||||||
|
day_month: '',
|
||||||
|
month: '',
|
||||||
|
day_week: '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (value === 'specificTime') {
|
||||||
|
setValueDef('');
|
||||||
|
setCronValue({
|
||||||
|
minute: selectedTime?.getMinutes().toString(),
|
||||||
|
hour: selectedTime?.getHours().toString(),
|
||||||
|
day_month: selectedDate?.getDate().toString(),
|
||||||
|
month: (selectedDate && selectedDate.getMonth() + 1)?.toString(),
|
||||||
|
day_week: '*',
|
||||||
|
});
|
||||||
|
if (workflowData.scheduleInput.time <= new Date()) {
|
||||||
|
const newTime = new Date();
|
||||||
|
newTime.setMinutes(newTime.getMinutes() + 5);
|
||||||
|
setSelectedTime(newTime);
|
||||||
|
setCronValue({
|
||||||
|
minute: newTime.getMinutes().toString(),
|
||||||
|
hour: newTime.getHours().toString(),
|
||||||
|
day_month: selectedDate?.getDate().toString(),
|
||||||
|
month: (selectedDate && selectedDate.getMonth() + 1)?.toString(),
|
||||||
|
day_week: '*',
|
||||||
|
});
|
||||||
|
workflowAction.setWorkflowDetails({
|
||||||
|
scheduleInput: {
|
||||||
|
...workflowData.scheduleInput,
|
||||||
|
time: newTime,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (valueDef === constants.recurringEveryHour) {
|
||||||
|
setCronValue({
|
||||||
|
minute: minute.toString(),
|
||||||
|
hour: '0-23',
|
||||||
|
day_month: '*',
|
||||||
|
month: '*',
|
||||||
|
day_week: '*',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (valueDef === constants.recurringEveryDay) {
|
||||||
|
setCronValue({
|
||||||
|
minute: selectedTime?.getMinutes().toString(),
|
||||||
|
hour: selectedTime?.getHours().toString(),
|
||||||
|
day_month: '*',
|
||||||
|
month: '*',
|
||||||
|
day_week: '0-6',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (valueDef === constants.recurringEveryWeek) {
|
||||||
|
setCronValue({
|
||||||
|
minute: selectedTime?.getMinutes().toString(),
|
||||||
|
hour: selectedTime?.getHours().toString(),
|
||||||
|
day_month: '*',
|
||||||
|
month: '*',
|
||||||
|
day_week: days.slice(0, 3),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (valueDef === constants.recurringEveryMonth) {
|
||||||
|
setCronValue({
|
||||||
|
minute: selectedTime?.getMinutes().toString(),
|
||||||
|
hour: selectedTime?.getHours().toString(),
|
||||||
|
day_month: dates.toString(),
|
||||||
|
month: '*',
|
||||||
|
day_week: '*',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (value === 'recurringSchedule' && valueDef === '') {
|
||||||
|
template.selectTemplate({ isDisable: true });
|
||||||
|
} else {
|
||||||
|
template.selectTemplate({ isDisable: false });
|
||||||
|
}
|
||||||
|
workflowAction.setWorkflowDetails({
|
||||||
|
scheduleType: {
|
||||||
|
scheduleOnce: value,
|
||||||
|
recurringSchedule: valueDef,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, [valueDef, value]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
EditYaml();
|
||||||
|
}, [cronValue]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Scaffold>
|
||||||
|
<BackButton />
|
||||||
|
<Typography className={classes.title}>
|
||||||
|
{t('editSchedule.title')}
|
||||||
|
</Typography>
|
||||||
|
<div className={classes.root}>
|
||||||
|
<div className={classes.innerContainer}>
|
||||||
|
<br />
|
||||||
|
{/* Upper segment */}
|
||||||
|
<div className={classes.scSegments}>
|
||||||
|
<div>
|
||||||
|
<Typography className={classes.headerText}>
|
||||||
|
<strong>{t('createWorkflow.scheduleWorkflow.header')}</strong>
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<div className={classes.schBody}>
|
||||||
|
<Typography align="left" className={classes.description}>
|
||||||
|
{t('createWorkflow.scheduleWorkflow.info')}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<img
|
||||||
|
src="/icons/calendarWorkflowIcon.svg"
|
||||||
|
alt="calendar"
|
||||||
|
className={classes.calIcon}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
{/* Lower segment */}
|
||||||
|
<div className={classes.scFormControl}>
|
||||||
|
<FormControl component="fieldset" className={classes.formControl}>
|
||||||
|
<RadioGroup
|
||||||
|
data-cy="ScheduleOptions"
|
||||||
|
aria-label="schedule"
|
||||||
|
name="schedule"
|
||||||
|
value={value}
|
||||||
|
onChange={handleChange}
|
||||||
|
>
|
||||||
|
{/* options to choose schedule */}
|
||||||
|
<FormControlLabel
|
||||||
|
value="now"
|
||||||
|
control={
|
||||||
|
<Radio
|
||||||
|
classes={{
|
||||||
|
root: classes.radio,
|
||||||
|
checked: classes.checked,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<Typography className={classes.radioText}>
|
||||||
|
{t('createWorkflow.scheduleWorkflow.radio.now')}
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{value === 'specificTime' && (
|
||||||
|
<div className={classes.schLater}>
|
||||||
|
<Typography className={classes.captionText}>
|
||||||
|
{t('createWorkflow.scheduleWorkflow.radio.future')}
|
||||||
|
</Typography>
|
||||||
|
<div className={classes.innerSpecific}>
|
||||||
|
<CustomDate
|
||||||
|
selectedDate={selectedDate}
|
||||||
|
handleDateChange={(event) => {
|
||||||
|
handleDateChange(event);
|
||||||
|
}}
|
||||||
|
disabled={false}
|
||||||
|
/>
|
||||||
|
<CustomTime
|
||||||
|
handleDateChange={(event) => {
|
||||||
|
handleTimeChange(event);
|
||||||
|
}}
|
||||||
|
value={selectedTime}
|
||||||
|
ampm
|
||||||
|
disabled={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<FormControlLabel
|
||||||
|
value="recurringSchedule"
|
||||||
|
control={
|
||||||
|
<Radio
|
||||||
|
classes={{
|
||||||
|
root: classes.radio,
|
||||||
|
checked: classes.checked,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<Typography className={classes.radioText}>
|
||||||
|
{t('createWorkflow.scheduleWorkflow.radio.recurr')}
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{value === 'recurringSchedule' && (
|
||||||
|
<div className={classes.schLater}>
|
||||||
|
<Typography className={classes.captionText}>
|
||||||
|
{t('createWorkflow.scheduleWorkflow.radio.rightRecurr')}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
{/* options to select time of recurring schedule */}
|
||||||
|
<div className={classes.innerRecurring}>
|
||||||
|
<FormControl component="fieldset">
|
||||||
|
<RadioGroup
|
||||||
|
aria-label="instanceDef"
|
||||||
|
name="instanceDef"
|
||||||
|
value={valueDef}
|
||||||
|
onChange={(event) => {
|
||||||
|
handleChangeInstance(event);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FormControlLabel
|
||||||
|
value={constants.recurringEveryHour}
|
||||||
|
control={
|
||||||
|
<Radio
|
||||||
|
classes={{
|
||||||
|
root: classes.radio,
|
||||||
|
checked: classes.checked,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={t(
|
||||||
|
'createWorkflow.scheduleWorkflow.every.hr'
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{valueDef === constants.recurringEveryHour && (
|
||||||
|
<div>
|
||||||
|
<div className={classes.scRandom}>
|
||||||
|
<Typography className={classes.scRandsub1}>
|
||||||
|
{t('createWorkflow.scheduleWorkflow.at')}
|
||||||
|
</Typography>
|
||||||
|
<SetTime
|
||||||
|
data={mins}
|
||||||
|
handleChange={(event) => {
|
||||||
|
setMinute(event.target.value as number);
|
||||||
|
setCronValue({
|
||||||
|
...cronValue,
|
||||||
|
minute: (event.target
|
||||||
|
.value as number).toString(),
|
||||||
|
hour: '0-23',
|
||||||
|
});
|
||||||
|
workflowAction.setWorkflowDetails({
|
||||||
|
scheduleInput: {
|
||||||
|
...workflowData.scheduleInput,
|
||||||
|
hour_interval: event.target
|
||||||
|
.value as number,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
value={minute}
|
||||||
|
/>
|
||||||
|
{minute === 0 || minute === 1 ? (
|
||||||
|
<Typography>
|
||||||
|
{t('createWorkflow.scheduleWorkflow.min')}
|
||||||
|
</Typography>
|
||||||
|
) : (
|
||||||
|
<Typography>
|
||||||
|
{t('createWorkflow.scheduleWorkflow.mins')}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<FormControlLabel
|
||||||
|
value={constants.recurringEveryDay}
|
||||||
|
control={
|
||||||
|
<Radio
|
||||||
|
classes={{
|
||||||
|
root: classes.radio,
|
||||||
|
checked: classes.checked,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={t(
|
||||||
|
'createWorkflow.scheduleWorkflow.every.day'
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{valueDef === constants.recurringEveryDay && (
|
||||||
|
<div>
|
||||||
|
<div className={classes.scRandom}>
|
||||||
|
<Typography className={classes.scRandsub1}>
|
||||||
|
{t('createWorkflow.scheduleWorkflow.at')}
|
||||||
|
</Typography>
|
||||||
|
<CustomTime
|
||||||
|
handleDateChange={(date: Date | null) => {
|
||||||
|
setSelectedTime(date);
|
||||||
|
setCronValue({
|
||||||
|
...cronValue,
|
||||||
|
minute: date?.getMinutes().toString(),
|
||||||
|
hour: date?.getHours().toString(),
|
||||||
|
day_week: '0-6',
|
||||||
|
});
|
||||||
|
workflowAction.setWorkflowDetails({
|
||||||
|
scheduleInput: {
|
||||||
|
...workflowData.scheduleInput,
|
||||||
|
time: date,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
value={selectedTime}
|
||||||
|
ampm
|
||||||
|
disabled={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<FormControlLabel
|
||||||
|
value={constants.recurringEveryWeek}
|
||||||
|
control={
|
||||||
|
<Radio
|
||||||
|
classes={{
|
||||||
|
root: classes.radio,
|
||||||
|
checked: classes.checked,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={t(
|
||||||
|
'createWorkflow.scheduleWorkflow.every.week'
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{valueDef === constants.recurringEveryWeek && (
|
||||||
|
<div>
|
||||||
|
<div className={classes.scRandom}>
|
||||||
|
<Typography className={classes.scRandsub1}>
|
||||||
|
{t('createWorkflow.scheduleWorkflow.on')}
|
||||||
|
</Typography>
|
||||||
|
<FormControl className={classes.formControlDT}>
|
||||||
|
<Select
|
||||||
|
className={classes.select}
|
||||||
|
disableUnderline
|
||||||
|
value={days}
|
||||||
|
onChange={(e) => {
|
||||||
|
setCronValue({
|
||||||
|
...cronValue,
|
||||||
|
month: '*',
|
||||||
|
day_week: ((e.target
|
||||||
|
.value as unknown) as string).slice(
|
||||||
|
0,
|
||||||
|
3
|
||||||
|
),
|
||||||
|
});
|
||||||
|
setDays(
|
||||||
|
(e.target.value as unknown) as string
|
||||||
|
);
|
||||||
|
workflowAction.setWorkflowDetails({
|
||||||
|
scheduleInput: {
|
||||||
|
...workflowData.scheduleInput,
|
||||||
|
weekday: (e.target
|
||||||
|
.value as unknown) as string,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
label="days"
|
||||||
|
inputProps={{
|
||||||
|
name: 'days',
|
||||||
|
style: {
|
||||||
|
fontSize: '0.75rem',
|
||||||
|
height: '0.43rem',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{weekdays.map((day) => (
|
||||||
|
<option
|
||||||
|
key={day}
|
||||||
|
className={classes.opt}
|
||||||
|
value={day}
|
||||||
|
>
|
||||||
|
{day}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<Typography className={classes.scRandsub1}>
|
||||||
|
{t('createWorkflow.scheduleWorkflow.at')}
|
||||||
|
</Typography>
|
||||||
|
<CustomTime
|
||||||
|
handleDateChange={(date: Date | null) => {
|
||||||
|
reccuringDateChange(date);
|
||||||
|
}}
|
||||||
|
value={selectedTime}
|
||||||
|
ampm
|
||||||
|
disabled={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<FormControlLabel
|
||||||
|
value={constants.recurringEveryMonth}
|
||||||
|
control={
|
||||||
|
<Radio
|
||||||
|
classes={{
|
||||||
|
root: classes.radio,
|
||||||
|
checked: classes.checked,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={t(
|
||||||
|
'createWorkflow.scheduleWorkflow.every.month'
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{valueDef === constants.recurringEveryMonth && (
|
||||||
|
<div>
|
||||||
|
<div className={classes.scRandom}>
|
||||||
|
<Typography className={classes.scRandsub1}>
|
||||||
|
{t('createWorkflow.scheduleWorkflow.on')}
|
||||||
|
</Typography>
|
||||||
|
<SetTime
|
||||||
|
data={names}
|
||||||
|
handleChange={(event) => {
|
||||||
|
setCronValue({
|
||||||
|
...cronValue,
|
||||||
|
day_month: (event.target
|
||||||
|
.value as number).toString(),
|
||||||
|
});
|
||||||
|
setDates(event.target.value as number);
|
||||||
|
workflowAction.setWorkflowDetails({
|
||||||
|
scheduleInput: {
|
||||||
|
...workflowData.scheduleInput,
|
||||||
|
day: event.target.value as number,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
value={dates}
|
||||||
|
/>
|
||||||
|
<Typography className={classes.scRandsub1}>
|
||||||
|
{t('createWorkflow.scheduleWorkflow.at')}
|
||||||
|
</Typography>
|
||||||
|
<CustomTime
|
||||||
|
handleDateChange={(date: Date | null) => {
|
||||||
|
reccuringDateChange(date);
|
||||||
|
}}
|
||||||
|
value={selectedTime}
|
||||||
|
ampm
|
||||||
|
disabled={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</RadioGroup>
|
||||||
|
</FormControl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* Cancel and Save Button */}
|
||||||
|
<div className={classes.buttonDiv} aria-label="buttons">
|
||||||
|
<ButtonOutlined
|
||||||
|
onClick={() => {
|
||||||
|
history.push({
|
||||||
|
pathname: `/workflows/`,
|
||||||
|
search: `?projectID=${getProjectID()}&projectRole=${getProjectRole()}`,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</ButtonOutlined>
|
||||||
|
<ButtonFilled
|
||||||
|
onClick={() =>
|
||||||
|
history.push({
|
||||||
|
pathname: `/workflows/schedule/${getProjectID()}/${fetchWorkflowNameFromManifest(
|
||||||
|
manifest
|
||||||
|
)}`,
|
||||||
|
search: `?projectID=${getProjectID()}&projectRole=${getProjectRole()}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t('editSchedule.verify')}
|
||||||
|
</ButtonFilled>
|
||||||
|
</div>
|
||||||
|
</Scaffold>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ScheduleWorkflow;
|
||||||
|
|
@ -0,0 +1,504 @@
|
||||||
|
import { useMutation, useQuery } from '@apollo/client';
|
||||||
|
import Typography from '@material-ui/core/Typography';
|
||||||
|
import EditIcon from '@material-ui/icons/Edit';
|
||||||
|
import cronstrue from 'cronstrue';
|
||||||
|
import { ButtonFilled, ButtonOutlined, Modal } from 'litmus-ui';
|
||||||
|
import localforage from 'localforage';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
import YAML from 'yaml';
|
||||||
|
import AdjustedWeights from '../../components/AdjustedWeights';
|
||||||
|
import BackButton from '../../components/Button/BackButton';
|
||||||
|
import Loader from '../../components/Loader';
|
||||||
|
import YamlEditor from '../../components/YamlEditor/Editor';
|
||||||
|
import { parseYamlValidations } from '../../components/YamlEditor/Validations';
|
||||||
|
import Scaffold from '../../containers/layouts/Scaffold';
|
||||||
|
import { UPDATE_SCHEDULE } from '../../graphql/mutations';
|
||||||
|
import { SCHEDULE_DETAILS } from '../../graphql/queries';
|
||||||
|
import {
|
||||||
|
CreateWorkFlowInput,
|
||||||
|
UpdateWorkflowResponse,
|
||||||
|
WeightMap,
|
||||||
|
} from '../../models/graphql/createWorkflowData';
|
||||||
|
import { ScheduleDataVars, Schedules } from '../../models/graphql/scheduleData';
|
||||||
|
import { experimentMap, WorkflowData } from '../../models/redux/workflow';
|
||||||
|
import useActions from '../../redux/actions';
|
||||||
|
import * as TabActions from '../../redux/actions/tabs';
|
||||||
|
import * as TemplateSelectionActions from '../../redux/actions/template';
|
||||||
|
import * as WorkflowActions from '../../redux/actions/workflow';
|
||||||
|
import { history } from '../../redux/configureStore';
|
||||||
|
import { RootState } from '../../redux/reducers';
|
||||||
|
import { getProjectID, getProjectRole } from '../../utils/getSearchParams';
|
||||||
|
import { fetchWorkflowNameFromManifest } from '../../utils/yamlUtils';
|
||||||
|
import useStyles from './styles';
|
||||||
|
|
||||||
|
interface URLParams {
|
||||||
|
workflowName: string;
|
||||||
|
scheduleProjectID: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Weights {
|
||||||
|
experimentName: string;
|
||||||
|
weight: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WorkflowProps {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditSchedule: React.FC = () => {
|
||||||
|
const classes = useStyles();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const [workflow, setWorkflow] = useState<WorkflowProps>({
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
});
|
||||||
|
const [weights, setWeights] = useState<experimentMap[]>([
|
||||||
|
{
|
||||||
|
experimentName: '',
|
||||||
|
weight: 0,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const [open, setOpen] = React.useState(false);
|
||||||
|
const [finishModalOpen, setFinishModalOpen] = useState(false);
|
||||||
|
const [errorModal, setErrorModal] = useState(false);
|
||||||
|
|
||||||
|
const template = useActions(TemplateSelectionActions);
|
||||||
|
const workflowData: WorkflowData = useSelector(
|
||||||
|
(state: RootState) => state.workflowData
|
||||||
|
);
|
||||||
|
const workflowAction = useActions(WorkflowActions);
|
||||||
|
// Get Parameters from URL
|
||||||
|
const paramData: URLParams = useParams();
|
||||||
|
const projectID = getProjectID();
|
||||||
|
const userRole = getProjectRole();
|
||||||
|
|
||||||
|
// Apollo query to get the scheduled data
|
||||||
|
const { data, loading } = useQuery<Schedules, ScheduleDataVars>(
|
||||||
|
SCHEDULE_DETAILS,
|
||||||
|
{
|
||||||
|
variables: { projectID: paramData.scheduleProjectID },
|
||||||
|
fetchPolicy: 'cache-and-network',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const manifest = useSelector(
|
||||||
|
(state: RootState) => state.workflowManifest.manifest
|
||||||
|
);
|
||||||
|
|
||||||
|
const wfDetails =
|
||||||
|
data &&
|
||||||
|
data.getScheduledWorkflows.filter(
|
||||||
|
(wf) => wf.workflow_name === paramData.workflowName
|
||||||
|
)[0];
|
||||||
|
const doc = new YAML.Document();
|
||||||
|
const w: Weights[] = [];
|
||||||
|
const { cronSyntax, clusterid, clustername } = workflowData;
|
||||||
|
|
||||||
|
const [createChaosWorkFlow, { error: workflowError }] = useMutation<
|
||||||
|
UpdateWorkflowResponse,
|
||||||
|
CreateWorkFlowInput
|
||||||
|
>(UPDATE_SCHEDULE, {
|
||||||
|
onCompleted: () => {
|
||||||
|
setFinishModalOpen(true);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleMutation = () => {
|
||||||
|
if (
|
||||||
|
workflow.name.length !== 0 &&
|
||||||
|
workflow.description.length !== 0 &&
|
||||||
|
weights.length !== 0
|
||||||
|
) {
|
||||||
|
const weightData: WeightMap[] = [];
|
||||||
|
|
||||||
|
weights.forEach((data) => {
|
||||||
|
weightData.push({
|
||||||
|
experiment_name: data.experimentName,
|
||||||
|
weightage: data.weight,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/* JSON.stringify takes 3 parameters [object to be converted,
|
||||||
|
a function to alter the conversion, spaces to be shown in final result for indentation ] */
|
||||||
|
const yml = YAML.parse(manifest);
|
||||||
|
const yamlJson = JSON.stringify(yml, null, 2); // Converted to Stringified JSON
|
||||||
|
|
||||||
|
const chaosWorkFlowInputs = {
|
||||||
|
workflow_id: wfDetails?.workflow_id,
|
||||||
|
workflow_manifest: yamlJson,
|
||||||
|
cronSyntax,
|
||||||
|
workflow_name: fetchWorkflowNameFromManifest(manifest),
|
||||||
|
workflow_description: workflow.description,
|
||||||
|
isCustomWorkflow: false,
|
||||||
|
weightages: weightData,
|
||||||
|
project_id: projectID,
|
||||||
|
cluster_id: clusterid,
|
||||||
|
};
|
||||||
|
|
||||||
|
createChaosWorkFlow({
|
||||||
|
variables: { ChaosWorkFlowInput: chaosWorkFlowInputs },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
localforage.getItem('editSchedule').then((isCronEdited) => {
|
||||||
|
if (wfDetails !== undefined) {
|
||||||
|
for (let i = 0; i < wfDetails?.weightages.length; i++) {
|
||||||
|
w.push({
|
||||||
|
experimentName: wfDetails?.weightages[i].experiment_name,
|
||||||
|
weight: wfDetails?.weightages[i].weightage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
doc.contents = JSON.parse(wfDetails?.workflow_manifest);
|
||||||
|
workflowAction.setWorkflowManifest({
|
||||||
|
manifest: isCronEdited === null ? doc.toString() : manifest,
|
||||||
|
});
|
||||||
|
setWorkflow({
|
||||||
|
name: wfDetails?.workflow_name,
|
||||||
|
description: wfDetails?.workflow_description,
|
||||||
|
});
|
||||||
|
localforage.setItem('weights', w);
|
||||||
|
workflowAction.setWorkflowDetails({
|
||||||
|
workflow_id: wfDetails?.workflow_id,
|
||||||
|
clusterid: wfDetails?.cluster_id,
|
||||||
|
cronSyntax:
|
||||||
|
isCronEdited === null ? wfDetails?.cronSyntax : cronSyntax,
|
||||||
|
scheduleType: {
|
||||||
|
scheduleOnce:
|
||||||
|
wfDetails?.cronSyntax === '' ? 'now' : 'recurringSchedule',
|
||||||
|
recurringSchedule: '',
|
||||||
|
},
|
||||||
|
scheduleInput: {
|
||||||
|
hour_interval: 0,
|
||||||
|
day: 1,
|
||||||
|
weekday: 'Monday',
|
||||||
|
time: new Date(),
|
||||||
|
date: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
template.selectTemplate({ selectTemplateID: 0, isDisable: false });
|
||||||
|
setWeights(w);
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
const tabs = useActions(TabActions);
|
||||||
|
|
||||||
|
const [yamlStatus, setYamlStatus] = React.useState(
|
||||||
|
`${t('createWorkflow.verifyCommit.codeIsFine')}`
|
||||||
|
);
|
||||||
|
const [modified, setModified] = React.useState(false);
|
||||||
|
|
||||||
|
const handleEditOpen = () => {
|
||||||
|
setModified(false);
|
||||||
|
setOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEditClose = () => {
|
||||||
|
setModified(true);
|
||||||
|
setOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleNext = () => {
|
||||||
|
handleMutation();
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const editorValidations = parseYamlValidations(manifest, classes);
|
||||||
|
const stateObject = {
|
||||||
|
markers: editorValidations.markers,
|
||||||
|
annotations: editorValidations.annotations,
|
||||||
|
};
|
||||||
|
if (stateObject.annotations.length > 0) {
|
||||||
|
setYamlStatus(`${t('createWorkflow.verifyCommit.errYaml')}`);
|
||||||
|
} else {
|
||||||
|
setYamlStatus(`${t('createWorkflow.verifyCommit.codeIsFine')}`);
|
||||||
|
}
|
||||||
|
}, [modified]);
|
||||||
|
|
||||||
|
const handleFinishModal = () => {
|
||||||
|
history.push({
|
||||||
|
pathname: `/workflows`,
|
||||||
|
search: `?projectID=${projectID}&projectRole=${userRole}`,
|
||||||
|
});
|
||||||
|
setFinishModalOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleErrorModalClose = () => {
|
||||||
|
setErrorModal(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Scaffold>
|
||||||
|
{loading ? (
|
||||||
|
<Loader />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<BackButton />
|
||||||
|
<Typography className={classes.title}>
|
||||||
|
{t('editSchedule.title')}
|
||||||
|
</Typography>
|
||||||
|
<div className={classes.root}>
|
||||||
|
<div className={classes.innerContainer}>
|
||||||
|
<Typography className={classes.sumText}>
|
||||||
|
{t('createWorkflow.verifyCommit.summary.header')}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<div className={classes.outerSum}>
|
||||||
|
<div className={classes.summaryDiv}>
|
||||||
|
<div className={classes.innerSumDiv}>
|
||||||
|
<Typography className={classes.col1}>
|
||||||
|
{t('createWorkflow.verifyCommit.summary.workflowName')}:
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<div className={classes.col2} data-cy="WorkflowName">
|
||||||
|
<Typography>
|
||||||
|
{fetchWorkflowNameFromManifest(manifest)}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={classes.summaryDiv}>
|
||||||
|
<div className={classes.innerSumDiv}>
|
||||||
|
<Typography className={classes.col1}>
|
||||||
|
{t('createWorkflow.verifyCommit.summary.clustername')}:
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<Typography className={classes.schCol2}>
|
||||||
|
{clustername}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={classes.summaryDiv}>
|
||||||
|
<div className={classes.innerSumDiv}>
|
||||||
|
<Typography className={classes.col1}>
|
||||||
|
{t('createWorkflow.verifyCommit.summary.desc')}:
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<div className={classes.col2}>
|
||||||
|
<Typography>{workflow.description}</Typography>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={classes.summaryDiv}>
|
||||||
|
<div className={classes.innerSumDiv}>
|
||||||
|
<Typography className={classes.col1}>
|
||||||
|
{t('createWorkflow.verifyCommit.summary.schedule')}:
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<div className={classes.schCol2}>
|
||||||
|
{cronSyntax === '' ? (
|
||||||
|
<Typography>
|
||||||
|
{t('createWorkflow.verifyCommit.summary.schedulingNow')}
|
||||||
|
</Typography>
|
||||||
|
) : (
|
||||||
|
<Typography>{cronstrue.toString(cronSyntax)}</Typography>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<ButtonOutlined
|
||||||
|
className={classes.editButton}
|
||||||
|
onClick={() =>
|
||||||
|
history.push({
|
||||||
|
pathname: `/workflows/schedule/${projectID}/${fetchWorkflowNameFromManifest(
|
||||||
|
manifest
|
||||||
|
)}/set`,
|
||||||
|
search: `?projectID=${projectID}&projectRole=${userRole}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<EditIcon className={classes.editIcon} data-cy="edit" />
|
||||||
|
{t('editSchedule.edit')}
|
||||||
|
</ButtonOutlined>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={classes.summaryDiv}>
|
||||||
|
<div className={classes.innerSumDiv}>
|
||||||
|
<Typography className={classes.col1}>
|
||||||
|
{t('createWorkflow.verifyCommit.summary.adjustedWeights')}
|
||||||
|
:
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
{weights.length === 0 ? (
|
||||||
|
<div>
|
||||||
|
<Typography className={classes.col2}>
|
||||||
|
{t('createWorkflow.verifyCommit.error')}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className={classes.adjWeights}>
|
||||||
|
<div className={classes.progress}>
|
||||||
|
{weights.map((Test) => (
|
||||||
|
<AdjustedWeights
|
||||||
|
key={Test.weight}
|
||||||
|
testName={`${Test.experimentName} ${t(
|
||||||
|
'createWorkflow.verifyCommit.test'
|
||||||
|
)}`}
|
||||||
|
testValue={Test.weight}
|
||||||
|
spacing={false}
|
||||||
|
icon={false}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className={classes.summaryDiv}>
|
||||||
|
<div className={classes.innerSumDiv}>
|
||||||
|
<Typography className={classes.col1}>
|
||||||
|
{t('createWorkflow.verifyCommit.YAML')}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<div className={classes.yamlFlex}>
|
||||||
|
{weights.length === 0 ? (
|
||||||
|
<Typography className={classes.spacingHorizontal}>
|
||||||
|
{t('createWorkflow.verifyCommit.errYaml')}
|
||||||
|
</Typography>
|
||||||
|
) : (
|
||||||
|
<Typography>
|
||||||
|
<b>{yamlStatus}</b>
|
||||||
|
<span className={classes.spacingHorizontal}>
|
||||||
|
{t('createWorkflow.verifyCommit.youCanMoveOn')}
|
||||||
|
</span>
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
<br />
|
||||||
|
<ButtonFilled
|
||||||
|
className={classes.verifyYAMLButton}
|
||||||
|
onClick={handleEditOpen}
|
||||||
|
>
|
||||||
|
{t('createWorkflow.verifyCommit.button.viewYaml')}
|
||||||
|
</ButtonFilled>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* Cancel and Save Button */}
|
||||||
|
<div className={classes.buttonDiv} aria-label="buttons">
|
||||||
|
<ButtonOutlined
|
||||||
|
onClick={() => {
|
||||||
|
history.push({
|
||||||
|
pathname: `/workflows/`,
|
||||||
|
search: `?projectID=${projectID}&projectRole=${userRole}`,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('editSchedule.cancel')}
|
||||||
|
</ButtonOutlined>
|
||||||
|
<ButtonFilled onClick={() => handleNext()}>
|
||||||
|
{t('editSchedule.save')}
|
||||||
|
</ButtonFilled>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
open={open}
|
||||||
|
onClose={handleEditClose}
|
||||||
|
width="60%"
|
||||||
|
modalActions={
|
||||||
|
<ButtonOutlined
|
||||||
|
onClick={handleEditClose}
|
||||||
|
className={classes.closeBtn}
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</ButtonOutlined>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<YamlEditor content={manifest} filename={workflow.name} readOnly />
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
{/* Finish Modal */}
|
||||||
|
<div>
|
||||||
|
<Modal
|
||||||
|
data-cy="FinishModal"
|
||||||
|
open={finishModalOpen}
|
||||||
|
onClose={handleFinishModal}
|
||||||
|
width="60%"
|
||||||
|
aria-labelledby="simple-modal-title"
|
||||||
|
aria-describedby="simple-modal-description"
|
||||||
|
modalActions={
|
||||||
|
<div data-cy="GoToWorkflowButton">
|
||||||
|
<ButtonOutlined onClick={handleFinishModal}>
|
||||||
|
✕
|
||||||
|
</ButtonOutlined>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className={classes.modal}>
|
||||||
|
<img src="/icons/finish.svg" alt="mark" />
|
||||||
|
<div className={classes.heading}>
|
||||||
|
{t('editSchedule.theSchedule')}
|
||||||
|
<br />
|
||||||
|
<span className={classes.successful}>{workflow.name}</span>,
|
||||||
|
<br />
|
||||||
|
<span className={classes.bold}>
|
||||||
|
{t('editSchedule.successfullyCreated')}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className={classes.headWorkflow}>
|
||||||
|
{t('workflowStepper.congratulationsSub1')} <br />{' '}
|
||||||
|
{t('workflowStepper.congratulationsSub2')}
|
||||||
|
</div>
|
||||||
|
<div className={classes.button}>
|
||||||
|
<ButtonFilled
|
||||||
|
data-cy="selectFinish"
|
||||||
|
onClick={() => {
|
||||||
|
handleFinishModal();
|
||||||
|
tabs.changeWorkflowsTabs(0);
|
||||||
|
history.push({
|
||||||
|
pathname: '/workflows',
|
||||||
|
search: `?projectID=${projectID}&projectRole=${userRole}`,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>{t('workflowStepper.workflowBtn')}</div>
|
||||||
|
</ButtonFilled>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
<Modal
|
||||||
|
open={errorModal}
|
||||||
|
onClose={handleErrorModalClose}
|
||||||
|
width="60%"
|
||||||
|
modalActions={
|
||||||
|
<ButtonOutlined onClick={handleErrorModalClose}>
|
||||||
|
✕
|
||||||
|
</ButtonOutlined>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className={classes.modal}>
|
||||||
|
<img src="/icons/red-cross.svg" alt="mark" />
|
||||||
|
<div className={classes.heading}>
|
||||||
|
<strong>{t('workflowStepper.workflowFailed')}</strong>
|
||||||
|
</div>
|
||||||
|
<div className={classes.headWorkflow}>
|
||||||
|
<Typography>
|
||||||
|
{t('workflowStepper.error')} : {workflowError?.message}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<div className={classes.button}>
|
||||||
|
<ButtonFilled
|
||||||
|
data-cy="selectFinish"
|
||||||
|
onClick={() => {
|
||||||
|
setErrorModal(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>{t('workflowStepper.back')}</div>
|
||||||
|
</ButtonFilled>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Scaffold>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EditSchedule;
|
||||||
|
|
@ -0,0 +1,217 @@
|
||||||
|
import { makeStyles, Theme } from '@material-ui/core/styles';
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => ({
|
||||||
|
horizontalLine: {
|
||||||
|
background: theme.palette.border.main,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
padding: theme.spacing(0, 2),
|
||||||
|
fontWeight: 700,
|
||||||
|
fontSize: '2rem',
|
||||||
|
[theme.breakpoints.up('lg')]: {
|
||||||
|
fontSize: '2.3rem',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
root: {
|
||||||
|
background: theme.palette.background.paper,
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
padding: theme.spacing(1, 2),
|
||||||
|
margin: theme.spacing(2, 'auto'),
|
||||||
|
width: '100%',
|
||||||
|
flexDirection: 'column',
|
||||||
|
},
|
||||||
|
|
||||||
|
// Inner Container
|
||||||
|
innerContainer: {
|
||||||
|
margin: theme.spacing(4, 'auto'),
|
||||||
|
width: '95%', // Inner width of the container
|
||||||
|
},
|
||||||
|
|
||||||
|
suHeader: {
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
},
|
||||||
|
headerText: {
|
||||||
|
fontSize: '1.2rem',
|
||||||
|
[theme.breakpoints.up('lg')]: {
|
||||||
|
fontSize: '1.4rem',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
margin: theme.spacing(3, 0),
|
||||||
|
fontSize: '1rem',
|
||||||
|
},
|
||||||
|
bfinIcon: {
|
||||||
|
width: '7rem',
|
||||||
|
height: '6.31rem',
|
||||||
|
},
|
||||||
|
|
||||||
|
// Body
|
||||||
|
outerSum: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
},
|
||||||
|
summaryDiv: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'flex-start',
|
||||||
|
alignItems: 'baseline',
|
||||||
|
margin: theme.spacing(1, 0),
|
||||||
|
},
|
||||||
|
innerSumDiv: {
|
||||||
|
alignContent: 'center',
|
||||||
|
display: 'table-cell',
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
width: '20%',
|
||||||
|
[theme.breakpoints.up('lg')]: {
|
||||||
|
width: '10%',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sumText: {
|
||||||
|
width: '100%',
|
||||||
|
margin: theme.spacing(2, 0),
|
||||||
|
fontWeight: 700,
|
||||||
|
fontSize: '1.2rem',
|
||||||
|
[theme.breakpoints.up('lg')]: {
|
||||||
|
fontSize: '1.4rem',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
col1: {
|
||||||
|
alignContent: 'center',
|
||||||
|
color: theme.palette.highlight,
|
||||||
|
fontSize: '1rem',
|
||||||
|
paddingTop: theme.spacing(0.5),
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
},
|
||||||
|
schedule: {
|
||||||
|
fontSize: '0.85rem',
|
||||||
|
padding: theme.spacing(0.75, 0, 2, 0),
|
||||||
|
},
|
||||||
|
col2: {
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
marginLeft: theme.spacing(5),
|
||||||
|
fontWeight: 700,
|
||||||
|
width: '75%',
|
||||||
|
},
|
||||||
|
schCol2: {
|
||||||
|
width: '75%',
|
||||||
|
marginLeft: theme.spacing(5),
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
},
|
||||||
|
clusterName: {
|
||||||
|
fontSize: '0.85rem',
|
||||||
|
marginLeft: theme.spacing(7),
|
||||||
|
paddingTop: theme.spacing(0.5),
|
||||||
|
},
|
||||||
|
editButton: {
|
||||||
|
height: '1rem',
|
||||||
|
},
|
||||||
|
editIcon: {
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
height: '0.8rem',
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
fontSize: '0.875rem',
|
||||||
|
color: theme.palette.secondary.dark,
|
||||||
|
},
|
||||||
|
adjWeights: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
|
||||||
|
width: '80%',
|
||||||
|
[theme.breakpoints.up('lg')]: {
|
||||||
|
width: '90%',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
height: '3rem',
|
||||||
|
fontSize: '0.9375rem',
|
||||||
|
color: theme.palette.text.disabled,
|
||||||
|
width: '30rem',
|
||||||
|
margin: theme.spacing(3.75, 0, 30, 0),
|
||||||
|
},
|
||||||
|
typoCol2: {
|
||||||
|
fontSize: '1rem',
|
||||||
|
},
|
||||||
|
textEdit: {
|
||||||
|
marginTop: theme.spacing(7.5),
|
||||||
|
},
|
||||||
|
buttonOutlineText: {
|
||||||
|
padding: theme.spacing(1.5),
|
||||||
|
},
|
||||||
|
errorText: {
|
||||||
|
color: theme.palette.error.main,
|
||||||
|
fontSize: '1rem',
|
||||||
|
marginLeft: theme.spacing(5),
|
||||||
|
},
|
||||||
|
yamlFlex: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
width: '75%',
|
||||||
|
marginLeft: theme.spacing(5),
|
||||||
|
},
|
||||||
|
progress: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
flexGrow: 1,
|
||||||
|
marginLeft: theme.spacing(5),
|
||||||
|
},
|
||||||
|
spacingHorizontal: {
|
||||||
|
margin: theme.spacing(0, 1),
|
||||||
|
},
|
||||||
|
buttomPad: {
|
||||||
|
paddingBottom: theme.spacing(3.75),
|
||||||
|
},
|
||||||
|
closeBtn: {
|
||||||
|
color: theme.palette.secondary.contrastText,
|
||||||
|
},
|
||||||
|
buttonDiv: {
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
verifyYAMLButton: {
|
||||||
|
width: '20%',
|
||||||
|
},
|
||||||
|
bold: {
|
||||||
|
fontWeight: 700,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Modal
|
||||||
|
modal: {
|
||||||
|
[theme.breakpoints.up('lg')]: {
|
||||||
|
padding: theme.spacing(10),
|
||||||
|
},
|
||||||
|
padding: theme.spacing(3),
|
||||||
|
},
|
||||||
|
heading: {
|
||||||
|
fontSize: '2rem',
|
||||||
|
textalign: 'center',
|
||||||
|
marginTop: theme.spacing(3),
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
},
|
||||||
|
headWorkflow: {
|
||||||
|
fontsize: '2rem',
|
||||||
|
textalign: 'center',
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
marginTop: theme.spacing(3),
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
textAlign: 'center',
|
||||||
|
marginTop: theme.spacing(5),
|
||||||
|
},
|
||||||
|
closeButton: {
|
||||||
|
borderColor: theme.palette.border.main,
|
||||||
|
},
|
||||||
|
successful: {
|
||||||
|
fontSize: '2.2rem',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
margin: theme.spacing(2, 0),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
export default useStyles;
|
||||||
|
|
@ -8,9 +8,6 @@ import {
|
||||||
import createReducer from './createReducer';
|
import createReducer from './createReducer';
|
||||||
|
|
||||||
const initialState: WorkflowData = {
|
const initialState: WorkflowData = {
|
||||||
id: '',
|
|
||||||
isRecurring: false,
|
|
||||||
isDisabled: false,
|
|
||||||
chaosEngineChanged: false,
|
chaosEngineChanged: false,
|
||||||
namespace: 'litmus',
|
namespace: 'litmus',
|
||||||
clusterid: '',
|
clusterid: '',
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,10 @@ const parsed = (yaml: string) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const fetchWorkflowNameFromManifest = (manifest: string) => {
|
||||||
|
return YAML.parse(manifest).metadata.name;
|
||||||
|
};
|
||||||
|
|
||||||
export const getWorkflowParameter = (parameterString: string) => {
|
export const getWorkflowParameter = (parameterString: string) => {
|
||||||
return parameterString
|
return parameterString
|
||||||
.substring(1, parameterString.length - 1)
|
.substring(1, parameterString.length - 1)
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import { RERUN_CHAOS_WORKFLOW } from '../../../graphql/mutations';
|
||||||
import { ScheduleWorkflow } from '../../../models/graphql/scheduleData';
|
import { ScheduleWorkflow } from '../../../models/graphql/scheduleData';
|
||||||
import useActions from '../../../redux/actions';
|
import useActions from '../../../redux/actions';
|
||||||
import * as TabActions from '../../../redux/actions/tabs';
|
import * as TabActions from '../../../redux/actions/tabs';
|
||||||
import * as WorkflowActions from '../../../redux/actions/workflow';
|
|
||||||
import { history } from '../../../redux/configureStore';
|
import { history } from '../../../redux/configureStore';
|
||||||
import { ReactComponent as CrossMarkIcon } from '../../../svg/crossmark.svg';
|
import { ReactComponent as CrossMarkIcon } from '../../../svg/crossmark.svg';
|
||||||
import timeDifferenceForDate from '../../../utils/datesModifier';
|
import timeDifferenceForDate from '../../../utils/datesModifier';
|
||||||
|
|
@ -63,8 +62,6 @@ const TableData: React.FC<TableDataProps> = ({
|
||||||
setPopAnchorEl(null);
|
setPopAnchorEl(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const workflow = useActions(WorkflowActions);
|
|
||||||
|
|
||||||
const handlePopOverClick = (event: React.MouseEvent<HTMLElement>) => {
|
const handlePopOverClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
setPopAnchorEl(event.currentTarget);
|
setPopAnchorEl(event.currentTarget);
|
||||||
};
|
};
|
||||||
|
|
@ -125,13 +122,6 @@ const TableData: React.FC<TableDataProps> = ({
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// If regularity is not Once then set recurring schedule state to true
|
|
||||||
if (data.cronSyntax !== '') {
|
|
||||||
workflow.setWorkflowDetails({
|
|
||||||
isRecurring: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const [reRunChaosWorkFlow] = useMutation(RERUN_CHAOS_WORKFLOW, {
|
const [reRunChaosWorkFlow] = useMutation(RERUN_CHAOS_WORKFLOW, {
|
||||||
onCompleted: () => {
|
onCompleted: () => {
|
||||||
tabs.changeWorkflowsTabs(0);
|
tabs.changeWorkflowsTabs(0);
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,14 @@ import { useSelector } from 'react-redux';
|
||||||
import YAML from 'yaml';
|
import YAML from 'yaml';
|
||||||
import CustomDate from '../../../components/DateTime/CustomDate/index';
|
import CustomDate from '../../../components/DateTime/CustomDate/index';
|
||||||
import CustomTime from '../../../components/DateTime/CustomTime/index';
|
import CustomTime from '../../../components/DateTime/CustomTime/index';
|
||||||
|
import { constants } from '../../../constants';
|
||||||
import { WorkflowData } from '../../../models/redux/workflow';
|
import { WorkflowData } from '../../../models/redux/workflow';
|
||||||
import useActions from '../../../redux/actions';
|
import useActions from '../../../redux/actions';
|
||||||
import * as TemplateSelectionActions from '../../../redux/actions/template';
|
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 { cronWorkflow, workflowOnce } from '../../../utils/workflowTemplate';
|
||||||
|
import { fetchWorkflowNameFromManifest } from '../../../utils/yamlUtils';
|
||||||
import SetTime from './SetTime/index';
|
import SetTime from './SetTime/index';
|
||||||
import useStyles from './styles';
|
import useStyles from './styles';
|
||||||
|
|
||||||
|
|
@ -54,6 +57,10 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
);
|
);
|
||||||
const workflow = useActions(WorkflowActions);
|
const workflow = useActions(WorkflowActions);
|
||||||
const template = useActions(TemplateSelectionActions);
|
const template = useActions(TemplateSelectionActions);
|
||||||
|
|
||||||
|
const scheduleOnce = workflowOnce;
|
||||||
|
const scheduleMore = cronWorkflow;
|
||||||
|
|
||||||
// Controls Radio Buttons
|
// Controls Radio Buttons
|
||||||
const [value, setValue] = React.useState(
|
const [value, setValue] = React.useState(
|
||||||
workflowData.scheduleType.scheduleOnce
|
workflowData.scheduleType.scheduleOnce
|
||||||
|
|
@ -81,10 +88,6 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
workflow.setWorkflowDetails({
|
workflow.setWorkflowDetails({
|
||||||
cronSyntax,
|
cronSyntax,
|
||||||
});
|
});
|
||||||
if (value === 'disable')
|
|
||||||
workflow.setWorkflowDetails({
|
|
||||||
isDisabled: true,
|
|
||||||
});
|
|
||||||
}, [cronValue]);
|
}, [cronValue]);
|
||||||
|
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
|
@ -163,6 +166,76 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
validateTime(selectedTime, date);
|
validateTime(selectedTime, date);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function EditYaml() {
|
||||||
|
const oldParsedYaml = YAML.parse(manifest);
|
||||||
|
let NewYaml: string;
|
||||||
|
if (
|
||||||
|
oldParsedYaml.kind === 'Workflow' &&
|
||||||
|
workflowData.scheduleType.scheduleOnce !== 'now'
|
||||||
|
) {
|
||||||
|
const oldParsedYaml = YAML.parse(manifest);
|
||||||
|
const newParsedYaml = YAML.parse(scheduleMore);
|
||||||
|
delete newParsedYaml.spec.workflowSpec;
|
||||||
|
newParsedYaml.spec.schedule = workflowData.cronSyntax;
|
||||||
|
delete newParsedYaml.metadata.generateName;
|
||||||
|
newParsedYaml.metadata.name = fetchWorkflowNameFromManifest(manifest);
|
||||||
|
newParsedYaml.metadata.labels = {
|
||||||
|
workflow_id: workflowData.workflow_id,
|
||||||
|
};
|
||||||
|
newParsedYaml.spec.workflowSpec = oldParsedYaml.spec;
|
||||||
|
const tz = {
|
||||||
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC',
|
||||||
|
};
|
||||||
|
Object.entries(tz).forEach(([key, value]) => {
|
||||||
|
newParsedYaml.spec[key] = value;
|
||||||
|
});
|
||||||
|
NewYaml = YAML.stringify(newParsedYaml);
|
||||||
|
workflow.setWorkflowManifest({
|
||||||
|
manifest: NewYaml,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
oldParsedYaml.kind === 'CronWorkflow' &&
|
||||||
|
workflowData.scheduleType.scheduleOnce === 'now'
|
||||||
|
) {
|
||||||
|
const oldParsedYaml = YAML.parse(manifest);
|
||||||
|
const newParsedYaml = YAML.parse(scheduleOnce);
|
||||||
|
delete newParsedYaml.spec;
|
||||||
|
delete newParsedYaml.metadata.generateName;
|
||||||
|
newParsedYaml.metadata.name = fetchWorkflowNameFromManifest(manifest);
|
||||||
|
newParsedYaml.spec = oldParsedYaml.spec.workflowSpec;
|
||||||
|
newParsedYaml.metadata.labels = {
|
||||||
|
workflow_id: workflowData.workflow_id,
|
||||||
|
};
|
||||||
|
NewYaml = YAML.stringify(newParsedYaml);
|
||||||
|
workflow.setWorkflowManifest({
|
||||||
|
manifest: NewYaml,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
oldParsedYaml.kind === 'CronWorkflow' &&
|
||||||
|
workflowData.scheduleType.scheduleOnce !== 'now'
|
||||||
|
// && !isDisabled
|
||||||
|
) {
|
||||||
|
const newParsedYaml = YAML.parse(manifest);
|
||||||
|
newParsedYaml.spec.schedule = workflowData.cronSyntax;
|
||||||
|
// newParsedYaml.spec.suspend = false;
|
||||||
|
delete newParsedYaml.metadata.generateName;
|
||||||
|
newParsedYaml.metadata.name = fetchWorkflowNameFromManifest(manifest);
|
||||||
|
newParsedYaml.metadata.labels = { workflow_id: workflowData.workflow_id };
|
||||||
|
const tz = {
|
||||||
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC',
|
||||||
|
};
|
||||||
|
Object.entries(tz).forEach(([key, value]) => {
|
||||||
|
newParsedYaml.spec[key] = value;
|
||||||
|
});
|
||||||
|
NewYaml = YAML.stringify(newParsedYaml);
|
||||||
|
workflow.setWorkflowManifest({
|
||||||
|
manifest: NewYaml,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Function for recurring date change
|
// Function for recurring date change
|
||||||
const reccuringDateChange = (date: Date | null) => {
|
const reccuringDateChange = (date: Date | null) => {
|
||||||
setSelectedTime(date);
|
setSelectedTime(date);
|
||||||
|
|
@ -202,9 +275,6 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
// UseEffect to update the values of CronSyntax on radio button change
|
// UseEffect to update the values of CronSyntax on radio button change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (value === 'now') {
|
if (value === 'now') {
|
||||||
workflow.setWorkflowDetails({
|
|
||||||
isDisabled: false,
|
|
||||||
});
|
|
||||||
setValueDef('');
|
setValueDef('');
|
||||||
setCronValue({
|
setCronValue({
|
||||||
minute: '',
|
minute: '',
|
||||||
|
|
@ -214,11 +284,6 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
day_week: '',
|
day_week: '',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (value === 'disable') {
|
|
||||||
workflow.setWorkflowDetails({
|
|
||||||
isDisabled: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (value === 'specificTime') {
|
if (value === 'specificTime') {
|
||||||
setValueDef('');
|
setValueDef('');
|
||||||
setCronValue({
|
setCronValue({
|
||||||
|
|
@ -247,10 +312,7 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (valueDef === 'everyHr') {
|
if (valueDef === constants.recurringEveryHour) {
|
||||||
workflow.setWorkflowDetails({
|
|
||||||
isDisabled: false,
|
|
||||||
});
|
|
||||||
setCronValue({
|
setCronValue({
|
||||||
minute: minute.toString(),
|
minute: minute.toString(),
|
||||||
hour: '0-23',
|
hour: '0-23',
|
||||||
|
|
@ -259,10 +321,7 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
day_week: '*',
|
day_week: '*',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (valueDef === 'everyDay') {
|
if (valueDef === constants.recurringEveryDay) {
|
||||||
workflow.setWorkflowDetails({
|
|
||||||
isDisabled: false,
|
|
||||||
});
|
|
||||||
setCronValue({
|
setCronValue({
|
||||||
minute: selectedTime?.getMinutes().toString(),
|
minute: selectedTime?.getMinutes().toString(),
|
||||||
hour: selectedTime?.getHours().toString(),
|
hour: selectedTime?.getHours().toString(),
|
||||||
|
|
@ -271,10 +330,7 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
day_week: '0-6',
|
day_week: '0-6',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (valueDef === 'everyWeek') {
|
if (valueDef === constants.recurringEveryWeek) {
|
||||||
workflow.setWorkflowDetails({
|
|
||||||
isDisabled: false,
|
|
||||||
});
|
|
||||||
setCronValue({
|
setCronValue({
|
||||||
minute: selectedTime?.getMinutes().toString(),
|
minute: selectedTime?.getMinutes().toString(),
|
||||||
hour: selectedTime?.getHours().toString(),
|
hour: selectedTime?.getHours().toString(),
|
||||||
|
|
@ -283,10 +339,7 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
day_week: days.slice(0, 3),
|
day_week: days.slice(0, 3),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (valueDef === 'everyMonth') {
|
if (valueDef === constants.recurringEveryMonth) {
|
||||||
workflow.setWorkflowDetails({
|
|
||||||
isDisabled: false,
|
|
||||||
});
|
|
||||||
setCronValue({
|
setCronValue({
|
||||||
minute: selectedTime?.getMinutes().toString(),
|
minute: selectedTime?.getMinutes().toString(),
|
||||||
hour: selectedTime?.getHours().toString(),
|
hour: selectedTime?.getHours().toString(),
|
||||||
|
|
@ -296,9 +349,6 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (value === 'recurringSchedule' && valueDef === '') {
|
if (value === 'recurringSchedule' && valueDef === '') {
|
||||||
workflow.setWorkflowDetails({
|
|
||||||
isDisabled: false,
|
|
||||||
});
|
|
||||||
template.selectTemplate({ isDisable: true });
|
template.selectTemplate({ isDisable: true });
|
||||||
} else {
|
} else {
|
||||||
template.selectTemplate({ isDisable: false });
|
template.selectTemplate({ isDisable: false });
|
||||||
|
|
@ -312,6 +362,7 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
}, [valueDef, value]);
|
}, [valueDef, value]);
|
||||||
|
|
||||||
function onNext() {
|
function onNext() {
|
||||||
|
EditYaml();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -354,56 +405,23 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
>
|
>
|
||||||
{/* options to choose schedule */}
|
{/* options to choose schedule */}
|
||||||
{!workflowData.isRecurring ? (
|
<FormControlLabel
|
||||||
<FormControlLabel
|
value="now"
|
||||||
value="now"
|
control={
|
||||||
control={
|
<Radio
|
||||||
<Radio
|
classes={{
|
||||||
classes={{
|
root: classes.radio,
|
||||||
root: classes.radio,
|
checked: classes.checked,
|
||||||
checked: classes.checked,
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
}
|
||||||
}
|
|
||||||
label={
|
|
||||||
<Typography className={classes.radioText}>
|
|
||||||
{t('createWorkflow.scheduleWorkflow.radio.now')}
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
) : YAML.parse(manifest).spec.suspend === true ? (
|
|
||||||
<></>
|
|
||||||
) : workflowData.isRecurring ? (
|
|
||||||
<FormControlLabel
|
|
||||||
value="disable"
|
|
||||||
control={
|
|
||||||
<Radio
|
|
||||||
classes={{
|
|
||||||
root: classes.radio,
|
|
||||||
checked: classes.checked,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label={
|
|
||||||
<Typography className={classes.radioText}>
|
|
||||||
Disable Schedule
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)}
|
|
||||||
{/* <FormControlLabel
|
|
||||||
value="specificTime"
|
|
||||||
disabled
|
|
||||||
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.now')}
|
||||||
</Typography>
|
</Typography>
|
||||||
}
|
}
|
||||||
/> */}
|
/>
|
||||||
{value === 'specificTime' ? (
|
{value === 'specificTime' && (
|
||||||
<div className={classes.schLater}>
|
<div className={classes.schLater}>
|
||||||
<Typography className={classes.captionText}>
|
<Typography className={classes.captionText}>
|
||||||
{t('createWorkflow.scheduleWorkflow.radio.future')}
|
{t('createWorkflow.scheduleWorkflow.radio.future')}
|
||||||
|
|
@ -426,8 +444,6 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)}
|
)}
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
value="recurringSchedule"
|
value="recurringSchedule"
|
||||||
|
|
@ -442,7 +458,7 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
</Typography>
|
</Typography>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{value === 'recurringSchedule' ? (
|
{value === 'recurringSchedule' && (
|
||||||
<div className={classes.schLater}>
|
<div className={classes.schLater}>
|
||||||
<Typography className={classes.captionText}>
|
<Typography className={classes.captionText}>
|
||||||
{t('createWorkflow.scheduleWorkflow.radio.rightRecurr')}
|
{t('createWorkflow.scheduleWorkflow.radio.rightRecurr')}
|
||||||
|
|
@ -460,7 +476,7 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
value="everyHr"
|
value={constants.recurringEveryHour}
|
||||||
control={
|
control={
|
||||||
<Radio
|
<Radio
|
||||||
classes={{
|
classes={{
|
||||||
|
|
@ -471,7 +487,7 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
}
|
}
|
||||||
label={t('createWorkflow.scheduleWorkflow.every.hr')}
|
label={t('createWorkflow.scheduleWorkflow.every.hr')}
|
||||||
/>
|
/>
|
||||||
{valueDef === 'everyHr' ? (
|
{valueDef === constants.recurringEveryHour && (
|
||||||
<div>
|
<div>
|
||||||
<div className={classes.scRandom}>
|
<div className={classes.scRandom}>
|
||||||
<Typography className={classes.scRandsub1}>
|
<Typography className={classes.scRandsub1}>
|
||||||
|
|
@ -508,11 +524,9 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)}
|
)}
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
value="everyDay"
|
value={constants.recurringEveryDay}
|
||||||
control={
|
control={
|
||||||
<Radio
|
<Radio
|
||||||
classes={{
|
classes={{
|
||||||
|
|
@ -523,7 +537,7 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
}
|
}
|
||||||
label={t('createWorkflow.scheduleWorkflow.every.day')}
|
label={t('createWorkflow.scheduleWorkflow.every.day')}
|
||||||
/>
|
/>
|
||||||
{valueDef === 'everyDay' ? (
|
{valueDef === constants.recurringEveryDay && (
|
||||||
<div>
|
<div>
|
||||||
<div className={classes.scRandom}>
|
<div className={classes.scRandom}>
|
||||||
<Typography className={classes.scRandsub1}>
|
<Typography className={classes.scRandsub1}>
|
||||||
|
|
@ -551,11 +565,9 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)}
|
)}
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
value="everyWeek"
|
value={constants.recurringEveryWeek}
|
||||||
control={
|
control={
|
||||||
<Radio
|
<Radio
|
||||||
classes={{
|
classes={{
|
||||||
|
|
@ -568,7 +580,7 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
'createWorkflow.scheduleWorkflow.every.week'
|
'createWorkflow.scheduleWorkflow.every.week'
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{valueDef === 'everyWeek' ? (
|
{valueDef === constants.recurringEveryWeek && (
|
||||||
<div>
|
<div>
|
||||||
<div className={classes.scRandom}>
|
<div className={classes.scRandom}>
|
||||||
<Typography className={classes.scRandsub1}>
|
<Typography className={classes.scRandsub1}>
|
||||||
|
|
@ -633,11 +645,9 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)}
|
)}
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
value="everyMonth"
|
value={constants.recurringEveryMonth}
|
||||||
control={
|
control={
|
||||||
<Radio
|
<Radio
|
||||||
classes={{
|
classes={{
|
||||||
|
|
@ -650,7 +660,7 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
'createWorkflow.scheduleWorkflow.every.month'
|
'createWorkflow.scheduleWorkflow.every.month'
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{valueDef === 'everyMonth' ? (
|
{valueDef === constants.recurringEveryMonth && (
|
||||||
<div>
|
<div>
|
||||||
<div className={classes.scRandom}>
|
<div className={classes.scRandom}>
|
||||||
<Typography className={classes.scRandsub1}>
|
<Typography className={classes.scRandsub1}>
|
||||||
|
|
@ -687,15 +697,11 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)}
|
)}
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)}
|
)}
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,23 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||||
background: theme.palette.background.paper,
|
background: theme.palette.background.paper,
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
padding: theme.spacing(0, 2),
|
padding: theme.spacing(0, 2),
|
||||||
margin: '0 auto',
|
margin: '1rem auto',
|
||||||
width: '98%',
|
width: '98%',
|
||||||
height: '100%',
|
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
[theme.breakpoints.up('lg')]: {
|
[theme.breakpoints.up('lg')]: {
|
||||||
width: '99%',
|
width: '99%',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
title: {
|
||||||
|
padding: theme.spacing(0, 2),
|
||||||
|
fontWeight: 700,
|
||||||
|
fontSize: '2rem',
|
||||||
|
[theme.breakpoints.up('lg')]: {
|
||||||
|
fontSize: '2.3rem',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// Inner Container
|
// Inner Container
|
||||||
innerContainer: {
|
innerContainer: {
|
||||||
margin: theme.spacing(4, 'auto'),
|
margin: theme.spacing(4, 'auto'),
|
||||||
|
|
@ -28,7 +36,10 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||||
|
|
||||||
headerText: {
|
headerText: {
|
||||||
marginTop: theme.spacing(1.25),
|
marginTop: theme.spacing(1.25),
|
||||||
fontSize: '1.5625rem',
|
fontSize: '1.2rem',
|
||||||
|
[theme.breakpoints.up('lg')]: {
|
||||||
|
fontSize: '1.4rem',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
schBody: {
|
schBody: {
|
||||||
width: '32.18rem',
|
width: '32.18rem',
|
||||||
|
|
@ -68,6 +79,11 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
checked: {},
|
checked: {},
|
||||||
|
buttonDiv: {
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
|
||||||
/* For recurring schedule options */
|
/* For recurring schedule options */
|
||||||
scRandom: {
|
scRandom: {
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ 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 { getProjectID, getProjectRole } from '../../../utils/getSearchParams';
|
import { getProjectID, getProjectRole } from '../../../utils/getSearchParams';
|
||||||
|
import { fetchWorkflowNameFromManifest } from '../../../utils/yamlUtils';
|
||||||
import useStyles from './styles';
|
import useStyles from './styles';
|
||||||
|
|
||||||
interface WorkflowProps {
|
interface WorkflowProps {
|
||||||
|
|
@ -67,7 +68,7 @@ const VerifyCommit = forwardRef((_, ref) => {
|
||||||
(state: RootState) => state.workflowData
|
(state: RootState) => state.workflowData
|
||||||
);
|
);
|
||||||
|
|
||||||
const { clusterid, cronSyntax, isDisabled, clustername } = workflowData;
|
const { clusterid, cronSyntax, clustername } = workflowData;
|
||||||
|
|
||||||
const manifest = useSelector(
|
const manifest = useSelector(
|
||||||
(state: RootState) => state.workflowManifest.manifest
|
(state: RootState) => state.workflowManifest.manifest
|
||||||
|
|
@ -85,9 +86,6 @@ const VerifyCommit = forwardRef((_, ref) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const fetchWorkflowNameFromManifest = (manifest: string) => {
|
|
||||||
return YAML.parse(manifest).metadata.name;
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
saveWorkflowGenerateName(manifest);
|
saveWorkflowGenerateName(manifest);
|
||||||
|
|
@ -111,7 +109,7 @@ const VerifyCommit = forwardRef((_, ref) => {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const [yamlStatus, setYamlStatus] = React.useState(
|
const [yamlStatus, setYamlStatus] = React.useState(
|
||||||
'Your code is fine. You can move on!'
|
`${t('createWorkflow.verifyCommit.codeIsFine')}`
|
||||||
);
|
);
|
||||||
|
|
||||||
const [modified, setModified] = React.useState(false);
|
const [modified, setModified] = React.useState(false);
|
||||||
|
|
@ -245,6 +243,7 @@ const VerifyCommit = forwardRef((_, ref) => {
|
||||||
localforage.removeItem('hasSetWorkflowData');
|
localforage.removeItem('hasSetWorkflowData');
|
||||||
localforage.removeItem('weights');
|
localforage.removeItem('weights');
|
||||||
localforage.removeItem('selectedHub');
|
localforage.removeItem('selectedHub');
|
||||||
|
localforage.removeItem('editSchedule');
|
||||||
setFinishModalOpen(false);
|
setFinishModalOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -265,7 +264,7 @@ const VerifyCommit = forwardRef((_, ref) => {
|
||||||
<div className={classes.suHeader}>
|
<div className={classes.suHeader}>
|
||||||
<div>
|
<div>
|
||||||
<Typography className={classes.headerText}>
|
<Typography className={classes.headerText}>
|
||||||
<strong> {t('createWorkflow.verifyCommit.header')}</strong>
|
{t('createWorkflow.verifyCommit.header')}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography className={classes.description}>
|
<Typography className={classes.description}>
|
||||||
{t('createWorkflow.verifyCommit.info')}
|
{t('createWorkflow.verifyCommit.info')}
|
||||||
|
|
@ -280,7 +279,7 @@ const VerifyCommit = forwardRef((_, ref) => {
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<Typography className={classes.sumText}>
|
<Typography className={classes.sumText}>
|
||||||
<strong>{t('createWorkflow.verifyCommit.summary.header')}</strong>
|
{t('createWorkflow.verifyCommit.summary.header')}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<div className={classes.outerSum}>
|
<div className={classes.outerSum}>
|
||||||
|
|
@ -298,7 +297,6 @@ const VerifyCommit = forwardRef((_, ref) => {
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handleNameChange({ changedName: e.target.value })
|
handleNameChange({ changedName: e.target.value })
|
||||||
}
|
}
|
||||||
disabled={workflowData.isRecurring}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -339,18 +337,7 @@ const VerifyCommit = forwardRef((_, ref) => {
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.schCol2}>
|
<div className={classes.schCol2}>
|
||||||
{/* <CustomDate disabled={edit} />
|
{cronSyntax === '' ? (
|
||||||
<CustomTime
|
|
||||||
handleDateChange={handleDateChange}
|
|
||||||
value={selectedDate}
|
|
||||||
ampm
|
|
||||||
disabled={edit}
|
|
||||||
/> */}
|
|
||||||
{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>
|
||||||
|
|
@ -360,9 +347,9 @@ const VerifyCommit = forwardRef((_, ref) => {
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className={classes.editButton1}>
|
<div className={classes.editButton}>
|
||||||
<IconButton>
|
<IconButton>
|
||||||
<EditIcon className={classes.editbtn} data-cy="edit" />
|
<EditIcon className={classes.editIcon} data-cy="edit" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -376,29 +363,25 @@ const VerifyCommit = forwardRef((_, ref) => {
|
||||||
{weights.length === 0 ? (
|
{weights.length === 0 ? (
|
||||||
<div>
|
<div>
|
||||||
<Typography className={classes.errorText}>
|
<Typography className={classes.errorText}>
|
||||||
<strong>{t('createWorkflow.verifyCommit.error')}</strong>
|
{t('createWorkflow.verifyCommit.error')}
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className={classes.adjWeights}>
|
<div className={classes.adjWeights}>
|
||||||
<div
|
<div className={classes.progress}>
|
||||||
className={classes.progress}
|
|
||||||
style={{ flexWrap: 'wrap' }}
|
|
||||||
>
|
|
||||||
{WorkflowTestData.map((Test) => (
|
{WorkflowTestData.map((Test) => (
|
||||||
<AdjustedWeights
|
<AdjustedWeights
|
||||||
key={Test.weight}
|
key={Test.weight}
|
||||||
testName={`${Test.experimentName} test`}
|
testName={`${Test.experimentName} ${t(
|
||||||
|
'createWorkflow.verifyCommit.test'
|
||||||
|
)}`}
|
||||||
testValue={Test.weight}
|
testValue={Test.weight}
|
||||||
spacing={false}
|
spacing={false}
|
||||||
icon={false}
|
icon={false}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<ButtonOutlined
|
<ButtonOutlined data-cy="testRunButton">
|
||||||
disabled={workflowData.isRecurring}
|
|
||||||
data-cy="testRunButton"
|
|
||||||
>
|
|
||||||
{t('createWorkflow.verifyCommit.button.edit')}
|
{t('createWorkflow.verifyCommit.button.edit')}
|
||||||
</ButtonOutlined>
|
</ButtonOutlined>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -406,22 +389,28 @@ const VerifyCommit = forwardRef((_, ref) => {
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.summaryDiv}>
|
<div className={classes.summaryDiv}>
|
||||||
<div className={classes.innerSumDiv}>
|
<div className={classes.innerSumDiv}>
|
||||||
<Typography className={classes.col1}>YAML:</Typography>
|
<Typography className={classes.col1}>
|
||||||
|
{t('createWorkflow.verifyCommit.YAML')}
|
||||||
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.yamlFlex}>
|
<div className={classes.yamlFlex}>
|
||||||
{weights.length === 0 ? (
|
{weights.length === 0 ? (
|
||||||
<Typography>
|
<Typography className={classes.spacingHorizontal}>
|
||||||
{' '}
|
{t('createWorkflow.verifyCommit.errYaml')}
|
||||||
{t('createWorkflow.verifyCommit.errYaml')}{' '}
|
|
||||||
</Typography>
|
</Typography>
|
||||||
) : (
|
) : (
|
||||||
<Typography>
|
<Typography>
|
||||||
<b>{yamlStatus}</b>{' '}
|
<b>{yamlStatus}</b>
|
||||||
{t('createWorkflow.verifyCommit.youCanMoveOn')}
|
<span className={classes.spacingHorizontal}>
|
||||||
|
{t('createWorkflow.verifyCommit.youCanMoveOn')}
|
||||||
|
</span>
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
<br />
|
<br />
|
||||||
<ButtonFilled style={{ width: '60%' }} onClick={handleOpen}>
|
<ButtonFilled
|
||||||
|
className={classes.verifyYAMLButton}
|
||||||
|
onClick={handleOpen}
|
||||||
|
>
|
||||||
{t('createWorkflow.verifyCommit.button.viewYaml')}
|
{t('createWorkflow.verifyCommit.button.viewYaml')}
|
||||||
</ButtonFilled>
|
</ButtonFilled>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -467,7 +456,9 @@ const VerifyCommit = forwardRef((_, ref) => {
|
||||||
<br />
|
<br />
|
||||||
<span className={classes.successful}>{workflow.name}</span>,
|
<span className={classes.successful}>{workflow.name}</span>,
|
||||||
<br />
|
<br />
|
||||||
<strong>{t('workflowStepper.successful')}</strong>
|
<span className={classes.bold}>
|
||||||
|
{t('workflowStepper.successful')}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.headWorkflow}>
|
<div className={classes.headWorkflow}>
|
||||||
{t('workflowStepper.congratulationsSub1')} <br />{' '}
|
{t('workflowStepper.congratulationsSub1')} <br />{' '}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
},
|
},
|
||||||
headerText: {
|
headerText: {
|
||||||
|
fontWeight: 700,
|
||||||
fontSize: '1.2rem',
|
fontSize: '1.2rem',
|
||||||
[theme.breakpoints.up('lg')]: {
|
[theme.breakpoints.up('lg')]: {
|
||||||
fontSize: '1.4rem',
|
fontSize: '1.4rem',
|
||||||
|
|
@ -64,8 +65,8 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||||
},
|
},
|
||||||
sumText: {
|
sumText: {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
marginTop: theme.spacing(4.5),
|
margin: theme.spacing(2, 0),
|
||||||
marginBottom: theme.spacing(3),
|
fontWeight: 700,
|
||||||
fontSize: '1.2rem',
|
fontSize: '1.2rem',
|
||||||
[theme.breakpoints.up('lg')]: {
|
[theme.breakpoints.up('lg')]: {
|
||||||
fontSize: '1.4rem',
|
fontSize: '1.4rem',
|
||||||
|
|
@ -98,11 +99,12 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||||
marginLeft: theme.spacing(7),
|
marginLeft: theme.spacing(7),
|
||||||
paddingTop: theme.spacing(0.5),
|
paddingTop: theme.spacing(0.5),
|
||||||
},
|
},
|
||||||
editButton1: {
|
editButton: {
|
||||||
marginLeft: theme.spacing(1),
|
height: '1rem',
|
||||||
},
|
},
|
||||||
editbtn: {
|
editIcon: {
|
||||||
color: theme.palette.text.secondary,
|
color: theme.palette.text.primary,
|
||||||
|
height: '0.8rem',
|
||||||
},
|
},
|
||||||
link: {
|
link: {
|
||||||
fontSize: '0.875rem',
|
fontSize: '0.875rem',
|
||||||
|
|
@ -135,8 +137,12 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||||
buttonOutlineText: {
|
buttonOutlineText: {
|
||||||
padding: theme.spacing(1.5),
|
padding: theme.spacing(1.5),
|
||||||
},
|
},
|
||||||
|
spacingHorizontal: {
|
||||||
|
margin: theme.spacing(0, 1),
|
||||||
|
},
|
||||||
errorText: {
|
errorText: {
|
||||||
color: theme.palette.error.main,
|
color: theme.palette.error.main,
|
||||||
|
fontWeight: 700,
|
||||||
fontSize: '1rem',
|
fontSize: '1rem',
|
||||||
marginLeft: theme.spacing(5),
|
marginLeft: theme.spacing(5),
|
||||||
},
|
},
|
||||||
|
|
@ -148,6 +154,7 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||||
progress: {
|
progress: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
flexWrap: 'wrap',
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
marginLeft: theme.spacing(5),
|
marginLeft: theme.spacing(5),
|
||||||
},
|
},
|
||||||
|
|
@ -159,6 +166,9 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||||
marginTop: theme.spacing(-6),
|
marginTop: theme.spacing(-6),
|
||||||
marginRight: theme.spacing(-2.5),
|
marginRight: theme.spacing(-2.5),
|
||||||
},
|
},
|
||||||
|
verifyYAMLButton: {
|
||||||
|
width: '60%',
|
||||||
|
},
|
||||||
|
|
||||||
// Modal
|
// Modal
|
||||||
modal: {
|
modal: {
|
||||||
|
|
@ -190,6 +200,10 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||||
successful: {
|
successful: {
|
||||||
fontSize: '2.2rem',
|
fontSize: '2.2rem',
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
|
margin: theme.spacing(2, 0),
|
||||||
|
},
|
||||||
|
bold: {
|
||||||
|
fontWeight: 700,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
export default useStyles;
|
export default useStyles;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue