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'
|
||||
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.'
|
||||
save: 'Save Changes'
|
||||
save: 'Save Changes'
|
||||
scheduleNow: Schedule now
|
||||
scheduleAfter: Schedule after some time
|
||||
scheduleAfterSometime: 'Choose the minutes, hours, or days when you want to start workflow'
|
||||
|
|
@ -154,6 +154,15 @@ schedule:
|
|||
workflowBtn: Go to Workflow
|
||||
backBtn: Go Back
|
||||
|
||||
editSchedule:
|
||||
title: Edit Schedule
|
||||
verify: Verify
|
||||
cancel: Cancel
|
||||
save: Save Changes
|
||||
edit: Edit
|
||||
theSchedule: The Schedule
|
||||
successfullyCreated: is successfully created
|
||||
|
||||
community:
|
||||
title: 'Community'
|
||||
heading: 'Litmus Insights'
|
||||
|
|
@ -794,6 +803,8 @@ createWorkflow:
|
|||
errYaml: Error in CRD Yaml.
|
||||
codeIsFine: Your code is fine.
|
||||
youCanMoveOn: You can move on !
|
||||
test: test
|
||||
YAML: YAML
|
||||
button:
|
||||
edit: Edit
|
||||
viewYaml: View YAML
|
||||
|
|
|
|||
|
|
@ -1,13 +1,8 @@
|
|||
import { Card, CardActionArea, Typography } from '@material-ui/core';
|
||||
import React from 'react';
|
||||
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 ArrowDisabled } from '../../svg/arrow_disabled.svg';
|
||||
import { getProjectID, getProjectRole } from '../../utils/getSearchParams';
|
||||
import useStyles from './styles';
|
||||
|
||||
interface CreateWorkflowCardProps {
|
||||
|
|
@ -19,28 +14,9 @@ const CreateWorkflowCard: React.FC<CreateWorkflowCardProps> = ({
|
|||
}) => {
|
||||
const { t } = useTranslation();
|
||||
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 (
|
||||
<Card
|
||||
onClick={isDisabled ? () => {} : handleCreateWorkflow}
|
||||
className={classes.createCard}
|
||||
data-cy="createWorkflow"
|
||||
>
|
||||
<Card className={classes.createCard} data-cy="createWorkflow">
|
||||
<CardActionArea className={classes.createCardAction}>
|
||||
<Typography className={classes.createCardHeading}>
|
||||
{t('home.workflow.heading')}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import { QuickActionCard } from 'litmus-ui';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { constants } from '../../constants';
|
||||
import { Role } from '../../models/graphql/user';
|
||||
import useActions from '../../redux/actions';
|
||||
import * as TabActions from '../../redux/actions/tabs';
|
||||
|
|
@ -97,7 +98,7 @@ const LocalQuickActionCard: React.FC<LocalQuickActionCardProps> = ({
|
|||
? {
|
||||
src: '/icons/survey.svg',
|
||||
alt: 'survey',
|
||||
onClick: () => window.open('https://forms.gle/qMuVphRyEWCFqjD56'),
|
||||
onClick: () => window.open(constants.FeedbackForm),
|
||||
text: t('quickActionCard.quickSurvey'),
|
||||
}
|
||||
: emptyData,
|
||||
|
|
|
|||
|
|
@ -7,9 +7,8 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|||
},
|
||||
drawerPaper: {
|
||||
width: '100%',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
backgroundColor: theme.palette.sidebarMenu,
|
||||
position: 'relative',
|
||||
color: 'inherit',
|
||||
},
|
||||
litmusDiv: {
|
||||
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 Settings = lazy(() => import('../../pages/Settings'));
|
||||
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 AnalyticsPage = lazy(() => import('../../pages/AnalyticsPage'));
|
||||
const AnalyticsDashboard = lazy(
|
||||
|
|
@ -194,11 +196,16 @@ const Routes: React.FC = () => {
|
|||
path="/workflows/:workflowRunId"
|
||||
component={WorkflowDetails}
|
||||
/>
|
||||
{/* <Route
|
||||
<Route
|
||||
exact
|
||||
path="/workflows/schedule/:scheduleProjectID/:workflowName" // Check
|
||||
component={SchedulePage}
|
||||
/> */}
|
||||
path="/workflows/schedule/:scheduleProjectID/:workflowName"
|
||||
component={EditSchedule}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/workflows/schedule/:scheduleProjectID/:workflowName/set"
|
||||
component={SetNewSchedule}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/workflows/template/:templateName"
|
||||
|
|
|
|||
|
|
@ -28,9 +28,6 @@ export interface customWorkflow {
|
|||
}
|
||||
|
||||
export interface WorkflowData {
|
||||
id: string;
|
||||
isRecurring: boolean;
|
||||
isDisabled: boolean;
|
||||
chaosEngineChanged: boolean;
|
||||
namespace: 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';
|
||||
|
||||
const initialState: WorkflowData = {
|
||||
id: '',
|
||||
isRecurring: false,
|
||||
isDisabled: false,
|
||||
chaosEngineChanged: false,
|
||||
namespace: 'litmus',
|
||||
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) => {
|
||||
return parameterString
|
||||
.substring(1, parameterString.length - 1)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import { RERUN_CHAOS_WORKFLOW } from '../../../graphql/mutations';
|
|||
import { ScheduleWorkflow } from '../../../models/graphql/scheduleData';
|
||||
import useActions from '../../../redux/actions';
|
||||
import * as TabActions from '../../../redux/actions/tabs';
|
||||
import * as WorkflowActions from '../../../redux/actions/workflow';
|
||||
import { history } from '../../../redux/configureStore';
|
||||
import { ReactComponent as CrossMarkIcon } from '../../../svg/crossmark.svg';
|
||||
import timeDifferenceForDate from '../../../utils/datesModifier';
|
||||
|
|
@ -63,8 +62,6 @@ const TableData: React.FC<TableDataProps> = ({
|
|||
setPopAnchorEl(null);
|
||||
};
|
||||
|
||||
const workflow = useActions(WorkflowActions);
|
||||
|
||||
const handlePopOverClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||
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, {
|
||||
onCompleted: () => {
|
||||
tabs.changeWorkflowsTabs(0);
|
||||
|
|
|
|||
|
|
@ -18,11 +18,14 @@ import { useSelector } from 'react-redux';
|
|||
import YAML from 'yaml';
|
||||
import CustomDate from '../../../components/DateTime/CustomDate/index';
|
||||
import CustomTime from '../../../components/DateTime/CustomTime/index';
|
||||
import { constants } from '../../../constants';
|
||||
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 { RootState } from '../../../redux/reducers';
|
||||
import { cronWorkflow, workflowOnce } from '../../../utils/workflowTemplate';
|
||||
import { fetchWorkflowNameFromManifest } from '../../../utils/yamlUtils';
|
||||
import SetTime from './SetTime/index';
|
||||
import useStyles from './styles';
|
||||
|
||||
|
|
@ -54,6 +57,10 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
);
|
||||
const workflow = useActions(WorkflowActions);
|
||||
const template = useActions(TemplateSelectionActions);
|
||||
|
||||
const scheduleOnce = workflowOnce;
|
||||
const scheduleMore = cronWorkflow;
|
||||
|
||||
// Controls Radio Buttons
|
||||
const [value, setValue] = React.useState(
|
||||
workflowData.scheduleType.scheduleOnce
|
||||
|
|
@ -81,10 +88,6 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
workflow.setWorkflowDetails({
|
||||
cronSyntax,
|
||||
});
|
||||
if (value === 'disable')
|
||||
workflow.setWorkflowDetails({
|
||||
isDisabled: true,
|
||||
});
|
||||
}, [cronValue]);
|
||||
|
||||
const classes = useStyles();
|
||||
|
|
@ -163,6 +166,76 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
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
|
||||
const reccuringDateChange = (date: Date | null) => {
|
||||
setSelectedTime(date);
|
||||
|
|
@ -202,9 +275,6 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
// UseEffect to update the values of CronSyntax on radio button change
|
||||
useEffect(() => {
|
||||
if (value === 'now') {
|
||||
workflow.setWorkflowDetails({
|
||||
isDisabled: false,
|
||||
});
|
||||
setValueDef('');
|
||||
setCronValue({
|
||||
minute: '',
|
||||
|
|
@ -214,11 +284,6 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
day_week: '',
|
||||
});
|
||||
}
|
||||
if (value === 'disable') {
|
||||
workflow.setWorkflowDetails({
|
||||
isDisabled: true,
|
||||
});
|
||||
}
|
||||
if (value === 'specificTime') {
|
||||
setValueDef('');
|
||||
setCronValue({
|
||||
|
|
@ -247,10 +312,7 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
});
|
||||
}
|
||||
}
|
||||
if (valueDef === 'everyHr') {
|
||||
workflow.setWorkflowDetails({
|
||||
isDisabled: false,
|
||||
});
|
||||
if (valueDef === constants.recurringEveryHour) {
|
||||
setCronValue({
|
||||
minute: minute.toString(),
|
||||
hour: '0-23',
|
||||
|
|
@ -259,10 +321,7 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
day_week: '*',
|
||||
});
|
||||
}
|
||||
if (valueDef === 'everyDay') {
|
||||
workflow.setWorkflowDetails({
|
||||
isDisabled: false,
|
||||
});
|
||||
if (valueDef === constants.recurringEveryDay) {
|
||||
setCronValue({
|
||||
minute: selectedTime?.getMinutes().toString(),
|
||||
hour: selectedTime?.getHours().toString(),
|
||||
|
|
@ -271,10 +330,7 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
day_week: '0-6',
|
||||
});
|
||||
}
|
||||
if (valueDef === 'everyWeek') {
|
||||
workflow.setWorkflowDetails({
|
||||
isDisabled: false,
|
||||
});
|
||||
if (valueDef === constants.recurringEveryWeek) {
|
||||
setCronValue({
|
||||
minute: selectedTime?.getMinutes().toString(),
|
||||
hour: selectedTime?.getHours().toString(),
|
||||
|
|
@ -283,10 +339,7 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
day_week: days.slice(0, 3),
|
||||
});
|
||||
}
|
||||
if (valueDef === 'everyMonth') {
|
||||
workflow.setWorkflowDetails({
|
||||
isDisabled: false,
|
||||
});
|
||||
if (valueDef === constants.recurringEveryMonth) {
|
||||
setCronValue({
|
||||
minute: selectedTime?.getMinutes().toString(),
|
||||
hour: selectedTime?.getHours().toString(),
|
||||
|
|
@ -296,9 +349,6 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
});
|
||||
}
|
||||
if (value === 'recurringSchedule' && valueDef === '') {
|
||||
workflow.setWorkflowDetails({
|
||||
isDisabled: false,
|
||||
});
|
||||
template.selectTemplate({ isDisable: true });
|
||||
} else {
|
||||
template.selectTemplate({ isDisable: false });
|
||||
|
|
@ -312,6 +362,7 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
}, [valueDef, value]);
|
||||
|
||||
function onNext() {
|
||||
EditYaml();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -354,56 +405,23 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
onChange={handleChange}
|
||||
>
|
||||
{/* options to choose schedule */}
|
||||
{!workflowData.isRecurring ? (
|
||||
<FormControlLabel
|
||||
value="now"
|
||||
control={
|
||||
<Radio
|
||||
classes={{
|
||||
root: classes.radio,
|
||||
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 }}/>}
|
||||
<FormControlLabel
|
||||
value="now"
|
||||
control={
|
||||
<Radio
|
||||
classes={{
|
||||
root: classes.radio,
|
||||
checked: classes.checked,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label={
|
||||
<Typography className={classes.radioText}>
|
||||
{t('createWorkflow.scheduleWorkflow.radio.specific')}
|
||||
{t('createWorkflow.scheduleWorkflow.radio.now')}
|
||||
</Typography>
|
||||
}
|
||||
/> */}
|
||||
{value === 'specificTime' ? (
|
||||
/>
|
||||
{value === 'specificTime' && (
|
||||
<div className={classes.schLater}>
|
||||
<Typography className={classes.captionText}>
|
||||
{t('createWorkflow.scheduleWorkflow.radio.future')}
|
||||
|
|
@ -426,8 +444,6 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<FormControlLabel
|
||||
value="recurringSchedule"
|
||||
|
|
@ -442,7 +458,7 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
</Typography>
|
||||
}
|
||||
/>
|
||||
{value === 'recurringSchedule' ? (
|
||||
{value === 'recurringSchedule' && (
|
||||
<div className={classes.schLater}>
|
||||
<Typography className={classes.captionText}>
|
||||
{t('createWorkflow.scheduleWorkflow.radio.rightRecurr')}
|
||||
|
|
@ -460,7 +476,7 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
}}
|
||||
>
|
||||
<FormControlLabel
|
||||
value="everyHr"
|
||||
value={constants.recurringEveryHour}
|
||||
control={
|
||||
<Radio
|
||||
classes={{
|
||||
|
|
@ -471,7 +487,7 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
}
|
||||
label={t('createWorkflow.scheduleWorkflow.every.hr')}
|
||||
/>
|
||||
{valueDef === 'everyHr' ? (
|
||||
{valueDef === constants.recurringEveryHour && (
|
||||
<div>
|
||||
<div className={classes.scRandom}>
|
||||
<Typography className={classes.scRandsub1}>
|
||||
|
|
@ -508,11 +524,9 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<FormControlLabel
|
||||
value="everyDay"
|
||||
value={constants.recurringEveryDay}
|
||||
control={
|
||||
<Radio
|
||||
classes={{
|
||||
|
|
@ -523,7 +537,7 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
}
|
||||
label={t('createWorkflow.scheduleWorkflow.every.day')}
|
||||
/>
|
||||
{valueDef === 'everyDay' ? (
|
||||
{valueDef === constants.recurringEveryDay && (
|
||||
<div>
|
||||
<div className={classes.scRandom}>
|
||||
<Typography className={classes.scRandsub1}>
|
||||
|
|
@ -551,11 +565,9 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<FormControlLabel
|
||||
value="everyWeek"
|
||||
value={constants.recurringEveryWeek}
|
||||
control={
|
||||
<Radio
|
||||
classes={{
|
||||
|
|
@ -568,7 +580,7 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
'createWorkflow.scheduleWorkflow.every.week'
|
||||
)}
|
||||
/>
|
||||
{valueDef === 'everyWeek' ? (
|
||||
{valueDef === constants.recurringEveryWeek && (
|
||||
<div>
|
||||
<div className={classes.scRandom}>
|
||||
<Typography className={classes.scRandsub1}>
|
||||
|
|
@ -633,11 +645,9 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<FormControlLabel
|
||||
value="everyMonth"
|
||||
value={constants.recurringEveryMonth}
|
||||
control={
|
||||
<Radio
|
||||
classes={{
|
||||
|
|
@ -650,7 +660,7 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
'createWorkflow.scheduleWorkflow.every.month'
|
||||
)}
|
||||
/>
|
||||
{valueDef === 'everyMonth' ? (
|
||||
{valueDef === constants.recurringEveryMonth && (
|
||||
<div>
|
||||
<div className={classes.scRandom}>
|
||||
<Typography className={classes.scRandsub1}>
|
||||
|
|
@ -687,15 +697,11 @@ const ScheduleWorkflow = forwardRef((_, ref) => {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
|
|
|
|||
|
|
@ -5,15 +5,23 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|||
background: theme.palette.background.paper,
|
||||
color: theme.palette.text.primary,
|
||||
padding: theme.spacing(0, 2),
|
||||
margin: '0 auto',
|
||||
margin: '1rem auto',
|
||||
width: '98%',
|
||||
height: '100%',
|
||||
flexDirection: 'column',
|
||||
[theme.breakpoints.up('lg')]: {
|
||||
width: '99%',
|
||||
},
|
||||
},
|
||||
|
||||
title: {
|
||||
padding: theme.spacing(0, 2),
|
||||
fontWeight: 700,
|
||||
fontSize: '2rem',
|
||||
[theme.breakpoints.up('lg')]: {
|
||||
fontSize: '2.3rem',
|
||||
},
|
||||
},
|
||||
|
||||
// Inner Container
|
||||
innerContainer: {
|
||||
margin: theme.spacing(4, 'auto'),
|
||||
|
|
@ -28,7 +36,10 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|||
|
||||
headerText: {
|
||||
marginTop: theme.spacing(1.25),
|
||||
fontSize: '1.5625rem',
|
||||
fontSize: '1.2rem',
|
||||
[theme.breakpoints.up('lg')]: {
|
||||
fontSize: '1.4rem',
|
||||
},
|
||||
},
|
||||
schBody: {
|
||||
width: '32.18rem',
|
||||
|
|
@ -68,6 +79,11 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|||
},
|
||||
},
|
||||
checked: {},
|
||||
buttonDiv: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
width: '100%',
|
||||
},
|
||||
|
||||
/* For recurring schedule options */
|
||||
scRandom: {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ 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 WorkflowProps {
|
||||
|
|
@ -67,7 +68,7 @@ const VerifyCommit = forwardRef((_, ref) => {
|
|||
(state: RootState) => state.workflowData
|
||||
);
|
||||
|
||||
const { clusterid, cronSyntax, isDisabled, clustername } = workflowData;
|
||||
const { clusterid, cronSyntax, clustername } = workflowData;
|
||||
|
||||
const manifest = useSelector(
|
||||
(state: RootState) => state.workflowManifest.manifest
|
||||
|
|
@ -85,9 +86,6 @@ const VerifyCommit = forwardRef((_, ref) => {
|
|||
});
|
||||
}
|
||||
};
|
||||
const fetchWorkflowNameFromManifest = (manifest: string) => {
|
||||
return YAML.parse(manifest).metadata.name;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
saveWorkflowGenerateName(manifest);
|
||||
|
|
@ -111,7 +109,7 @@ const VerifyCommit = forwardRef((_, ref) => {
|
|||
}, []);
|
||||
|
||||
const [yamlStatus, setYamlStatus] = React.useState(
|
||||
'Your code is fine. You can move on!'
|
||||
`${t('createWorkflow.verifyCommit.codeIsFine')}`
|
||||
);
|
||||
|
||||
const [modified, setModified] = React.useState(false);
|
||||
|
|
@ -245,6 +243,7 @@ const VerifyCommit = forwardRef((_, ref) => {
|
|||
localforage.removeItem('hasSetWorkflowData');
|
||||
localforage.removeItem('weights');
|
||||
localforage.removeItem('selectedHub');
|
||||
localforage.removeItem('editSchedule');
|
||||
setFinishModalOpen(false);
|
||||
};
|
||||
|
||||
|
|
@ -265,7 +264,7 @@ const VerifyCommit = forwardRef((_, ref) => {
|
|||
<div className={classes.suHeader}>
|
||||
<div>
|
||||
<Typography className={classes.headerText}>
|
||||
<strong> {t('createWorkflow.verifyCommit.header')}</strong>
|
||||
{t('createWorkflow.verifyCommit.header')}
|
||||
</Typography>
|
||||
<Typography className={classes.description}>
|
||||
{t('createWorkflow.verifyCommit.info')}
|
||||
|
|
@ -280,7 +279,7 @@ const VerifyCommit = forwardRef((_, ref) => {
|
|||
<Divider />
|
||||
|
||||
<Typography className={classes.sumText}>
|
||||
<strong>{t('createWorkflow.verifyCommit.summary.header')}</strong>
|
||||
{t('createWorkflow.verifyCommit.summary.header')}
|
||||
</Typography>
|
||||
|
||||
<div className={classes.outerSum}>
|
||||
|
|
@ -298,7 +297,6 @@ const VerifyCommit = forwardRef((_, ref) => {
|
|||
onChange={(e) =>
|
||||
handleNameChange({ changedName: e.target.value })
|
||||
}
|
||||
disabled={workflowData.isRecurring}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -339,18 +337,7 @@ const VerifyCommit = forwardRef((_, ref) => {
|
|||
</Typography>
|
||||
</div>
|
||||
<div className={classes.schCol2}>
|
||||
{/* <CustomDate disabled={edit} />
|
||||
<CustomTime
|
||||
handleDateChange={handleDateChange}
|
||||
value={selectedDate}
|
||||
ampm
|
||||
disabled={edit}
|
||||
/> */}
|
||||
{isDisabled ? (
|
||||
<Typography className={classes.schedule}>
|
||||
{t('createWorkflow.verifyCommit.summary.disabled')}
|
||||
</Typography>
|
||||
) : cronSyntax === '' ? (
|
||||
{cronSyntax === '' ? (
|
||||
<Typography className={classes.schedule}>
|
||||
{t('createWorkflow.verifyCommit.summary.schedulingNow')}
|
||||
</Typography>
|
||||
|
|
@ -360,9 +347,9 @@ const VerifyCommit = forwardRef((_, ref) => {
|
|||
</Typography>
|
||||
)}
|
||||
|
||||
<div className={classes.editButton1}>
|
||||
<div className={classes.editButton}>
|
||||
<IconButton>
|
||||
<EditIcon className={classes.editbtn} data-cy="edit" />
|
||||
<EditIcon className={classes.editIcon} data-cy="edit" />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -376,29 +363,25 @@ const VerifyCommit = forwardRef((_, ref) => {
|
|||
{weights.length === 0 ? (
|
||||
<div>
|
||||
<Typography className={classes.errorText}>
|
||||
<strong>{t('createWorkflow.verifyCommit.error')}</strong>
|
||||
{t('createWorkflow.verifyCommit.error')}
|
||||
</Typography>
|
||||
</div>
|
||||
) : (
|
||||
<div className={classes.adjWeights}>
|
||||
<div
|
||||
className={classes.progress}
|
||||
style={{ flexWrap: 'wrap' }}
|
||||
>
|
||||
<div className={classes.progress}>
|
||||
{WorkflowTestData.map((Test) => (
|
||||
<AdjustedWeights
|
||||
key={Test.weight}
|
||||
testName={`${Test.experimentName} test`}
|
||||
testName={`${Test.experimentName} ${t(
|
||||
'createWorkflow.verifyCommit.test'
|
||||
)}`}
|
||||
testValue={Test.weight}
|
||||
spacing={false}
|
||||
icon={false}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<ButtonOutlined
|
||||
disabled={workflowData.isRecurring}
|
||||
data-cy="testRunButton"
|
||||
>
|
||||
<ButtonOutlined data-cy="testRunButton">
|
||||
{t('createWorkflow.verifyCommit.button.edit')}
|
||||
</ButtonOutlined>
|
||||
</div>
|
||||
|
|
@ -406,22 +389,28 @@ const VerifyCommit = forwardRef((_, ref) => {
|
|||
</div>
|
||||
<div className={classes.summaryDiv}>
|
||||
<div className={classes.innerSumDiv}>
|
||||
<Typography className={classes.col1}>YAML:</Typography>
|
||||
<Typography className={classes.col1}>
|
||||
{t('createWorkflow.verifyCommit.YAML')}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className={classes.yamlFlex}>
|
||||
{weights.length === 0 ? (
|
||||
<Typography>
|
||||
{' '}
|
||||
{t('createWorkflow.verifyCommit.errYaml')}{' '}
|
||||
<Typography className={classes.spacingHorizontal}>
|
||||
{t('createWorkflow.verifyCommit.errYaml')}
|
||||
</Typography>
|
||||
) : (
|
||||
<Typography>
|
||||
<b>{yamlStatus}</b>{' '}
|
||||
{t('createWorkflow.verifyCommit.youCanMoveOn')}
|
||||
<b>{yamlStatus}</b>
|
||||
<span className={classes.spacingHorizontal}>
|
||||
{t('createWorkflow.verifyCommit.youCanMoveOn')}
|
||||
</span>
|
||||
</Typography>
|
||||
)}
|
||||
<br />
|
||||
<ButtonFilled style={{ width: '60%' }} onClick={handleOpen}>
|
||||
<ButtonFilled
|
||||
className={classes.verifyYAMLButton}
|
||||
onClick={handleOpen}
|
||||
>
|
||||
{t('createWorkflow.verifyCommit.button.viewYaml')}
|
||||
</ButtonFilled>
|
||||
</div>
|
||||
|
|
@ -467,7 +456,9 @@ const VerifyCommit = forwardRef((_, ref) => {
|
|||
<br />
|
||||
<span className={classes.successful}>{workflow.name}</span>,
|
||||
<br />
|
||||
<strong>{t('workflowStepper.successful')}</strong>
|
||||
<span className={classes.bold}>
|
||||
{t('workflowStepper.successful')}
|
||||
</span>
|
||||
</div>
|
||||
<div className={classes.headWorkflow}>
|
||||
{t('workflowStepper.congratulationsSub1')} <br />{' '}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|||
justifyContent: 'space-between',
|
||||
},
|
||||
headerText: {
|
||||
fontWeight: 700,
|
||||
fontSize: '1.2rem',
|
||||
[theme.breakpoints.up('lg')]: {
|
||||
fontSize: '1.4rem',
|
||||
|
|
@ -64,8 +65,8 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|||
},
|
||||
sumText: {
|
||||
width: '100%',
|
||||
marginTop: theme.spacing(4.5),
|
||||
marginBottom: theme.spacing(3),
|
||||
margin: theme.spacing(2, 0),
|
||||
fontWeight: 700,
|
||||
fontSize: '1.2rem',
|
||||
[theme.breakpoints.up('lg')]: {
|
||||
fontSize: '1.4rem',
|
||||
|
|
@ -98,11 +99,12 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|||
marginLeft: theme.spacing(7),
|
||||
paddingTop: theme.spacing(0.5),
|
||||
},
|
||||
editButton1: {
|
||||
marginLeft: theme.spacing(1),
|
||||
editButton: {
|
||||
height: '1rem',
|
||||
},
|
||||
editbtn: {
|
||||
color: theme.palette.text.secondary,
|
||||
editIcon: {
|
||||
color: theme.palette.text.primary,
|
||||
height: '0.8rem',
|
||||
},
|
||||
link: {
|
||||
fontSize: '0.875rem',
|
||||
|
|
@ -135,8 +137,12 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|||
buttonOutlineText: {
|
||||
padding: theme.spacing(1.5),
|
||||
},
|
||||
spacingHorizontal: {
|
||||
margin: theme.spacing(0, 1),
|
||||
},
|
||||
errorText: {
|
||||
color: theme.palette.error.main,
|
||||
fontWeight: 700,
|
||||
fontSize: '1rem',
|
||||
marginLeft: theme.spacing(5),
|
||||
},
|
||||
|
|
@ -148,6 +154,7 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|||
progress: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
flexGrow: 1,
|
||||
marginLeft: theme.spacing(5),
|
||||
},
|
||||
|
|
@ -159,6 +166,9 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|||
marginTop: theme.spacing(-6),
|
||||
marginRight: theme.spacing(-2.5),
|
||||
},
|
||||
verifyYAMLButton: {
|
||||
width: '60%',
|
||||
},
|
||||
|
||||
// Modal
|
||||
modal: {
|
||||
|
|
@ -190,6 +200,10 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|||
successful: {
|
||||
fontSize: '2.2rem',
|
||||
fontWeight: 'bold',
|
||||
margin: theme.spacing(2, 0),
|
||||
},
|
||||
bold: {
|
||||
fontWeight: 700,
|
||||
},
|
||||
}));
|
||||
export default useStyles;
|
||||
|
|
|
|||
Loading…
Reference in New Issue