add & configure machine: added formular integration

This commit is contained in:
Johannes Kirschbauer 2023-11-11 10:08:39 +01:00
parent 15a136b266
commit 97b8c7a701
Signed by: hsjobeki
GPG Key ID: F62ED8B8BF204685
7 changed files with 141 additions and 49 deletions

View File

@ -1,6 +1,13 @@
import { setMachineSchema } from "@/api/machine/machine";
import { useListClanModules } from "@/api/modules/modules";
import { Alert, AlertTitle, FormHelperText, Typography } from "@mui/material";
import {
Alert,
AlertTitle,
Divider,
FormHelperText,
Input,
Typography,
} from "@mui/material";
import Box from "@mui/material/Box";
import Chip from "@mui/material/Chip";
import FormControl from "@mui/material/FormControl";
@ -8,9 +15,14 @@ import InputLabel from "@mui/material/InputLabel";
import MenuItem from "@mui/material/MenuItem";
import OutlinedInput from "@mui/material/OutlinedInput";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import { useEffect } from "react";
import { Dispatch, SetStateAction, useEffect, useState } from "react";
import { Controller } from "react-hook-form";
import { toast } from "react-hot-toast";
import { CreateMachineForm, FormStepContentProps } from "./interfaces";
import {
CreateMachineForm,
FormHooks,
FormStepContentProps,
} from "./interfaces";
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
@ -23,51 +35,114 @@ const MenuProps = {
},
};
interface IupdateSchema {
clanName: string;
modules: string[];
formHooks: FormHooks;
setSchemaError: Dispatch<SetStateAction<null | string>>;
}
const updateSchema = ({
clanName,
modules,
formHooks,
setSchemaError,
}: IupdateSchema) => {
formHooks.setValue("isSchemaLoading", true);
setMachineSchema(clanName, "example_machine", {
clanImports: modules,
})
.then((response) => {
if (response.statusText == "OK") {
formHooks.setValue("schema", response.data.schema);
setSchemaError(null);
}
})
.catch((error) => {
formHooks.setValue("schema", {});
console.error({ error });
setSchemaError(error.message);
toast.error(`${error.message}`);
})
.finally(() => {
formHooks.setValue("isSchemaLoading", false);
});
};
type ClanModulesProps = FormStepContentProps;
const SchemaSuccessMsg = () => (
<Alert severity="success">
<AlertTitle>Success</AlertTitle>
<Typography variant="subtitle2" sx={{ mt: 2 }}>
Machine configuration schema successfully created.
</Typography>
</Alert>
);
interface SchemaErrorMsgProps {
msg: string | null;
}
const SchemaErrorMsg = (props: SchemaErrorMsgProps) => (
<Alert severity="error">
<AlertTitle>Error</AlertTitle>
<Typography variant="subtitle1" sx={{ mt: 2 }}>
Machine configuration schema could not be created.
</Typography>
<Typography variant="subtitle2" sx={{ mt: 2 }}>
{props.msg}
</Typography>
</Alert>
);
export default function ClanModules(props: ClanModulesProps) {
const { clanName, formHooks } = props;
const { data, isLoading } = useListClanModules(clanName);
const [schemaError, setSchemaError] = useState<string | null>(null);
const selectedModules = formHooks.watch("modules");
useEffect(() => {
setMachineSchema(clanName, "example_machine", {
imports: [],
}).then((response) => {
if (response.statusText == "OK") {
formHooks.setValue("schema", response.data.schema);
}
updateSchema({
clanName,
modules: formHooks.watch("modules"),
formHooks,
setSchemaError,
});
formHooks.setValue("modules", []);
// Only re-run if global clanName has changed
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [clanName]);
const isSchemaLoading = formHooks.watch("isSchemaLoading");
const handleChange = (
event: SelectChangeEvent<CreateMachineForm["modules"]>,
event: SelectChangeEvent<CreateMachineForm["modules"]>
) => {
const {
target: { value },
} = event;
const newValue = typeof value === "string" ? value.split(",") : value;
formHooks.setValue("modules", newValue);
setMachineSchema(clanName, "example_machine", {
imports: newValue,
})
.then((response) => {
if (response.statusText == "OK") {
formHooks.setValue("schema", response.data.schema);
}
})
.catch((error) => {
formHooks.setValue("schema", {});
console.error({ error });
toast.error(`${error.message}`);
});
updateSchema({
clanName,
modules: newValue,
formHooks,
setSchemaError,
});
};
return (
<div className="my-4 flex w-full flex-col justify-center px-2">
<FormControl sx={{ my: 4 }} disabled={isLoading} required>
<InputLabel>Machine name</InputLabel>
<Controller
name="name"
control={formHooks.control}
render={({ field }) => <Input {...field} />}
/>
<FormHelperText>Choose a unique name for the machine.</FormHelperText>
</FormControl>
<Alert severity="info">
<AlertTitle>Info</AlertTitle>
Optionally select some modules {" "}
@ -106,6 +181,14 @@ export default function ClanModules(props: ClanModulesProps) {
(Optional) Select clan modules to be added.
</FormHelperText>
</FormControl>
{!isSchemaLoading && <Divider flexItem sx={{ my: 4 }} />}
{!isSchemaLoading &&
(!schemaError ? (
<SchemaSuccessMsg />
) : (
<SchemaErrorMsg msg={schemaError} />
))}
</div>
);
}

View File

@ -44,7 +44,7 @@ export function CustomConfig(props: FormStepContentProps) {
}
return acc;
}, {}),
[schema],
[schema]
);
return (
@ -110,11 +110,11 @@ function PureCustomConfig(props: PureCustomConfigProps) {
message: "invalid config",
});
toast.error(
"Configuration is invalid. Please check the highlighted fields for details.",
"Configuration is invalid. Please check the highlighted fields for details."
);
} else {
formHooks.clearErrors("config");
toast.success("Config seems valid");
toast.success("Configuration is valid");
}
};
@ -139,7 +139,7 @@ function PureCustomConfig(props: PureCustomConfigProps) {
variant="outlined"
color="secondary"
>
Validate
Validate configuration
</Button>
</div>
),

