298 lines
9.4 KiB
TypeScript
298 lines
9.4 KiB
TypeScript
import * as React from 'react';
|
|
import {makeStyles, Theme} from '@material-ui/core/styles';
|
|
import Tabs from '@material-ui/core/Tabs';
|
|
import Tab from '@material-ui/core/Tab';
|
|
import ManifestForm from './ManifestForm'
|
|
import DatabaseForm from './database/DatabaseForm'
|
|
import ElasticCacheForm from './elasticcache/ElasticCacheForm'
|
|
import * as _m from '../models/Manifest'
|
|
import { Form, Formik, FormikValues, useFormikContext, validateYupSchema, yupToFormErrors } from 'formik'
|
|
import {manifestValidationSchema} from "../models/ManifestValidationSchema";
|
|
import {tabA11yProps, TabPanel} from './FormUtil';
|
|
import Configuration from './configuration/ConfigurationForm';
|
|
import S3BucketForm from './s3bucket/S3BucketForm';
|
|
import { Button, Divider, Snackbar, TextField, Typography } from '@material-ui/core'
|
|
import {Alert} from "@material-ui/lab";
|
|
import DeploymentForm from './deployment/DeploymentForm';
|
|
import AWSAccessForm from './awsaccess/AWSAccessForm';
|
|
import ServiceMonitorForm from "./servicemonitor/ServiceMonitorForm";
|
|
import DocumentDbForm from "./documentdb/DocumentDbForm";
|
|
import ElasticSearchForm from "./elasticsearch/ElasticSearchForm";
|
|
import { Dispatch, SetStateAction } from 'react';
|
|
import DynamoDbForm from "./dynamodb/DynamoDbForm";
|
|
import {store} from "../store/store";
|
|
import { getBreachedValues } from '../helper/ChangeRequest'
|
|
import Popup from '../components/common/Popup'
|
|
|
|
const useStyles = makeStyles((theme: Theme) => ({
|
|
root: {
|
|
flexGrow: 1,
|
|
backgroundColor: theme.palette.background.paper,
|
|
display: 'flex',
|
|
},
|
|
tabs: {
|
|
borderRight: `1px solid ${theme.palette.divider}`,
|
|
}
|
|
}));
|
|
|
|
interface BaseFormProps {
|
|
initData: any,
|
|
setFormInitData: Dispatch<SetStateAction<object>>,
|
|
onSubmit: (values: any, afterSubmitCallback: Function) => void,
|
|
manifestLimits: any,
|
|
hasPendingCr: boolean,
|
|
crDescription: string,
|
|
setCrDescription: Dispatch<SetStateAction<string>>,
|
|
}
|
|
|
|
interface FormTab {
|
|
name: string
|
|
displayName: any
|
|
component: React.ReactNode
|
|
errorCheckFn: Function
|
|
}
|
|
|
|
const tabList: Array<FormTab> = [
|
|
{
|
|
name: 'deployment',
|
|
displayName: 'deployment',
|
|
component: <DeploymentForm />,
|
|
errorCheckFn: _m.hasDeployment,
|
|
},
|
|
{
|
|
name: 'database',
|
|
displayName: 'database',
|
|
component: <DatabaseForm />,
|
|
errorCheckFn: _m.hasDatabase
|
|
},
|
|
{
|
|
name: 'docdb',
|
|
displayName: 'document db',
|
|
component: <DocumentDbForm />,
|
|
errorCheckFn: _m.hasDocumentDb
|
|
},
|
|
{
|
|
name: 'elastic Cache',
|
|
displayName: 'elastic Cache',
|
|
component: <ElasticCacheForm />,
|
|
errorCheckFn: _m.hasElasticCache
|
|
},
|
|
{
|
|
name: 's3 bucket',
|
|
displayName: 's3 bucket',
|
|
component: <S3BucketForm />,
|
|
errorCheckFn: _m.hasS3Bucket
|
|
},
|
|
{
|
|
name: 'configuration',
|
|
displayName: 'configuration',
|
|
component: <Configuration />,
|
|
errorCheckFn: _m.hasConfiguration
|
|
},
|
|
{
|
|
name: 'aws access',
|
|
displayName: 'aws access',
|
|
component: <AWSAccessForm />,
|
|
errorCheckFn: _m.hasAwsAccess
|
|
},
|
|
{
|
|
name: 'Service Monitor',
|
|
displayName: 'service monitor',
|
|
component: <ServiceMonitorForm />,
|
|
errorCheckFn: _m.hasServiceMonitor
|
|
},
|
|
{
|
|
name: 'Elastic Search',
|
|
displayName: 'elastic search',
|
|
component: <ElasticSearchForm />,
|
|
errorCheckFn: _m.hasElasticSearch
|
|
},
|
|
{
|
|
name: 'DynamoDB',
|
|
displayName: 'DynamoDB',
|
|
component: <DynamoDbForm />,
|
|
errorCheckFn: _m.hasDynamoDb
|
|
},
|
|
]
|
|
|
|
const tabHasErrors = (tab: FormTab, errors: any) => {
|
|
return tab.errorCheckFn(errors)
|
|
}
|
|
|
|
const ErrorSnackBar = (props: { errors: any }) => {
|
|
const { errors } = props
|
|
const errorData = Object.entries(errors).slice(0, 4)
|
|
return (
|
|
<Snackbar open={errorData.length > 0} autoHideDuration={5000}>
|
|
<Alert severity="error">
|
|
{errorData.map((data, idx) => {
|
|
return (
|
|
<Typography
|
|
variant="subtitle2"
|
|
color="secondary"
|
|
key={idx} >
|
|
{ JSON.stringify(data)}
|
|
</Typography>
|
|
);
|
|
})}
|
|
</Alert>
|
|
</Snackbar>)
|
|
}
|
|
|
|
const BaseForm = (props: BaseFormProps) => {
|
|
const classes = useStyles();
|
|
const defaultSubmitButtonLabel = "Submit";
|
|
const submitChangeRequestLabel = "Submit Change Request"
|
|
const defaultSubmitButtonStyle = { alignItems: "flex-end", display: "flex", justifyContent: "flex-end", float: "right" };
|
|
const submitChangeRequestStyle = {...defaultSubmitButtonStyle, backgroundColor: '#fd7700'};
|
|
const { setCrDescription } = props
|
|
|
|
const [currentTab, setCurrentTab] = React.useState(0);
|
|
const { initData, onSubmit } = props
|
|
const { manifestLimits, hasPendingCr } = props;
|
|
const setFormInitData = props.setFormInitData;
|
|
const [manifestVersion, setManifestVersion] = React.useState()
|
|
const [versionListData, setVersionListData] = React.useState<any>([]);
|
|
const [isSubmitButtonDisabled, disableSubmitButton] = React.useState(false);
|
|
const { state } = React.useContext(store);
|
|
const [rolloutPopupOpen, setRolloutPopupOpen] = React.useState(false);
|
|
const [isCrRequired, setCrRequired] = React.useState(false);
|
|
|
|
const isDisabled = () => {
|
|
if (!isSubmitButtonDisabled &&
|
|
(manifestVersion === Math.max(...versionListData)
|
|
|| manifestVersion === undefined
|
|
|| manifestVersion === 'None')) {
|
|
disableSubmitButton(false)
|
|
} else {
|
|
disableSubmitButton(true)
|
|
}
|
|
return isSubmitButtonDisabled
|
|
}
|
|
|
|
const handleTabChange = (e: React.ChangeEvent<{}>, newValue: number) => {
|
|
setCurrentTab(newValue);
|
|
};
|
|
|
|
const submitButton = (
|
|
style: {},
|
|
label: string,
|
|
type: "submit" | "button" = "submit",
|
|
onClick: () => void = () => {}
|
|
) => (
|
|
<Button
|
|
disabled={isDisabled()}
|
|
color="secondary"
|
|
type={type}
|
|
variant="contained"
|
|
size="large"
|
|
style={style}
|
|
onClick={onClick}
|
|
>
|
|
{label}
|
|
</Button>
|
|
);
|
|
|
|
const submitCrButton = (
|
|
type: "submit" | "button" = "button",
|
|
style = submitChangeRequestStyle,
|
|
onClick: () => void = () => {}
|
|
) => submitButton(submitChangeRequestStyle, submitChangeRequestLabel, type, onClick);
|
|
|
|
const submitManifestButton = () => submitButton(defaultSubmitButtonStyle, defaultSubmitButtonLabel)
|
|
|
|
const renderSubmitButton = () => {
|
|
if (isCrRequired) {
|
|
return submitCrButton("button", submitChangeRequestStyle, () => setRolloutPopupOpen(true));
|
|
} else {
|
|
return submitManifestButton();
|
|
}
|
|
};
|
|
|
|
const handleSubmit = (values: FormikValues) => {
|
|
disableSubmitButton(true);
|
|
onSubmit(values, () => {
|
|
setCrRequired(false);
|
|
disableSubmitButton(false);
|
|
});
|
|
}
|
|
|
|
return (
|
|
<div style={{ marginBottom: 20 }}>
|
|
<Formik
|
|
enableReinitialize
|
|
initialValues={initData}
|
|
validate={async value => {
|
|
const breachedValues = getBreachedValues(manifestLimits[`${value.environment}`], value, state.preChangeManifest)
|
|
setCrRequired(breachedValues.length !== 0);
|
|
|
|
try {
|
|
await validateYupSchema(value, manifestValidationSchema, false, value);
|
|
} catch (err) {
|
|
return yupToFormErrors(err); //for rendering validation errors
|
|
}
|
|
return {};
|
|
}}
|
|
validateOnChange={false}
|
|
onSubmit={values => handleSubmit(values)}
|
|
>
|
|
{
|
|
props => (
|
|
<Form>
|
|
<ManifestForm manifest={props.values}
|
|
setFormInitData={setFormInitData}
|
|
setManifestVersion={setManifestVersion}
|
|
setVersionListData={setVersionListData}
|
|
versionListData={versionListData}
|
|
hasPendingCr={hasPendingCr}/>
|
|
<Divider />
|
|
<div className={classes.root}>
|
|
<Tabs orientation="vertical" variant="scrollable" value={currentTab} onChange={handleTabChange}
|
|
aria-label="Vertical tabs " className={classes.tabs} >
|
|
{
|
|
tabList.map((tab, index) => (
|
|
<Tab key={index} style={{ backgroundColor: tabHasErrors(tab, props.errors) ? '#ffb3bf' : 'white' }}
|
|
label={tab.displayName} {...tabA11yProps(index, 'vertical')} />
|
|
))
|
|
}
|
|
</Tabs>
|
|
{
|
|
tabList.map((tab, index) => (
|
|
<TabPanel key={index} value={currentTab} index={index} tabtype='vertical'>
|
|
{tab.component}
|
|
</TabPanel>
|
|
))
|
|
}
|
|
</div>
|
|
{renderSubmitButton()}
|
|
<Popup
|
|
open={rolloutPopupOpen}
|
|
onClose={() => setRolloutPopupOpen(false)}
|
|
title="Submit change request"
|
|
>
|
|
<TextField
|
|
style={{ textAlign: "center" }}
|
|
type="text"
|
|
fullWidth
|
|
placeholder="Please describe changes in the CR."
|
|
variant="outlined"
|
|
onBlur={({ target: { value } }) => setCrDescription(value)}
|
|
multiline
|
|
/>
|
|
{submitCrButton("submit", {...submitChangeRequestStyle},
|
|
() => {
|
|
handleSubmit(props.values);
|
|
setRolloutPopupOpen(false);
|
|
})}
|
|
</Popup>
|
|
{/*<Debug {...props} />*/}
|
|
<ErrorSnackBar errors={props.errors} />
|
|
</Form>
|
|
)}
|
|
</Formik>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default BaseForm
|