GUI: initialize support for vars prompts
... for now only when updating a machine (not when installing) Whenever the user clicks on the update button in the machine view, and only if user input is needed for some missing vars, the user will be forwarded to a vars page.
This commit is contained in:
@@ -21,6 +21,8 @@ import { ModuleDetails as AddModule } from "./routes/modules/add";
|
||||
import { ApiTester } from "./api_test";
|
||||
import { IconVariant } from "./components/icon";
|
||||
import { Components } from "./routes/components";
|
||||
import { activeURI } from "./App";
|
||||
import { VarsForMachine, VarsStep } from "./routes/machines/install/vars-step";
|
||||
|
||||
export const client = new QueryClient();
|
||||
|
||||
@@ -31,7 +33,6 @@ if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
|
||||
"Root element not found. Did you forget to add it to your index.html? Or maybe the id attribute got misspelled?",
|
||||
);
|
||||
}
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.log("Development mode");
|
||||
// Load the debugger in development mode
|
||||
@@ -73,6 +74,12 @@ export const routes: AppRoute[] = [
|
||||
hidden: true,
|
||||
component: () => <MachineDetails />,
|
||||
},
|
||||
{
|
||||
path: "/:id/vars",
|
||||
label: "Vars",
|
||||
hidden: true,
|
||||
component: () => <VarsForMachine />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -11,9 +11,9 @@ import {
|
||||
getValues,
|
||||
setValue,
|
||||
} from "@modular-forms/solid";
|
||||
import { useParams } from "@solidjs/router";
|
||||
import { createQuery } from "@tanstack/solid-query";
|
||||
import { createSignal, For, Match, Show, Switch } from "solid-js";
|
||||
import { useNavigate, useParams, useSearchParams } from "@solidjs/router";
|
||||
import { createQuery, useQueryClient } from "@tanstack/solid-query";
|
||||
import { createEffect, createSignal, For, Match, Show, Switch } from "solid-js";
|
||||
import toast from "solid-toast";
|
||||
import { MachineAvatar } from "./avatar";
|
||||
import { Header } from "@/src/layout/header";
|
||||
@@ -316,21 +316,7 @@ const InstallMachine = (props: InstallMachineProps) => {
|
||||
/>
|
||||
</Match>
|
||||
<Match when={step() === "3"}>
|
||||
<VarsStep
|
||||
// @ts-expect-error: This cannot be undefined in this context.
|
||||
machine_id={props.name}
|
||||
// @ts-expect-error: This cannot be undefined in this context.
|
||||
dir={activeURI()}
|
||||
footer={<Footer />}
|
||||
handleNext={(data) => {
|
||||
// const prev = getValue(formStore, "2");
|
||||
// setValue(formStore, "2", { ...prev, ...data });
|
||||
handleNext();
|
||||
}}
|
||||
initial={{
|
||||
...getValue(formStore, "3"),
|
||||
}}
|
||||
/>
|
||||
<div>TODO: vars</div>
|
||||
</Match>
|
||||
<Match when={step() === "4"}>
|
||||
<SummaryStep
|
||||
@@ -416,6 +402,10 @@ const MachineForm = (props: MachineDetailsProps) => {
|
||||
|
||||
const [installModalOpen, setInstallModalOpen] = createSignal(false);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const handleSubmit = async (values: MachineFormInterface) => {
|
||||
console.log("submitting", values);
|
||||
|
||||
@@ -447,7 +437,40 @@ const MachineForm = (props: MachineDetailsProps) => {
|
||||
return null;
|
||||
};
|
||||
|
||||
const generatorsQuery = createQuery(() => ({
|
||||
queryKey: [activeURI(), machineName(), "generators"],
|
||||
queryFn: async () => {
|
||||
const machine_name = machineName();
|
||||
const base_dir = activeURI();
|
||||
if (!machine_name || !base_dir) {
|
||||
return [];
|
||||
}
|
||||
const result = await callApi("get_generators_closure", {
|
||||
base_dir: base_dir,
|
||||
machine_name: machine_name,
|
||||
});
|
||||
if (result.status === "error") throw new Error("Failed to fetch data");
|
||||
return result.data;
|
||||
},
|
||||
}));
|
||||
|
||||
const handleUpdateButton = async () => {
|
||||
const t = toast.loading("Checking for generators...");
|
||||
await generatorsQuery.refetch();
|
||||
toast.dismiss(t);
|
||||
if (generatorsQuery.data?.length !== 0) {
|
||||
navigate(`/machines/${machineName()}/vars`);
|
||||
} else {
|
||||
handleUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
const [isUpdating, setIsUpdating] = createSignal(false);
|
||||
|
||||
const handleUpdate = async () => {
|
||||
if (isUpdating()) {
|
||||
return;
|
||||
}
|
||||
const curr_uri = activeURI();
|
||||
if (!curr_uri) {
|
||||
return;
|
||||
@@ -461,6 +484,7 @@ const MachineForm = (props: MachineDetailsProps) => {
|
||||
const target = targetHost();
|
||||
|
||||
const loading_toast = toast.loading("Updating machine...");
|
||||
setIsUpdating(true);
|
||||
const r = await callApi("update_machines", {
|
||||
base_path: curr_uri,
|
||||
machines: [
|
||||
@@ -472,6 +496,7 @@ const MachineForm = (props: MachineDetailsProps) => {
|
||||
},
|
||||
],
|
||||
});
|
||||
setIsUpdating(false);
|
||||
toast.dismiss(loading_toast);
|
||||
|
||||
if (r.status === "error") {
|
||||
@@ -481,6 +506,15 @@ const MachineForm = (props: MachineDetailsProps) => {
|
||||
toast.success("Machine updated successfully");
|
||||
}
|
||||
};
|
||||
|
||||
createEffect(() => {
|
||||
const action = searchParams.action;
|
||||
if (action === "update") {
|
||||
setSearchParams({ action: undefined });
|
||||
handleUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div class="flex flex-col gap-6 p-4">
|
||||
@@ -626,7 +660,7 @@ const MachineForm = (props: MachineDetailsProps) => {
|
||||
<Button
|
||||
class="w-full"
|
||||
// disabled={!online()}
|
||||
onClick={() => handleUpdate()}
|
||||
onClick={() => handleUpdateButton()}
|
||||
endIcon={<Icon icon="Update" />}
|
||||
>
|
||||
Update
|
||||
|
||||
@@ -5,31 +5,73 @@ import {
|
||||
validate,
|
||||
FieldValues,
|
||||
} from "@modular-forms/solid";
|
||||
import { createQuery } from "@tanstack/solid-query";
|
||||
import { StepProps } from "./hardware-step";
|
||||
import { createQuery, useQueryClient } from "@tanstack/solid-query";
|
||||
import { Typography } from "@/src/components/Typography";
|
||||
import { Group } from "@/src/components/group";
|
||||
import { For, Match, Show, Switch } from "solid-js";
|
||||
import { TextInput } from "@/src/Form/fields";
|
||||
import toast from "solid-toast";
|
||||
import { useNavigate, useParams } from "@solidjs/router";
|
||||
import { activeURI } from "@/src/App";
|
||||
|
||||
export type VarsValues = FieldValues & Record<string, string>;
|
||||
export type VarsValues = FieldValues & Record<string, Record<string, string>>;
|
||||
|
||||
export const VarsStep = (props: StepProps<VarsValues>) => {
|
||||
const [formStore, { Form, Field }] = createForm<VarsValues>({
|
||||
initialValues: { ...props.initial, schema: "single-disk" },
|
||||
});
|
||||
export interface VarsStepProps {
|
||||
machine_id: string;
|
||||
dir: string;
|
||||
}
|
||||
|
||||
export const VarsStep = (props: VarsStepProps) => {
|
||||
const [formStore, { Form, Field }] = createForm<VarsValues>({});
|
||||
|
||||
const navigate = useNavigate();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const handleSubmit: SubmitHandler<VarsValues> = async (values, event) => {
|
||||
console.log("Submit Disk", { values });
|
||||
// sanitize the values back (replace __dot__)
|
||||
// This hack is needed because we are using "." in the keys of the form
|
||||
const sanitizedValues = Object.fromEntries(
|
||||
Object.entries(values).map(([key, value]) => [
|
||||
key.replaceAll("__dot__", "."),
|
||||
Object.fromEntries(
|
||||
Object.entries(value).map(([k, v]) => [
|
||||
k.replaceAll("__dot__", "."),
|
||||
v,
|
||||
]),
|
||||
),
|
||||
]),
|
||||
) as VarsValues;
|
||||
const valid = await validate(formStore);
|
||||
console.log("Valid", valid);
|
||||
if (!valid) return;
|
||||
props.handleNext(values);
|
||||
if (generatorsQuery.data === undefined) {
|
||||
toast.error("Error fetching data");
|
||||
return;
|
||||
}
|
||||
const loading_toast = toast.loading("Generating vars...");
|
||||
const result = await callApi("generate_vars_for_machine", {
|
||||
machine_name: props.machine_id,
|
||||
base_dir: props.dir,
|
||||
generators: generatorsQuery.data.map((generator) => generator.name),
|
||||
all_prompt_values: sanitizedValues,
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [props.dir, props.machine_id, "generators"],
|
||||
});
|
||||
toast.dismiss(loading_toast);
|
||||
if (result.status === "error") {
|
||||
toast.error(result.errors[0].message);
|
||||
return;
|
||||
}
|
||||
if (result.status === "success") {
|
||||
toast.success("Vars saved successfully");
|
||||
navigate(`/machines/${props.machine_id}?action=update`);
|
||||
}
|
||||
};
|
||||
|
||||
const generatorsQuery = createQuery(() => ({
|
||||
queryKey: [props.dir, props.machine_id, "generators"],
|
||||
queryFn: async () => {
|
||||
const result = await callApi("get_generators", {
|
||||
const result = await callApi("get_generators_closure", {
|
||||
base_dir: props.dir,
|
||||
machine_name: props.machine_id,
|
||||
});
|
||||
@@ -61,14 +103,33 @@ export const VarsStep = (props: StepProps<VarsValues>) => {
|
||||
{generator.share ? "True" : "False"}
|
||||
</div>
|
||||
<For each={generator.prompts}>
|
||||
{(f) => (
|
||||
{(prompt) => (
|
||||
<Group>
|
||||
<Typography hierarchy="label" size="s">
|
||||
{!f.previous_value ? "Required" : "Optional"}
|
||||
{!prompt.previous_value ? "Required" : "Optional"}
|
||||
</Typography>
|
||||
<Typography hierarchy="label" size="s">
|
||||
{f.name}
|
||||
{prompt.name}
|
||||
</Typography>
|
||||
{/* Avoid nesting issue in case of a "." */}
|
||||
<Field
|
||||
name={`${generator.name.replaceAll(".", "__dot__")}.${prompt.name.replaceAll(".", "__dot__")}`}
|
||||
>
|
||||
{(field, props) => (
|
||||
<TextInput
|
||||
inputProps={{
|
||||
...props,
|
||||
type:
|
||||
prompt.prompt_type === "hidden"
|
||||
? "password"
|
||||
: "text",
|
||||
}}
|
||||
label={prompt.description}
|
||||
value={prompt.previous_value ?? ""}
|
||||
error={field.error}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
</Group>
|
||||
)}
|
||||
</For>
|
||||
@@ -80,7 +141,17 @@ export const VarsStep = (props: StepProps<VarsValues>) => {
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
<Show when={generatorsQuery.isFetched}>{props.footer}</Show>
|
||||
<button type="submit">Submit</button>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export const VarsForMachine = () => {
|
||||
const params = useParams();
|
||||
|
||||
return (
|
||||
<Show when={activeURI()}>
|
||||
{(uri) => <VarsStep machine_id={params.id} dir={uri()} />}
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user