Merge pull request 'GUI: initialize support for vars prompts' (#3529) from DavHau/clan-core:gui-prompts into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/3529
This commit is contained in:
@@ -12,7 +12,12 @@ from clan_cli.tests.age_keys import SopsSetup
|
|||||||
from clan_cli.tests.fixtures_flakes import ClanFlake
|
from clan_cli.tests.fixtures_flakes import ClanFlake
|
||||||
from clan_cli.tests.helpers import cli
|
from clan_cli.tests.helpers import cli
|
||||||
from clan_cli.vars.check import check_vars
|
from clan_cli.vars.check import check_vars
|
||||||
from clan_cli.vars.generate import Generator, generate_vars_for_machine_interactive
|
from clan_cli.vars.generate import (
|
||||||
|
Generator,
|
||||||
|
generate_vars_for_machine,
|
||||||
|
generate_vars_for_machine_interactive,
|
||||||
|
get_generators_closure,
|
||||||
|
)
|
||||||
from clan_cli.vars.get import get_var
|
from clan_cli.vars.get import get_var
|
||||||
from clan_cli.vars.graph import all_missing_closure, requested_closure
|
from clan_cli.vars.graph import all_missing_closure, requested_closure
|
||||||
from clan_cli.vars.list import stringify_all_vars
|
from clan_cli.vars.list import stringify_all_vars
|
||||||
@@ -640,9 +645,6 @@ def test_api_set_prompts(
|
|||||||
monkeypatch: pytest.MonkeyPatch,
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
flake: ClanFlake,
|
flake: ClanFlake,
|
||||||
) -> None:
|
) -> None:
|
||||||
from clan_cli.vars._types import GeneratorUpdate
|
|
||||||
from clan_cli.vars.list import get_generators, set_prompts
|
|
||||||
|
|
||||||
config = flake.machines["my_machine"]
|
config = flake.machines["my_machine"]
|
||||||
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
|
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
|
||||||
my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"]
|
my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"]
|
||||||
@@ -652,33 +654,39 @@ def test_api_set_prompts(
|
|||||||
flake.refresh()
|
flake.refresh()
|
||||||
|
|
||||||
monkeypatch.chdir(flake.path)
|
monkeypatch.chdir(flake.path)
|
||||||
params = {"machine_name": "my_machine", "base_dir": str(flake.path)}
|
|
||||||
|
|
||||||
set_prompts(
|
generate_vars_for_machine(
|
||||||
**params,
|
machine_name="my_machine",
|
||||||
updates=[
|
base_dir=flake.path,
|
||||||
GeneratorUpdate(
|
generators=["my_generator"],
|
||||||
generator="my_generator",
|
all_prompt_values={
|
||||||
prompt_values={"prompt1": "input1"},
|
"my_generator": {
|
||||||
)
|
"prompt1": "input1",
|
||||||
],
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
machine = Machine(name="my_machine", flake=Flake(str(flake.path)))
|
machine = Machine(name="my_machine", flake=Flake(str(flake.path)))
|
||||||
store = in_repo.FactStore(machine)
|
store = in_repo.FactStore(machine)
|
||||||
assert store.exists(Generator("my_generator"), "prompt1")
|
assert store.exists(Generator("my_generator"), "prompt1")
|
||||||
assert store.get(Generator("my_generator"), "prompt1").decode() == "input1"
|
assert store.get(Generator("my_generator"), "prompt1").decode() == "input1"
|
||||||
set_prompts(
|
generate_vars_for_machine(
|
||||||
**params,
|
machine_name="my_machine",
|
||||||
updates=[
|
base_dir=flake.path,
|
||||||
GeneratorUpdate(
|
generators=["my_generator"],
|
||||||
generator="my_generator",
|
all_prompt_values={
|
||||||
prompt_values={"prompt1": "input2"},
|
"my_generator": {
|
||||||
)
|
"prompt1": "input2",
|
||||||
],
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
assert store.get(Generator("my_generator"), "prompt1").decode() == "input2"
|
assert store.get(Generator("my_generator"), "prompt1").decode() == "input2"
|
||||||
|
|
||||||
generators = get_generators(**params)
|
generators = get_generators_closure(
|
||||||
|
machine_name="my_machine",
|
||||||
|
base_dir=flake.path,
|
||||||
|
regenerate=True,
|
||||||
|
include_previous_values=True,
|
||||||
|
)
|
||||||
assert len(generators) == 1
|
assert len(generators) == 1
|
||||||
assert generators[0].name == "my_generator"
|
assert generators[0].name == "my_generator"
|
||||||
assert generators[0].prompts[0].name == "prompt1"
|
assert generators[0].prompts[0].name == "prompt1"
|
||||||
|
|||||||
@@ -294,10 +294,28 @@ def _ask_prompts(
|
|||||||
return prompt_values
|
return prompt_values
|
||||||
|
|
||||||
|
|
||||||
|
def _get_previous_value(
|
||||||
|
machine: "Machine",
|
||||||
|
generator: Generator,
|
||||||
|
prompt: Prompt,
|
||||||
|
) -> str | None:
|
||||||
|
if not prompt.persist:
|
||||||
|
return None
|
||||||
|
|
||||||
|
pub_store = machine.public_vars_store
|
||||||
|
if pub_store.exists(generator, prompt.name):
|
||||||
|
return pub_store.get(generator, prompt.name).decode()
|
||||||
|
sec_store = machine.secret_vars_store
|
||||||
|
if sec_store.exists(generator, prompt.name):
|
||||||
|
return sec_store.get(generator, prompt.name).decode()
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_closure(
|
def get_closure(
|
||||||
machine: "Machine",
|
machine: "Machine",
|
||||||
generator_name: str | None,
|
generator_name: str | None,
|
||||||
regenerate: bool,
|
regenerate: bool,
|
||||||
|
include_previous_values: bool = False,
|
||||||
) -> list[Generator]:
|
) -> list[Generator]:
|
||||||
from .graph import all_missing_closure, full_closure
|
from .graph import all_missing_closure, full_closure
|
||||||
|
|
||||||
@@ -310,14 +328,24 @@ def get_closure(
|
|||||||
for generator in vars_generators:
|
for generator in vars_generators:
|
||||||
generator.machine(machine)
|
generator.machine(machine)
|
||||||
|
|
||||||
|
result_closure = []
|
||||||
if generator_name is None: # all generators selected
|
if generator_name is None: # all generators selected
|
||||||
if regenerate:
|
if regenerate:
|
||||||
return full_closure(generators)
|
result_closure = full_closure(generators)
|
||||||
return all_missing_closure(generators)
|
else:
|
||||||
|
result_closure = all_missing_closure(generators)
|
||||||
# specific generator selected
|
# specific generator selected
|
||||||
if regenerate:
|
elif regenerate:
|
||||||
return requested_closure([generator_name], generators)
|
result_closure = requested_closure([generator_name], generators)
|
||||||
return minimal_closure([generator_name], generators)
|
else:
|
||||||
|
result_closure = minimal_closure([generator_name], generators)
|
||||||
|
|
||||||
|
if include_previous_values:
|
||||||
|
for generator in result_closure:
|
||||||
|
for prompt in generator.prompts:
|
||||||
|
prompt.previous_value = _get_previous_value(machine, generator, prompt)
|
||||||
|
|
||||||
|
return result_closure
|
||||||
|
|
||||||
|
|
||||||
@API.register
|
@API.register
|
||||||
@@ -325,6 +353,7 @@ def get_generators_closure(
|
|||||||
machine_name: str,
|
machine_name: str,
|
||||||
base_dir: Path,
|
base_dir: Path,
|
||||||
regenerate: bool = False,
|
regenerate: bool = False,
|
||||||
|
include_previous_values: bool = False,
|
||||||
) -> list[Generator]:
|
) -> list[Generator]:
|
||||||
from clan_cli.machines.machines import Machine
|
from clan_cli.machines.machines import Machine
|
||||||
|
|
||||||
@@ -332,13 +361,14 @@ def get_generators_closure(
|
|||||||
machine=Machine(name=machine_name, flake=Flake(str(base_dir))),
|
machine=Machine(name=machine_name, flake=Flake(str(base_dir))),
|
||||||
generator_name=None,
|
generator_name=None,
|
||||||
regenerate=regenerate,
|
regenerate=regenerate,
|
||||||
|
include_previous_values=include_previous_values,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _generate_vars_for_machine(
|
def _generate_vars_for_machine(
|
||||||
machine: "Machine",
|
machine: "Machine",
|
||||||
generators: list[Generator],
|
generators: list[Generator],
|
||||||
all_prompt_values: dict[str, dict],
|
all_prompt_values: dict[str, dict[str, str]],
|
||||||
no_sandbox: bool = False,
|
no_sandbox: bool = False,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
for generator in generators:
|
for generator in generators:
|
||||||
@@ -350,7 +380,7 @@ def _generate_vars_for_machine(
|
|||||||
generator=generator,
|
generator=generator,
|
||||||
secret_vars_store=machine.secret_vars_store,
|
secret_vars_store=machine.secret_vars_store,
|
||||||
public_vars_store=machine.public_vars_store,
|
public_vars_store=machine.public_vars_store,
|
||||||
prompt_values=all_prompt_values[generator.name],
|
prompt_values=all_prompt_values.get(generator.name, {}),
|
||||||
no_sandbox=no_sandbox,
|
no_sandbox=no_sandbox,
|
||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import { ModuleDetails as AddModule } from "./routes/modules/add";
|
|||||||
import { ApiTester } from "./api_test";
|
import { ApiTester } from "./api_test";
|
||||||
import { IconVariant } from "./components/icon";
|
import { IconVariant } from "./components/icon";
|
||||||
import { Components } from "./routes/components";
|
import { Components } from "./routes/components";
|
||||||
|
import { activeURI } from "./App";
|
||||||
|
import { VarsForMachine, VarsStep } from "./routes/machines/install/vars-step";
|
||||||
|
|
||||||
export const client = new QueryClient();
|
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?",
|
"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) {
|
if (import.meta.env.DEV) {
|
||||||
console.log("Development mode");
|
console.log("Development mode");
|
||||||
// Load the debugger in development mode
|
// Load the debugger in development mode
|
||||||
@@ -73,6 +74,12 @@ export const routes: AppRoute[] = [
|
|||||||
hidden: true,
|
hidden: true,
|
||||||
component: () => <MachineDetails />,
|
component: () => <MachineDetails />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/:id/vars",
|
||||||
|
label: "Vars",
|
||||||
|
hidden: true,
|
||||||
|
component: () => <VarsForMachine />,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ import {
|
|||||||
getValues,
|
getValues,
|
||||||
setValue,
|
setValue,
|
||||||
} from "@modular-forms/solid";
|
} from "@modular-forms/solid";
|
||||||
import { useParams } from "@solidjs/router";
|
import { useNavigate, useParams, useSearchParams } from "@solidjs/router";
|
||||||
import { createQuery } from "@tanstack/solid-query";
|
import { createQuery, useQueryClient } from "@tanstack/solid-query";
|
||||||
import { createSignal, For, Match, Show, Switch } from "solid-js";
|
import { createEffect, createSignal, For, Match, Show, Switch } from "solid-js";
|
||||||
import toast from "solid-toast";
|
import toast from "solid-toast";
|
||||||
import { MachineAvatar } from "./avatar";
|
import { MachineAvatar } from "./avatar";
|
||||||
import { Header } from "@/src/layout/header";
|
import { Header } from "@/src/layout/header";
|
||||||
@@ -316,21 +316,7 @@ const InstallMachine = (props: InstallMachineProps) => {
|
|||||||
/>
|
/>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={step() === "3"}>
|
<Match when={step() === "3"}>
|
||||||
<VarsStep
|
<div>TODO: vars</div>
|
||||||
// @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"),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={step() === "4"}>
|
<Match when={step() === "4"}>
|
||||||
<SummaryStep
|
<SummaryStep
|
||||||
@@ -416,6 +402,10 @@ const MachineForm = (props: MachineDetailsProps) => {
|
|||||||
|
|
||||||
const [installModalOpen, setInstallModalOpen] = createSignal(false);
|
const [installModalOpen, setInstallModalOpen] = createSignal(false);
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const handleSubmit = async (values: MachineFormInterface) => {
|
const handleSubmit = async (values: MachineFormInterface) => {
|
||||||
console.log("submitting", values);
|
console.log("submitting", values);
|
||||||
|
|
||||||
@@ -447,7 +437,40 @@ const MachineForm = (props: MachineDetailsProps) => {
|
|||||||
return null;
|
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 () => {
|
const handleUpdate = async () => {
|
||||||
|
if (isUpdating()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const curr_uri = activeURI();
|
const curr_uri = activeURI();
|
||||||
if (!curr_uri) {
|
if (!curr_uri) {
|
||||||
return;
|
return;
|
||||||
@@ -461,6 +484,7 @@ const MachineForm = (props: MachineDetailsProps) => {
|
|||||||
const target = targetHost();
|
const target = targetHost();
|
||||||
|
|
||||||
const loading_toast = toast.loading("Updating machine...");
|
const loading_toast = toast.loading("Updating machine...");
|
||||||
|
setIsUpdating(true);
|
||||||
const r = await callApi("update_machines", {
|
const r = await callApi("update_machines", {
|
||||||
base_path: curr_uri,
|
base_path: curr_uri,
|
||||||
machines: [
|
machines: [
|
||||||
@@ -472,6 +496,7 @@ const MachineForm = (props: MachineDetailsProps) => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
setIsUpdating(false);
|
||||||
toast.dismiss(loading_toast);
|
toast.dismiss(loading_toast);
|
||||||
|
|
||||||
if (r.status === "error") {
|
if (r.status === "error") {
|
||||||
@@ -481,6 +506,15 @@ const MachineForm = (props: MachineDetailsProps) => {
|
|||||||
toast.success("Machine updated successfully");
|
toast.success("Machine updated successfully");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
const action = searchParams.action;
|
||||||
|
if (action === "update") {
|
||||||
|
setSearchParams({ action: undefined });
|
||||||
|
handleUpdate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div class="flex flex-col gap-6 p-4">
|
<div class="flex flex-col gap-6 p-4">
|
||||||
@@ -626,7 +660,7 @@ const MachineForm = (props: MachineDetailsProps) => {
|
|||||||
<Button
|
<Button
|
||||||
class="w-full"
|
class="w-full"
|
||||||
// disabled={!online()}
|
// disabled={!online()}
|
||||||
onClick={() => handleUpdate()}
|
onClick={() => handleUpdateButton()}
|
||||||
endIcon={<Icon icon="Update" />}
|
endIcon={<Icon icon="Update" />}
|
||||||
>
|
>
|
||||||
Update
|
Update
|
||||||
|
|||||||
@@ -5,31 +5,73 @@ import {
|
|||||||
validate,
|
validate,
|
||||||
FieldValues,
|
FieldValues,
|
||||||
} from "@modular-forms/solid";
|
} from "@modular-forms/solid";
|
||||||
import { createQuery } from "@tanstack/solid-query";
|
import { createQuery, useQueryClient } from "@tanstack/solid-query";
|
||||||
import { StepProps } from "./hardware-step";
|
|
||||||
import { Typography } from "@/src/components/Typography";
|
import { Typography } from "@/src/components/Typography";
|
||||||
import { Group } from "@/src/components/group";
|
import { Group } from "@/src/components/group";
|
||||||
import { For, Match, Show, Switch } from "solid-js";
|
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>) => {
|
export interface VarsStepProps {
|
||||||
const [formStore, { Form, Field }] = createForm<VarsValues>({
|
machine_id: string;
|
||||||
initialValues: { ...props.initial, schema: "single-disk" },
|
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) => {
|
const handleSubmit: SubmitHandler<VarsValues> = async (values, event) => {
|
||||||
console.log("Submit Disk", { values });
|
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);
|
const valid = await validate(formStore);
|
||||||
console.log("Valid", valid);
|
if (generatorsQuery.data === undefined) {
|
||||||
if (!valid) return;
|
toast.error("Error fetching data");
|
||||||
props.handleNext(values);
|
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(() => ({
|
const generatorsQuery = createQuery(() => ({
|
||||||
queryKey: [props.dir, props.machine_id, "generators"],
|
queryKey: [props.dir, props.machine_id, "generators"],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const result = await callApi("get_generators", {
|
const result = await callApi("get_generators_closure", {
|
||||||
base_dir: props.dir,
|
base_dir: props.dir,
|
||||||
machine_name: props.machine_id,
|
machine_name: props.machine_id,
|
||||||
});
|
});
|
||||||
@@ -61,14 +103,33 @@ export const VarsStep = (props: StepProps<VarsValues>) => {
|
|||||||
{generator.share ? "True" : "False"}
|
{generator.share ? "True" : "False"}
|
||||||
</div>
|
</div>
|
||||||
<For each={generator.prompts}>
|
<For each={generator.prompts}>
|
||||||
{(f) => (
|
{(prompt) => (
|
||||||
<Group>
|
<Group>
|
||||||
<Typography hierarchy="label" size="s">
|
<Typography hierarchy="label" size="s">
|
||||||
{!f.previous_value ? "Required" : "Optional"}
|
{!prompt.previous_value ? "Required" : "Optional"}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography hierarchy="label" size="s">
|
<Typography hierarchy="label" size="s">
|
||||||
{f.name}
|
{prompt.name}
|
||||||
</Typography>
|
</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>
|
</Group>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
@@ -80,7 +141,17 @@ export const VarsStep = (props: StepProps<VarsValues>) => {
|
|||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Show when={generatorsQuery.isFetched}>{props.footer}</Show>
|
<button type="submit">Submit</button>
|
||||||
</Form>
|
</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