diff --git a/litmus-portal/frontend/public/locales/en/translation.yaml b/litmus-portal/frontend/public/locales/en/translation.yaml index cb506fc92..944bb1ad9 100644 --- a/litmus-portal/frontend/public/locales/en/translation.yaml +++ b/litmus-portal/frontend/public/locales/en/translation.yaml @@ -751,11 +751,15 @@ createWorkflow: edit: Edit workflow name cancel: Cancel save: Save + noMyHub: No MyHub selected + selectHub: Select a hub and continue with a pre-defined workflow + noPredefined: No Pre-defined experiments present + addPredefined: Add a pre-defined workflow to continue scheduling. reliabilityScore: header: Adjust the weights of the experiments in the workflow info: You have selected infoNext: tests in the “Kubernetes conformance test” workflow. Successful outcome of each test carries a certain weight. We have pre-selected weights for each test for you. However, you may review and modify the weightage against. - infoNextStrong: These weights are used to calculate the resiliency score at the end of test. + infoNextStrong: These weights are used to calculate the resiliency score at the end of test. testInfo: Compare the importance of the items above and launch a demo version of Kubernetes conformance test to see how it works. button: demo: Demo Launch diff --git a/litmus-portal/frontend/src/views/CreateWorkflow/ChooseWorkflow/choosePreDefinedExperiments.tsx b/litmus-portal/frontend/src/views/CreateWorkflow/ChooseWorkflow/choosePreDefinedExperiments.tsx index 21064bc15..e7d4aee48 100644 --- a/litmus-portal/frontend/src/views/CreateWorkflow/ChooseWorkflow/choosePreDefinedExperiments.tsx +++ b/litmus-portal/frontend/src/views/CreateWorkflow/ChooseWorkflow/choosePreDefinedExperiments.tsx @@ -1,15 +1,24 @@ +import { useLazyQuery, useQuery } from '@apollo/client'; import { AccordionDetails, + FormControl, + InputLabel, + MenuItem, RadioGroup, + Select, Typography, useTheme, } from '@material-ui/core'; import { LitmusCard, RadioButton, Search } from 'litmus-ui'; import localforage from 'localforage'; import React, { useEffect, useState } from 'react'; -import data from '../../../components/PredifinedWorkflows/data'; -import { preDefinedWorkflowData } from '../../../models/predefinedWorkflow'; -import useStyles from './styles'; +import { useTranslation } from 'react-i18next'; +import config from '../../../config'; +import { GET_HUB_STATUS, GET_PREDEFINED_WORKFLOW_LIST } from '../../../graphql'; +import { MyHubDetail } from '../../../models/graphql/user'; +import { HubStatus } from '../../../models/redux/myhub'; +import { getProjectID } from '../../../utils/getSearchParams'; +import useStyles, { MenuProps } from './styles'; interface ChooseWorkflowRadio { selected: string; @@ -17,6 +26,7 @@ interface ChooseWorkflowRadio { } const ChoosePreDefinedExperiments = () => { + const { t } = useTranslation(); const classes = useStyles(); const { palette } = useTheme(); @@ -24,7 +34,35 @@ const ChoosePreDefinedExperiments = () => { const [search, setSearch] = useState(null); const [selected, setSelected] = useState(''); - // Methods + const selectedProjectID = getProjectID(); + const [selectedHub, setSelectedHub] = useState(''); + const [availableHubs, setAvailableHubs] = useState([]); + const [workflowList, setWorkflowlist] = useState([]); + + // Get all MyHubs with status + const { data, loading } = useQuery(GET_HUB_STATUS, { + variables: { data: selectedProjectID }, + fetchPolicy: 'cache-and-network', + }); + + /** + * Query to get the list of Pre-defined workflows + */ + const [getPredefinedWorkflow] = useLazyQuery(GET_PREDEFINED_WORKFLOW_LIST, { + fetchPolicy: 'network-only', + onCompleted: (data) => { + if (data.GetPredefinedWorkflowList !== undefined) { + setWorkflowlist(data.GetPredefinedWorkflowList); + } + }, + onError: () => { + setWorkflowlist([]); + }, + }); + + /** + * Function to handle changes in Radio Buttons + */ const handleChange = (event: React.ChangeEvent) => { setSelected(event.target.value); const selection: ChooseWorkflowRadio = { @@ -35,13 +73,30 @@ const ChoosePreDefinedExperiments = () => { localforage.setItem('hasSetWorkflowData', false); }; - const filteredPreDefinedWorkflows = data.filter( - (w: preDefinedWorkflowData) => { - if (search === null) return w; - if (w.title.toLowerCase().includes(search.toLowerCase())) return w; - return null; - } - ); + const filteredPreDefinedWorkflows = workflowList.filter((w: string) => { + if (search === null) return w; + if (w.toLowerCase().includes(search.toLowerCase())) return w; + return null; + }); + + /** + * Function to handle change in MyHub dropdown + */ + const handleMyHubChange = ( + event: React.ChangeEvent<{ + name?: string | undefined; + value: unknown; + }> + ) => { + setSelectedHub(event.target.value as string); + getPredefinedWorkflow({ + variables: { + hubname: event.target.value as string, + projectid: selectedProjectID, + }, + }); + localforage.setItem('selectedHub', event.target.value as string); + }; // Selects Option A -> Sub Experiment Options which was already selected by the user useEffect(() => { @@ -54,60 +109,130 @@ const ChoosePreDefinedExperiments = () => { ); }, []); + /** + * UseEffect to check if Chaos Hub exists and if exists + * fetch the pre-defined workflows + */ + useEffect(() => { + if (data?.getHubStatus !== undefined) { + if (data.getHubStatus.length) { + setAvailableHubs([...data.getHubStatus]); + } + data.getHubStatus.forEach((hubData) => { + if (hubData.HubName.toLowerCase() === 'chaos hub') { + setSelectedHub('Chaos Hub'); + getPredefinedWorkflow({ + variables: { + hubname: 'Chaos Hub', + projectid: selectedProjectID, + }, + }); + } + }); + } + }, [loading]); + return ( {/* Wrapping content inside the Accordion to take full width */}
- setSearch(event.target.value)} - /> +
+
+ + + {t('createWorkflow.chooseWorkflow.selectMyHub')} + + + +
+
+ setSearch(event.target.value)} + /> +
+
{/* Leaving some space between the search and pre-defined workflow cards */}
-
{/* List of Pre-defined workflows */}
- - {filteredPreDefinedWorkflows.map((workflowData) => ( - - - {/* Wrap the entire body with 100% width to divide into 40-60 */} -
- {/* Left Div => Icon + Name of Workflow */} -
- Experiment Icon - - {workflowData.title} - + {selectedHub === '' ? ( +
+ + {t('createWorkflow.chooseWorkflow.noMyHub')} + + + {t('createWorkflow.chooseWorkflow.selectHub')} + +
+ ) : workflowList.length === 0 ? ( +
+ + + {t('createWorkflow.chooseWorkflow.noPredefined')} + + + + {t('createWorkflow.chooseWorkflow.addPredefined')} + +
+ ) : ( + + {filteredPreDefinedWorkflows?.map((wfData) => ( + + + {/* Wrap the entire body with 100% width to divide into 40-60 */} +
+ {/* Left Div => Icon + Name of Workflow */} +
+ Experiment Icon + + {wfData} + +
- {/* Right Div => Description of Workflow */} -
- {workflowData.description} -
-
- - - ))} - + + + ))} + + )}
{/* Bottom Blur */}
diff --git a/litmus-portal/frontend/src/views/CreateWorkflow/ChooseWorkflow/styles.ts b/litmus-portal/frontend/src/views/CreateWorkflow/ChooseWorkflow/styles.ts index 559904999..d31d3cbe3 100644 --- a/litmus-portal/frontend/src/views/CreateWorkflow/ChooseWorkflow/styles.ts +++ b/litmus-portal/frontend/src/views/CreateWorkflow/ChooseWorkflow/styles.ts @@ -40,6 +40,9 @@ const useStyles = makeStyles((theme: Theme) => ({ }, }, + searchDiv: { + margin: theme.spacing(2, 0, 0, 2.25), + }, // Divider divider: { border: 'none', @@ -72,8 +75,9 @@ const useStyles = makeStyles((theme: Theme) => ({ // Accordion Expanded Body [Content] predefinedWorkflowDiv: { - height: '15rem', + maxHeight: '15rem', overflowY: 'scroll', + padding: theme.spacing(3, 0, 3, 0), }, MuiAccordionroot: { '&.MuiAccordion-root:before': { @@ -97,11 +101,6 @@ const useStyles = makeStyles((theme: Theme) => ({ display: 'flex', marginLeft: theme.spacing(2), }, - - '& #right-div': { - width: '20rem', - display: 'flex', - }, }, existingWorkflowCard: { alignItems: 'center', @@ -149,7 +148,7 @@ const useStyles = makeStyles((theme: Theme) => ({ height: '3rem', }, predefinedWorkflowName: { - marginLeft: theme.spacing(2), + marginLeft: theme.spacing(4), marginTop: theme.spacing(1.5), }, blur: { @@ -158,7 +157,7 @@ const useStyles = makeStyles((theme: Theme) => ({ position: 'absolute', bottom: 0, background: theme.palette.background.paper, - opacity: '0.8', + opacity: '0.5', filter: 'blur(1rem)', }, @@ -233,6 +232,11 @@ const useStyles = makeStyles((theme: Theme) => ({ margin: theme.spacing(2.5), alignItems: 'center', }, + inputMyHubDiv: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + }, formControl: { minWidth: '9rem', marginLeft: theme.spacing(1), diff --git a/litmus-portal/frontend/src/views/CreateWorkflow/WorkflowSettings/index.tsx b/litmus-portal/frontend/src/views/CreateWorkflow/WorkflowSettings/index.tsx index e134d65af..9ccc0c12a 100644 --- a/litmus-portal/frontend/src/views/CreateWorkflow/WorkflowSettings/index.tsx +++ b/litmus-portal/frontend/src/views/CreateWorkflow/WorkflowSettings/index.tsx @@ -1,3 +1,4 @@ +import { useLazyQuery } from '@apollo/client'; import { Avatar, Typography } from '@material-ui/core'; import { ButtonOutlined, InputField, Modal } from 'litmus-ui'; import localforage from 'localforage'; @@ -9,21 +10,24 @@ import React, { } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; -import data from '../../../components/PredifinedWorkflows/data'; +import config from '../../../config'; +import { GET_EXPERIMENT_DATA } from '../../../graphql'; import { ChooseWorkflowRadio } from '../../../models/localforage/radioButton'; import { WorkflowDetailsProps } from '../../../models/localforage/workflow'; +import { ExperimentDetail } from '../../../models/redux/myhub'; import useActions from '../../../redux/actions'; import * as AlertActions from '../../../redux/actions/alert'; import * as WorkflowActions from '../../../redux/actions/workflow'; import { RootState } from '../../../redux/reducers'; import capitalize from '../../../utils/capitalize'; +import { getProjectID } from '../../../utils/getSearchParams'; import { validateWorkflowName } from '../../../utils/validate'; import useStyles from './styles'; const WorkflowSettings = forwardRef((_, ref) => { const classes = useStyles(); const [avatarModal, setAvatarModal] = useState(false); - + const projectID = getProjectID(); // Workflow States const [name, setName] = useState(''); const [descriptionHeader, setDescriptionHeader] = useState( @@ -38,6 +42,24 @@ const WorkflowSettings = forwardRef((_, ref) => { const { manifest } = useSelector( (state: RootState) => state.workflowManifest ); + const [hubName, setHubName] = useState(''); + // Query to get charts of selected MyHub + const [getWorkflowDetails] = useLazyQuery( + GET_EXPERIMENT_DATA, + { + fetchPolicy: 'cache-and-network', + onCompleted: (data) => { + if (data.getHubExperiment !== undefined) { + setName(data.getHubExperiment.Metadata.Name.toLowerCase()); + setDescription(data.getHubExperiment.Spec.CategoryDescription); + setIcon( + `${config.grahqlEndpoint}/icon/${projectID}/${hubName}/predefined/${data.getHubExperiment.Metadata.Name}.png` + ); + setCRDLink(data.getHubExperiment.Metadata.Name); + } + }, + } + ); const { t } = useTranslation(); const alert = useActions(AlertActions); @@ -81,16 +103,20 @@ const WorkflowSettings = forwardRef((_, ref) => { const initializeWithDefault = () => { localforage.getItem('selectedScheduleOption').then((value) => { + // Map over the list of predefined workflows and extract the name and detail if ((value as ChooseWorkflowRadio).selected === 'A') { - // Map over the list of predefined workflows and extract the name and detail - data.map((w) => { - if (w.workflowID.toString() === (value as ChooseWorkflowRadio).id) { - setName(w.title); - setDescription(w.details); - setIcon(w.urlToIcon); - setCRDLink(w.experimentPath); - } - return null; + localforage.getItem('selectedHub').then((hub) => { + setHubName(hub as string); + getWorkflowDetails({ + variables: { + data: { + HubName: hub as string, + ProjectID: projectID, + ChartName: 'predefined', + ExperimentName: (value as ChooseWorkflowRadio).id, + }, + }, + }); }); } if ((value as ChooseWorkflowRadio).selected === 'B') { @@ -123,6 +149,7 @@ const WorkflowSettings = forwardRef((_, ref) => { * and call checkForStoredData() * else it will initializeWithDefault() */ + localforage.getItem('hasSetWorkflowData').then((isDataPresent) => { return isDataPresent ? checkForStoredData() : initializeWithDefault(); });