UI: add vars step to installation
This commit is contained in:
@@ -311,9 +311,9 @@ def test_generated_shared_secret_sops(
|
||||
shared_generator["script"] = "echo hello > $out/my_shared_secret"
|
||||
m2_config = flake.machines["machine2"]
|
||||
m2_config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
|
||||
m2_config["clan"]["core"]["vars"]["generators"][
|
||||
"my_shared_generator"
|
||||
] = shared_generator.copy()
|
||||
m2_config["clan"]["core"]["vars"]["generators"]["my_shared_generator"] = (
|
||||
shared_generator.copy()
|
||||
)
|
||||
flake.refresh()
|
||||
monkeypatch.chdir(flake.path)
|
||||
sops_setup.init()
|
||||
@@ -732,9 +732,9 @@ def test_migration(
|
||||
my_service = config["clan"]["core"]["facts"]["services"]["my_service"]
|
||||
my_service["public"]["my_value"] = {}
|
||||
my_service["secret"]["my_secret"] = {}
|
||||
my_service["generator"][
|
||||
"script"
|
||||
] = "echo -n hello > $facts/my_value && echo -n hello > $secrets/my_secret"
|
||||
my_service["generator"]["script"] = (
|
||||
"echo -n hello > $facts/my_value && echo -n hello > $secrets/my_secret"
|
||||
)
|
||||
my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"]
|
||||
my_generator["files"]["my_value"]["secret"] = False
|
||||
my_generator["files"]["my_secret"]["secret"] = True
|
||||
|
||||
@@ -28,11 +28,20 @@ export const Modal = (props: ModalProps) => {
|
||||
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
if (dragging()) {
|
||||
const newTop = e.clientY - startOffset().y;
|
||||
const newLeft = e.clientX - startOffset().x;
|
||||
let newTop = e.clientY - startOffset().y;
|
||||
let newLeft = e.clientX - startOffset().x;
|
||||
|
||||
if (newTop < 0) {
|
||||
newTop = 0;
|
||||
}
|
||||
if (newLeft < 0) {
|
||||
newLeft = 0;
|
||||
}
|
||||
dialogRef.style.top = `${newTop}px`;
|
||||
dialogRef.style.left = `${newLeft}px`;
|
||||
|
||||
// dialogRef.style.maxHeight = `calc(100vh - ${newTop}px - 2rem)`;
|
||||
// dialogRef.style.maxHeight = `calc(100vh - ${newTop}px - 2rem)`;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -53,7 +62,7 @@ export const Modal = (props: ModalProps) => {
|
||||
)}
|
||||
classList={{
|
||||
"!cursor-grabbing": dragging(),
|
||||
[cx("scale-105 transition-transform")]: dragging(),
|
||||
[cx("scale-[101%] transition-transform")]: dragging(),
|
||||
}}
|
||||
ref={(el) => {
|
||||
dialogRef = el;
|
||||
@@ -112,7 +121,10 @@ export const Modal = (props: ModalProps) => {
|
||||
/>
|
||||
</div>
|
||||
</Dialog.Label>
|
||||
<Dialog.Description class="flex flex-col bg-def-1" as="div">
|
||||
<Dialog.Description
|
||||
class="flex max-h-[90vh] flex-col overflow-y-hidden bg-def-1"
|
||||
as="div"
|
||||
>
|
||||
{props.children}
|
||||
</Dialog.Description>
|
||||
</Dialog.Content>
|
||||
|
||||
@@ -25,7 +25,7 @@ import { HardwareValues, HWStep } from "./install/hardware-step";
|
||||
import { DiskStep, DiskValues } from "./install/disk-step";
|
||||
import { SummaryStep } from "./install/summary-step";
|
||||
import cx from "classnames";
|
||||
import { SectionHeader } from "@/src/components/group";
|
||||
import { VarsStep, VarsValues } from "./install/vars-step";
|
||||
|
||||
type MachineFormInterface = MachineData & {
|
||||
sshKey?: File;
|
||||
@@ -37,7 +37,8 @@ type MachineData = SuccessData<"get_inventory_machine_details">;
|
||||
const steps: Record<StepIdx, string> = {
|
||||
"1": "Hardware detection",
|
||||
"2": "Disk schema",
|
||||
"3": "Installation",
|
||||
"3": "Credentials & Data",
|
||||
"4": "Installation",
|
||||
};
|
||||
|
||||
type StepIdx = keyof AllStepsValues;
|
||||
@@ -45,7 +46,8 @@ type StepIdx = keyof AllStepsValues;
|
||||
export interface AllStepsValues extends FieldValues {
|
||||
"1": HardwareValues;
|
||||
"2": DiskValues;
|
||||
"3": NonNullable<unknown>;
|
||||
"3": VarsValues;
|
||||
"4": NonNullable<unknown>;
|
||||
}
|
||||
|
||||
const LoadingBar = () => (
|
||||
@@ -190,7 +192,7 @@ const InstallMachine = (props: InstallMachineProps) => {
|
||||
};
|
||||
|
||||
const Footer = () => (
|
||||
<div class="flex justify-between">
|
||||
<div class="flex justify-between p-4">
|
||||
<Button
|
||||
startIcon={<Icon icon="ArrowLeft" />}
|
||||
variant="light"
|
||||
@@ -214,7 +216,10 @@ const InstallMachine = (props: InstallMachineProps) => {
|
||||
return (
|
||||
<Switch
|
||||
fallback={
|
||||
<Form onSubmit={handleInstall}>
|
||||
<Form
|
||||
onSubmit={handleInstall}
|
||||
class="relative top-0 flex h-full flex-col gap-0"
|
||||
>
|
||||
{/* Register each step as form field */}
|
||||
{/* @ts-expect-error: object type is not statically supported */}
|
||||
<Field name="1">{(field, fieldProps) => <></>}</Field>
|
||||
@@ -269,78 +274,90 @@ const InstallMachine = (props: InstallMachineProps) => {
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-6 p-6">
|
||||
<Switch
|
||||
fallback={"Undefined content. This Step seems to not exist."}
|
||||
>
|
||||
<Match when={step() === "1"}>
|
||||
<HWStep
|
||||
// @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()}
|
||||
handleNext={(data) => {
|
||||
const prev = getValue(formStore, "1");
|
||||
setValue(formStore, "1", { ...prev, ...data });
|
||||
handleNext();
|
||||
}}
|
||||
initial={
|
||||
getValue(formStore, "1") || {
|
||||
target: props.targetHost || "",
|
||||
report: false,
|
||||
}
|
||||
<Switch fallback={"Undefined content. This Step seems to not exist."}>
|
||||
<Match when={step() === "1"}>
|
||||
<HWStep
|
||||
// @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()}
|
||||
handleNext={(data) => {
|
||||
const prev = getValue(formStore, "1");
|
||||
setValue(formStore, "1", { ...prev, ...data });
|
||||
handleNext();
|
||||
}}
|
||||
initial={
|
||||
getValue(formStore, "1") || {
|
||||
target: props.targetHost || "",
|
||||
report: false,
|
||||
}
|
||||
footer={<Footer />}
|
||||
/>
|
||||
</Match>
|
||||
<Match when={step() === "2"}>
|
||||
<DiskStep
|
||||
// @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();
|
||||
}}
|
||||
// @ts-expect-error: The placeholder type is to wide
|
||||
initial={{
|
||||
...props.machine.disk_schema,
|
||||
...getValue(formStore, "2"),
|
||||
initialized: !!props.machine.disk_schema,
|
||||
}}
|
||||
/>
|
||||
</Match>
|
||||
<Match when={step() === "3"}>
|
||||
<SummaryStep
|
||||
// @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()}
|
||||
handleNext={() => handleNext()}
|
||||
// @ts-expect-error: This cannot be known.
|
||||
initial={getValues(formStore)}
|
||||
footer={
|
||||
<div class="flex justify-between">
|
||||
<Button
|
||||
startIcon={<Icon icon="ArrowLeft" />}
|
||||
variant="light"
|
||||
type="button"
|
||||
onClick={handlePrev}
|
||||
disabled={step() === "1"}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
<Button startIcon={<Icon icon="Flash" />}>Install</Button>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</Match>
|
||||
</Switch>
|
||||
</div>
|
||||
}
|
||||
footer={<Footer />}
|
||||
/>
|
||||
</Match>
|
||||
<Match when={step() === "2"}>
|
||||
<DiskStep
|
||||
// @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();
|
||||
}}
|
||||
// @ts-expect-error: The placeholder type is to wide
|
||||
initial={{
|
||||
...props.machine.disk_schema,
|
||||
...getValue(formStore, "2"),
|
||||
initialized: !!props.machine.disk_schema,
|
||||
}}
|
||||
/>
|
||||
</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"),
|
||||
}}
|
||||
/>
|
||||
</Match>
|
||||
<Match when={step() === "4"}>
|
||||
<SummaryStep
|
||||
// @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()}
|
||||
handleNext={() => handleNext()}
|
||||
// @ts-expect-error: This cannot be known.
|
||||
initial={getValues(formStore)}
|
||||
footer={
|
||||
<div class="flex justify-between p-4">
|
||||
<Button
|
||||
startIcon={<Icon icon="ArrowLeft" />}
|
||||
variant="light"
|
||||
type="button"
|
||||
onClick={handlePrev}
|
||||
disabled={step() === "1"}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
<Button startIcon={<Icon icon="Flash" />}>Install</Button>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</Match>
|
||||
</Switch>
|
||||
</Form>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -105,101 +105,105 @@ export const HWStep = (props: StepProps<HardwareValues>) => {
|
||||
|
||||
return (
|
||||
<Form onSubmit={handleSubmit} class="flex flex-col gap-6">
|
||||
<Group>
|
||||
<Field name="target" validate={required("Target must be provided")}>
|
||||
{(field, fieldProps) => (
|
||||
<TextInput
|
||||
error={field.error}
|
||||
variant="ghost"
|
||||
label="Target ip"
|
||||
value={field.value || ""}
|
||||
inputProps={fieldProps}
|
||||
required
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
</Group>
|
||||
<Group>
|
||||
<Field
|
||||
name="report"
|
||||
type="boolean"
|
||||
validate={required("Report must be generated")}
|
||||
>
|
||||
{(field, fieldProps) => (
|
||||
<FieldLayout
|
||||
error={field.error && <InputError error={field.error} />}
|
||||
label={
|
||||
<InputLabel
|
||||
<div class="max-h-[calc(100vh-20rem)] overflow-y-scroll">
|
||||
<div class="flex h-full flex-col gap-6 p-4">
|
||||
<Group>
|
||||
<Field name="target" validate={required("Target must be provided")}>
|
||||
{(field, fieldProps) => (
|
||||
<TextInput
|
||||
error={field.error}
|
||||
variant="ghost"
|
||||
label="Target ip"
|
||||
value={field.value || ""}
|
||||
inputProps={fieldProps}
|
||||
required
|
||||
help="Detect hardware specific drivers from target ip"
|
||||
>
|
||||
Hardware report
|
||||
</InputLabel>
|
||||
}
|
||||
field={
|
||||
<Switch>
|
||||
<Match when={hwReportQuery.isLoading}>
|
||||
<div>Loading...</div>
|
||||
</Match>
|
||||
<Match when={hwReportQuery.error}>
|
||||
<div>Error...</div>
|
||||
</Match>
|
||||
<Match when={hwReportQuery.data}>
|
||||
{(data) => (
|
||||
<>
|
||||
<Switch>
|
||||
<Match when={data() === "none"}>
|
||||
<Badge color="red" icon="Attention">
|
||||
No report
|
||||
</Badge>
|
||||
<Button
|
||||
variant="ghost"
|
||||
disabled={isGenerating()}
|
||||
startIcon={<Icon icon="Report" />}
|
||||
class="w-full"
|
||||
onClick={generateReport}
|
||||
>
|
||||
Run hardware detection
|
||||
</Button>
|
||||
</Match>
|
||||
<Match when={data() === "nixos-facter"}>
|
||||
<Badge color="primary" icon="Checkmark">
|
||||
Report detected
|
||||
</Badge>
|
||||
<Button
|
||||
variant="ghost"
|
||||
disabled={isGenerating()}
|
||||
startIcon={<Icon icon="Report" />}
|
||||
class="w-full"
|
||||
onClick={generateReport}
|
||||
>
|
||||
Re-run hardware detection
|
||||
</Button>
|
||||
</Match>
|
||||
<Match when={data() === "nixos-generate-config"}>
|
||||
<Badge color="primary" icon="Checkmark">
|
||||
Legacy Report detected
|
||||
</Badge>
|
||||
<Button
|
||||
variant="ghost"
|
||||
disabled={isGenerating()}
|
||||
startIcon={<Icon icon="Report" />}
|
||||
class="w-full"
|
||||
onClick={generateReport}
|
||||
>
|
||||
Replace hardware detection
|
||||
</Button>
|
||||
</Match>
|
||||
</Switch>
|
||||
</>
|
||||
)}
|
||||
</Match>
|
||||
</Switch>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
</Group>
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
</Group>
|
||||
<Group>
|
||||
<Field
|
||||
name="report"
|
||||
type="boolean"
|
||||
validate={required("Report must be generated")}
|
||||
>
|
||||
{(field, fieldProps) => (
|
||||
<FieldLayout
|
||||
error={field.error && <InputError error={field.error} />}
|
||||
label={
|
||||
<InputLabel
|
||||
required
|
||||
help="Detect hardware specific drivers from target ip"
|
||||
>
|
||||
Hardware report
|
||||
</InputLabel>
|
||||
}
|
||||
field={
|
||||
<Switch>
|
||||
<Match when={hwReportQuery.isLoading}>
|
||||
<div>Loading...</div>
|
||||
</Match>
|
||||
<Match when={hwReportQuery.error}>
|
||||
<div>Error...</div>
|
||||
</Match>
|
||||
<Match when={hwReportQuery.data}>
|
||||
{(data) => (
|
||||
<>
|
||||
<Switch>
|
||||
<Match when={data() === "none"}>
|
||||
<Badge color="red" icon="Attention">
|
||||
No report
|
||||
</Badge>
|
||||
<Button
|
||||
variant="ghost"
|
||||
disabled={isGenerating()}
|
||||
startIcon={<Icon icon="Report" />}
|
||||
class="w-full"
|
||||
onClick={generateReport}
|
||||
>
|
||||
Run hardware detection
|
||||
</Button>
|
||||
</Match>
|
||||
<Match when={data() === "nixos-facter"}>
|
||||
<Badge color="primary" icon="Checkmark">
|
||||
Report detected
|
||||
</Badge>
|
||||
<Button
|
||||
variant="ghost"
|
||||
disabled={isGenerating()}
|
||||
startIcon={<Icon icon="Report" />}
|
||||
class="w-full"
|
||||
onClick={generateReport}
|
||||
>
|
||||
Re-run hardware detection
|
||||
</Button>
|
||||
</Match>
|
||||
<Match when={data() === "nixos-generate-config"}>
|
||||
<Badge color="primary" icon="Checkmark">
|
||||
Legacy Report detected
|
||||
</Badge>
|
||||
<Button
|
||||
variant="ghost"
|
||||
disabled={isGenerating()}
|
||||
startIcon={<Icon icon="Report" />}
|
||||
class="w-full"
|
||||
onClick={generateReport}
|
||||
>
|
||||
Replace hardware detection
|
||||
</Button>
|
||||
</Match>
|
||||
</Switch>
|
||||
</>
|
||||
)}
|
||||
</Match>
|
||||
</Switch>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Field>
|
||||
</Group>
|
||||
</div>
|
||||
</div>
|
||||
{props.footer}
|
||||
</Form>
|
||||
);
|
||||
|
||||
@@ -12,87 +12,96 @@ export const SummaryStep = (props: StepProps<AllStepsValues>) => {
|
||||
const diskValues = () => props.initial?.["2"];
|
||||
return (
|
||||
<>
|
||||
<Section>
|
||||
<Typography
|
||||
hierarchy="label"
|
||||
size="xs"
|
||||
weight="medium"
|
||||
class="uppercase"
|
||||
>
|
||||
Hardware Report
|
||||
</Typography>
|
||||
<Group>
|
||||
<FieldLayout
|
||||
label={<InputLabel>Detected</InputLabel>}
|
||||
field={
|
||||
hwValues()?.report ? (
|
||||
<Badge color="green" class="w-fit">
|
||||
<Icon icon="Checkmark" color="inherit" />
|
||||
</Badge>
|
||||
) : (
|
||||
<Badge color="red" class="w-fit">
|
||||
<Icon icon="Warning" color="inherit" />
|
||||
</Badge>
|
||||
)
|
||||
}
|
||||
></FieldLayout>
|
||||
<FieldLayout
|
||||
label={<InputLabel>Target</InputLabel>}
|
||||
field={
|
||||
<Typography hierarchy="body" size="xs" weight="bold">
|
||||
{hwValues()?.target}
|
||||
</Typography>
|
||||
}
|
||||
></FieldLayout>
|
||||
</Group>
|
||||
</Section>
|
||||
<Section>
|
||||
<Typography
|
||||
hierarchy="label"
|
||||
size="xs"
|
||||
weight="medium"
|
||||
class="uppercase"
|
||||
>
|
||||
Disk Configuration
|
||||
</Typography>
|
||||
<Group>
|
||||
<FieldLayout
|
||||
label={<InputLabel>Disk Layout</InputLabel>}
|
||||
field={
|
||||
<Typography hierarchy="body" size="xs" weight="bold">
|
||||
{diskValues()?.schema}
|
||||
</Typography>
|
||||
}
|
||||
></FieldLayout>
|
||||
<hr class="h-px w-full border-none bg-acc-3"></hr>
|
||||
<FieldLayout
|
||||
label={<InputLabel>Main Disk</InputLabel>}
|
||||
field={
|
||||
<Typography hierarchy="body" size="xs" weight="bold">
|
||||
{diskValues()?.placeholders.mainDisk}
|
||||
</Typography>
|
||||
}
|
||||
></FieldLayout>
|
||||
</Group>
|
||||
</Section>
|
||||
<SectionHeader
|
||||
variant="danger"
|
||||
headline={
|
||||
<span>
|
||||
<Typography hierarchy="body" size="s" weight="bold" color="inherit">
|
||||
Setup your device.
|
||||
</Typography>
|
||||
<div class="max-h-[calc(100vh-20rem)] overflow-y-scroll">
|
||||
<div class="flex h-full flex-col gap-6 p-4">
|
||||
<Section>
|
||||
<Typography
|
||||
hierarchy="body"
|
||||
size="s"
|
||||
hierarchy="label"
|
||||
size="xs"
|
||||
weight="medium"
|
||||
color="inherit"
|
||||
class="uppercase"
|
||||
>
|
||||
This will erase the disk and bootstrap fresh.
|
||||
Hardware Report
|
||||
</Typography>
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
<Group>
|
||||
<FieldLayout
|
||||
label={<InputLabel>Detected</InputLabel>}
|
||||
field={
|
||||
hwValues()?.report ? (
|
||||
<Badge color="green" class="w-fit">
|
||||
<Icon icon="Checkmark" color="inherit" />
|
||||
</Badge>
|
||||
) : (
|
||||
<Badge color="red" class="w-fit">
|
||||
<Icon icon="Warning" color="inherit" />
|
||||
</Badge>
|
||||
)
|
||||
}
|
||||
></FieldLayout>
|
||||
<FieldLayout
|
||||
label={<InputLabel>Target</InputLabel>}
|
||||
field={
|
||||
<Typography hierarchy="body" size="xs" weight="bold">
|
||||
{hwValues()?.target}
|
||||
</Typography>
|
||||
}
|
||||
></FieldLayout>
|
||||
</Group>
|
||||
</Section>
|
||||
<Section>
|
||||
<Typography
|
||||
hierarchy="label"
|
||||
size="xs"
|
||||
weight="medium"
|
||||
class="uppercase"
|
||||
>
|
||||
Disk Configuration
|
||||
</Typography>
|
||||
<Group>
|
||||
<FieldLayout
|
||||
label={<InputLabel>Disk Layout</InputLabel>}
|
||||
field={
|
||||
<Typography hierarchy="body" size="xs" weight="bold">
|
||||
{diskValues()?.schema}
|
||||
</Typography>
|
||||
}
|
||||
></FieldLayout>
|
||||
<hr class="h-px w-full border-none bg-acc-3"></hr>
|
||||
<FieldLayout
|
||||
label={<InputLabel>Main Disk</InputLabel>}
|
||||
field={
|
||||
<Typography hierarchy="body" size="xs" weight="bold">
|
||||
{diskValues()?.placeholders.mainDisk}
|
||||
</Typography>
|
||||
}
|
||||
></FieldLayout>
|
||||
</Group>
|
||||
</Section>
|
||||
<SectionHeader
|
||||
variant="danger"
|
||||
headline={
|
||||
<span>
|
||||
<Typography
|
||||
hierarchy="body"
|
||||
size="s"
|
||||
weight="bold"
|
||||
color="inherit"
|
||||
>
|
||||
Setup your device.
|
||||
</Typography>
|
||||
<Typography
|
||||
hierarchy="body"
|
||||
size="s"
|
||||
weight="medium"
|
||||
color="inherit"
|
||||
>
|
||||
This will erase the disk and bootstrap fresh.
|
||||
</Typography>
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{props.footer}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import { callApi } from "@/src/api";
|
||||
import {
|
||||
createForm,
|
||||
SubmitHandler,
|
||||
validate,
|
||||
FieldValues,
|
||||
} from "@modular-forms/solid";
|
||||
import { createQuery } from "@tanstack/solid-query";
|
||||
import { StepProps } from "./hardware-step";
|
||||
import { Typography } from "@/src/components/Typography";
|
||||
import { Group } from "@/src/components/group";
|
||||
import { For, Match, Show, Switch } from "solid-js";
|
||||
|
||||
export type VarsValues = FieldValues & Record<string, string>;
|
||||
|
||||
export const VarsStep = (props: StepProps<VarsValues>) => {
|
||||
const [formStore, { Form, Field }] = createForm<VarsValues>({
|
||||
initialValues: { ...props.initial, schema: "single-disk" },
|
||||
});
|
||||
|
||||
const handleSubmit: SubmitHandler<VarsValues> = async (values, event) => {
|
||||
console.log("Submit Disk", { values });
|
||||
const valid = await validate(formStore);
|
||||
console.log("Valid", valid);
|
||||
if (!valid) return;
|
||||
props.handleNext(values);
|
||||
};
|
||||
|
||||
const generatorsQuery = createQuery(() => ({
|
||||
queryKey: [props.dir, props.machine_id, "generators"],
|
||||
queryFn: async () => {
|
||||
const result = await callApi("get_generators", {
|
||||
base_dir: props.dir,
|
||||
machine_name: props.machine_id,
|
||||
});
|
||||
if (result.status === "error") throw new Error("Failed to fetch data");
|
||||
return result.data;
|
||||
},
|
||||
}));
|
||||
|
||||
return (
|
||||
<Form
|
||||
onSubmit={handleSubmit}
|
||||
class="flex h-full flex-col gap-6"
|
||||
noValidate={false}
|
||||
>
|
||||
<div class="max-h-[calc(100vh-20rem)] overflow-y-scroll">
|
||||
<div class="flex h-full flex-col gap-6 p-4">
|
||||
<Switch>
|
||||
<Match when={generatorsQuery.isLoading}>Loading ...</Match>
|
||||
<Match when={generatorsQuery.data}>
|
||||
{(generators) => (
|
||||
<For each={generators()}>
|
||||
{(generator) => (
|
||||
<Group>
|
||||
<Typography hierarchy="label" size="default">
|
||||
{generator.name}
|
||||
</Typography>
|
||||
<div>
|
||||
Bound to module (shared):{" "}
|
||||
{generator.share ? "True" : "False"}
|
||||
</div>
|
||||
<For each={generator.prompts}>
|
||||
{(f) => (
|
||||
<Group>
|
||||
<Typography hierarchy="label" size="s">
|
||||
{!f.previous_value ? "Required" : "Optional"}
|
||||
</Typography>
|
||||
<Typography hierarchy="label" size="s">
|
||||
{f.name}
|
||||
</Typography>
|
||||
</Group>
|
||||
)}
|
||||
</For>
|
||||
</Group>
|
||||
)}
|
||||
</For>
|
||||
)}
|
||||
</Match>
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
<Show when={generatorsQuery.isFetched}>{props.footer}</Show>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user