frontend/app/ProxyFramework/ProxyFrameworkAdd.tsx (170 lines of code) (raw):
import React, {useState} from "react";
import {RouteComponentProps} from "react-router";
import {
Button,
CircularProgress,
createStyles,
Divider,
Grid,
makeStyles, Snackbar,
Step,
StepLabel,
Stepper,
Theme
} from "@material-ui/core";
import AdminContainer from "../admin/AdminContainer";
import InitiateAddComponent from "./multistep/InitiateAddComponent";
import FindDeploymentComponent from "./multistep/FindDeploymentComponent";
import ConfirmationComponent from "./multistep/ConfirmationComponent";
import EnterDeploymentComponent from "./multistep/EnterDeploymentComponent";
import axios from "axios";
import MuiAlert from "@material-ui/lab/Alert";
import {ProxyFrameworkAutoConnection, ProxyFrameworkManualConnection} from "../types";
import ErrorViewComponent, {formatError} from "../common/ErrorViewComponent";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
width: '100%',
},
button: {
marginRight: theme.spacing(1),
},
instructions: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
},
buttonContainer: {
width: "100%",
marginLeft: "auto",
marginRight: "auto"
},
controlsDivider: {
marginTop: "1em",
marginBottom: "0.5em"
},
stepper: {
marginBottom: "1em"
},
throbber: {
marginRight: theme.spacing(3),
verticalAlign: "bottom"
}
}),
);
const steps = [
"Search",
"Select Deployment",
"Confirm"
];
const stackIdRegex = /^\w+:\w+:cloudformation:([\w\d\-]+):\d+:stack\/([^/]+)\/(.*)$/;
function breakdownStackId(stackId:string):ProxyFrameworkAutoConnection|null{
const results = stackIdRegex.exec(stackId);
if(!results){
console.error("Stack id ", stackId, " is not valid");
return null;
} else {
return {region: results[1], stackName: results[2], uuid: results[3]}
}
}
const ProxyFrameworkAdd:React.FC<RouteComponentProps> = (props) => {
const classes = useStyles();
const [activeStep, setActiveStep] = useState(0);
const [searchMode, setSearchMode] = useState<"search"|"entry">("search");
const [selectedDeployment, setSelectedDeployment] = useState<string|undefined>(undefined);
const [manualInput, setManualInput] = useState<ProxyFrameworkManualConnection|undefined>(undefined);
const [lastError, setLastError] = useState<string|undefined>(undefined);
const [connectionInProgress, setConnectionInProgress] = useState(false);
const [showingAlert, setShowingAlert] = useState(false);
const componentErrorOccurrec = (description:string)=>{
setLastError(description);
setShowingAlert(true);
}
/**
* returns the component for this step. Returns null if the step is out of range
*/
const contentBody = () => {
switch(activeStep) {
case 0:
return <InitiateAddComponent searchMode={searchMode} searchModeUpdated={(newMode)=>setSearchMode(newMode)}/>;
case 1:
return searchMode=="search" ? <FindDeploymentComponent
errorOccurred={componentErrorOccurrec}
deploymentSelected={(dpl:any)=>setSelectedDeployment(dpl)}
currentSelectedDeployment={selectedDeployment}
/> :
<EnterDeploymentComponent/>
case 2:
return <ConfirmationComponent searchMode={searchMode}
selectedDeployment={selectedDeployment}
manualInput={manualInput}
/>
default:
return null;
}
}
const maxSteps = steps.length;
const connectExistingStack = async ()=>{
const stackInfo = selectedDeployment ? breakdownStackId(selectedDeployment) : null;
if(stackInfo==null) {
setLastError("The given stack ID is not valid")
setShowingAlert(true);
} else {
const request = {
region: stackInfo.region,
stackName: stackInfo.stackName
};
setConnectionInProgress(true);
try {
const result = await axios.post("/api/proxyFramework/deployments", request)
setConnectionInProgress(false);
props.history.push("/admin/proxyFramework")
} catch(err) {
console.error(err);
setLastError(formatError(err, false));
setShowingAlert(true);
setConnectionInProgress(false);
}
}
}
const connectManual = async () => {
}
const performConnection = ()=>{
searchMode==="search" ? connectExistingStack() : connectManual();
}
const closeAlert = ()=>setShowingAlert(false);
return <AdminContainer {...props}>
<>
<Snackbar open={showingAlert} autoHideDuration={8000} onClose={closeAlert}>
<MuiAlert elevation={6} severity="error" onClose={closeAlert}>{lastError}</MuiAlert>
</Snackbar>
<Stepper activeStep={activeStep} className={classes.stepper}>
{
steps.map((label, idx)=>(
<Step key={label} completed={activeStep>idx}>
<StepLabel>{label}</StepLabel>
</Step>
))
}
</Stepper>
{
contentBody()
}
<Divider className={classes.controlsDivider}/>
<Grid container className={classes.buttonContainer} justify="space-between">
<Grid item>
<Button onClick={()=>setActiveStep(activeStep-1)} disabled={activeStep<1 || connectionInProgress} variant="outlined">Back</Button>
</Grid>
<Grid item>
{
activeStep<maxSteps-1 ?
<Button onClick={()=>setActiveStep(activeStep+1)} disabled={activeStep>=maxSteps-1} variant="contained">Next</Button> :
<>
{
connectionInProgress ? <CircularProgress className={classes.throbber}/> : null
}
<Button onClick={()=>performConnection()} disabled={connectionInProgress} variant="contained">Confirm</Button>
</>
}
</Grid>
</Grid>
</>
</AdminContainer>
}
export default ProxyFrameworkAdd;