flowviz
Version:
A framework which provides seamless integration with other phylogenetic tools and frameworks, while allowing workflow scheduling and execution, through the Apache Airflow workflow system.
306 lines (280 loc) • 7.38 kB
JavaScript
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import ArrowForwardIcon from "@mui/icons-material/ArrowForward";
import BadgeOutlinedIcon from "@mui/icons-material/BadgeOutlined";
import CellTowerIcon from "@mui/icons-material/CellTower";
import FactCheckOutlinedIcon from "@mui/icons-material/FactCheckOutlined";
import SendIcon from "@mui/icons-material/Send";
import {
Button,
Container,
Divider,
Grid,
Step,
StepLabel,
Stepper,
Toolbar,
Typography,
} from "@mui/material";
import * as React from "react";
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import InfoBar from "../component/common/infoBar";
import Loading from "../component/common/loading";
import Access from "../component/postTool/accessFragment";
import General from "../component/postTool/generalFragment";
import Rules from "../component/postTool/rulesFragment";
export default function PostTool({ toolService }) {
const navigate = useNavigate();
const [activeStep, setActiveStep] = useState(0);
const [canAdvance, setCanAdvance] = useState(false);
const [configMethod, setConfigMethod] = useState("api");
const onConfigMethodUpdate = (method) => {
setConfigMethod(method);
};
const handleNext = () => {
if (!canAdvance) return;
setActiveStep((prevActiveStep) => prevActiveStep + 1);
setCanAdvance(false);
};
const handleBack = () => {
if (activeStep === 0) return;
setActiveStep((prevActiveStep) => prevActiveStep - 1);
};
const [general, setGeneral] = useState({
name: "",
description: "",
});
const onGeneralUpdate = (gen) => {
setGeneral(gen);
};
const [libraryAccess, setLibraryAccess] = useState({
address: "",
port: "",
isContainer: false,
dockerDaemon: "",
dockerImage: "",
dockerContainer: "",
dockerVolumes: [],
});
const onLibraryAccessUpdate = (acc) => {
setLibraryAccess(acc);
};
const [apiAccess, setApiAccess] = useState({
url: "",
apiKey: "",
});
const onApiAccessUpdate = (acc) => {
setApiAccess(acc);
};
const generateEndpoint = (index) => {
return {
name: `Endpoint ${index}`,
description: [],
method: "",
path: "",
headers: {},
body: {},
};
};
const [api, setApi] = useState([generateEndpoint(0)]);
const generateCommandGroup = (index) => {
return {
name: `Command Set ${index}`,
invocation: [],
order: index,
allowCommandRep: false,
commands: [
{
name: "Command 0",
invocation: [],
values: [],
subCommands: [],
subCommandSets: [],
},
],
};
};
const [library, setLibrary] = useState([generateCommandGroup(0)]);
const onApiUpdate = (updatedApi) => {
setApi(updatedApi);
};
const onLibraryUpdate = (updatedLib) => {
setLibrary(updatedLib);
};
const [submitting, setSubmitting] = useState(false);
const steps = [
{
label: "General",
icon: <BadgeOutlinedIcon />,
description: "Tool's relevant metadata.",
fragment: (
<General
onGeneralUpdate={onGeneralUpdate}
setCanAdvance={setCanAdvance}
general={general}
/>
),
},
{
label: "Access",
icon: <CellTowerIcon />,
description: "Where the tool is located and how it can be accessed.",
fragment: (
<Access
apiAccess={apiAccess}
libraryAccess={libraryAccess}
configMethod={configMethod}
onLibraryAccessUpdate={onLibraryAccessUpdate}
onApiAccessUpdate={onApiAccessUpdate}
setCanAdvance={setCanAdvance}
onMethodChoice={onConfigMethodUpdate}
/>
),
},
{
label: "Rules",
icon: <FactCheckOutlinedIcon />,
description:
"The rules and guidelines needed to configure and use the tool.",
fragment: (
<Rules
api={api}
library={library}
configMethod={configMethod}
onLibraryUpdate={onLibraryUpdate}
generateCommandGroup={generateCommandGroup}
onApiUpdate={onApiUpdate}
generateEndpoint={generateEndpoint}
/>
),
},
];
const BackButton = () => (
<Grid item xs={6}>
<Button
variant="outlined"
startIcon={<ArrowBackIcon />}
onClick={() => handleBack()}
disabled={activeStep === 0}
>
Previous
</Button>
</Grid>
);
const onError = (error) => (
<Container component="main" maxWidth="lg">
<Toolbar />
<>
<Typography variant="h2">Add tool</Typography>
<Divider />
<Toolbar />
</>
<>
<Stepper
activeStep={activeStep}
orientation="horizontal"
sx={{ mt: 2 }}
>
{steps.map((step) => (
<Step key={step.label}>
<StepLabel icon={step.icon}>{step.label}</StepLabel>
</Step>
))}
</Stepper>
{steps[activeStep].fragment}
<Grid container>
<BackButton />
<NextButton />
</Grid>
</>
<InfoBar type="error" text={error} />
</Container>
);
const onSuccess = (data) => {
navigate("/submission", {
state: {
text: `Successfully added ${general.name}!`,
resourcePageLabel: general.name,
resourcePageUrl: `/documentation/${general.name}`,
},
});
return <></>;
};
const OnSubmit = () => {
const body = JSON.stringify(
configMethod === "api"
? {
general: general,
access: { _type: configMethod, api: libraryAccess },
api: api,
}
: {
general: general,
access: { _type: configMethod, library: libraryAccess },
library: library,
}
);
return toolService.postTool(body, onError, onSuccess, <Loading />);
};
const NextButton = () => (
<Grid
item
xs={6}
direction="column"
sx={{
display: "flex",
"justify-content": "flex-end",
"align-items": "flex-end",
}}
>
{activeStep === steps.length - 1 ? (
<Button
variant="outlined"
endIcon={<SendIcon />}
onClick={() => setSubmitting(true)}
>
Finish
</Button>
) : (
<Button
variant="outlined"
endIcon={<ArrowForwardIcon />}
onClick={() => handleNext()}
disabled={!canAdvance}
>
Next
</Button>
)}
</Grid>
);
return submitting ? (
<OnSubmit />
) : (
<Container component="main" maxWidth="lg">
<Toolbar />
<>
<Typography variant="h2">Add tool</Typography>
<Divider />
<Toolbar />
</>
<>
<Stepper
activeStep={activeStep}
orientation="horizontal"
sx={{ mt: 2 }}
>
{steps.map((step) => (
<Step key={step.label}>
<StepLabel icon={step.icon}>{step.label}</StepLabel>
</Step>
))}
</Stepper>
{steps[activeStep].fragment}
<Grid container>
<BackButton />
<NextButton />
</Grid>
</>
</Container>
);
}