View File

@ -1,6 +1,8 @@
import { createMachine, setMachineConfig } from "@/api/machine/machine";
import {
Box,
Button,
CircularProgress,
LinearProgress,
MobileStepper,
Step,
@ -11,6 +13,7 @@ import {
} from "@mui/material";
import React, { useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "react-hot-toast";
import { useAppState } from "../hooks/useAppContext";
import ClanModules from "./clanModules";
import { CustomConfig } from "./customConfig";
@ -22,22 +25,19 @@ export function CreateMachineForm() {
} = useAppState();
const formHooks = useForm<CreateMachineForm>({
defaultValues: {
isSchemaLoading: false,
name: "",
config: {},
modules: [],
},
});
const { handleSubmit, reset } = formHooks;
const { handleSubmit, reset, watch } = formHooks;
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
const [activeStep, setActiveStep] = useState<number>(0);
const steps: FormStep[] = [
{
id: "template",
label: "Template",
content: <div></div>,
},
{
id: "modules",
label: "Modules",
@ -56,11 +56,6 @@ export function CreateMachineForm() {
<LinearProgress />
),
},
{
id: "save",
label: "Save",
content: <div></div>,
},
];
const handleNext = () => {
@ -75,14 +70,23 @@ export function CreateMachineForm() {
}
};
const handleReset = () => {
setActiveStep(0);
reset();
};
const currentStep = steps.at(activeStep);
async function onSubmit(data: any) {
async function onSubmit(data: CreateMachineForm) {
console.log({ data }, "Aggregated Data; creating machine from");
if (clanName) {
if (!data.name) {
toast.error("Machine name should not be empty");
return;
}
await createMachine(clanName, {
name: data.name,
});
await setMachineConfig(clanName, data.name, {
config: data.config.formData,
clanImports: data.modules,
});
}
}
const BackButton = () => (
@ -102,17 +106,21 @@ export function CreateMachineForm() {
<Button
disabled={
!formHooks.formState.isValid ||
(activeStep == 1 && !formHooks.watch("schema")?.type)
(activeStep == 0 && !watch("schema")?.type) ||
watch("isSchemaLoading")
}
onClick={handleNext}
color="secondary"
startIcon={
watch("isSchemaLoading") ? <CircularProgress /> : undefined
}
>
{activeStep <= steps.length - 1 && "Next"}
</Button>
)}
{activeStep === steps.length - 1 && (
<Button color="secondary" onClick={handleReset}>
Reset
<Button color="secondary" type="submit">
Save
</Button>
)}
</>

View File

@ -9,6 +9,7 @@ export type CreateMachineForm = {
config: any;
modules: string[];
schema: JSONSchema7;
isSchemaLoading: boolean;
};
export type FormHooks = UseFormReturn<CreateMachineForm>;