frontend/app/ProxyFramework/multistep/FindDeploymentComponent.tsx (134 lines of code) (raw):

import React, {useState, useEffect} from "react"; import {Grid, LinearProgress, makeStyles, Paper, Radio, Theme, Typography} from "@material-ui/core"; import LoadingThrobber from "../../common/LoadingThrobber"; import { ProxyFrameworkDeploymentRow, ProxyFrameworkSearchResponse, ProxyFrameworkStackRow, RegionScanError } from "../../types"; import {ColDef, DataGrid} from "@material-ui/data-grid"; import axios from "axios"; import ErrorViewComponent, {formatError} from "../../common/ErrorViewComponent"; interface FindDeploymentComponentProps { deploymentSelected: (deploymentId:string)=>void; currentSelectedDeployment?: string; errorOccurred: (description:string)=>void; } const useStyles = makeStyles((theme:Theme)=>({ rightAlignedGridbox: { marginLeft: "auto" }, errorText: { color: theme.palette.error.dark }, emphasis: { fontWeight: "bold" }, tableContainer: { height: "50vh", overflow: "hidden" } })) function makeSearchTableColumns( getSelectedValue: ()=>string|undefined, radioSelectorChanged: (newValue:string)=>void ):ColDef[] { return [ { field: "selector", headerName: "Select", renderCell: (params) => ( <Radio checked={params.getValue("stackId")===getSelectedValue()} onClick={()=>radioSelectorChanged(params.getValue("stackId") as string)} /> ) }, { field: "stackName", headerName: "Stack Name", width: 300 }, { field: "templateDescription", headerName: "Description", width: 400 }, { field: "region", headerName: "Region", width: 200 }, { field: "status", headerName: "Status", width: 200 }, { field: "creationTime", headerName: "Created", width: 300 } ] } const FindDeploymentComponent:React.FC<FindDeploymentComponentProps> = (props) => { const classes = useStyles(); const [loading, setLoading] = useState(false); const [regionErrors, setRegionErrors] = useState<RegionScanError[]>([]); const [foundDeployments, setFoundDeployments] = useState<ProxyFrameworkStackRow[]>([]); const getSelectedValue = () => { return props.currentSelectedDeployment; } const radioSelectorChanged = (newValue:string) => { console.log("You selected the deployment ", newValue); props.deploymentSelected(newValue); } const searchTableColumns = makeSearchTableColumns(getSelectedValue, radioSelectorChanged); /** * load in the list of deployments at mount. If we unmount while the load is still in progress, cancel it. */ useEffect(()=>{ const cancelTokenSource = axios.CancelToken.source(); const loadData = async ()=> { const cancelToken = cancelTokenSource.token; try { const result = await axios.get<ProxyFrameworkSearchResponse>("/api/proxyFramework/deploymentScan", {cancelToken: cancelToken}); //the search response contains a list of lists; the outer list for each region and the inner for each deployment //in said region. We need to flatten that down into a single list here. const finalDeployments = result.data.success.flat().map((entry,idx)=>Object.assign({id: idx}, entry)); setFoundDeployments(finalDeployments); setRegionErrors(result.data.failure.map((entry)=>({region: entry[0],error: entry[1]}))); setLoading(false); } catch(err) { console.error("Could not load in deployments: ", err); if(err.hasOwnProperty("message") && err.message==="cancelled") { console.log("proxy framework search was aborted") } else { setLoading(false); props.errorOccurred(formatError(err, false)); } } } setLoading(true); loadData(); return ()=>{ cancelTokenSource.cancel("cancelled"); } }, []); return <div> <Grid container> <Grid item> <Typography variant="h5">Search for deployment</Typography> </Grid> {loading ? <Grid item className={classes.rightAlignedGridbox}> <LinearProgress/> <Typography style={{marginLeft:"1em"}}>Searching...</Typography> </Grid> : null } </Grid> <Paper elevation={3} className={classes.tableContainer}> <DataGrid columns={searchTableColumns} rows={foundDeployments}/> </Paper> { regionErrors.length>0 ? <div style={{display: regionErrors.length>0 ? "block" : "none"}}> <Typography variant="h6">The following regions failed: </Typography> <ul> { regionErrors.map(entry=> <li key={entry.region}> <Typography className={classes.emphasis}>{entry.region}</Typography> <Typography className={classes.errorText}>{entry.error}</Typography> </li> ) } </ul> </div> : null } </div> } export default FindDeploymentComponent;