GUI: add credentials page (vars)

add it to machine details
This commit is contained in:
DavHau
2025-05-21 17:01:49 +07:00
parent 1c0b383183
commit c45c94e045
5 changed files with 135 additions and 87 deletions

View File

@@ -538,6 +538,17 @@ const MachineForm = (props: MachineDetailsProps) => {
>
Update
</Button>
<Button
variant="light"
class="w-full"
size="s"
onClick={() => {
navigate(`/machines/${machineName()}/vars`);
}}
endIcon={<Icon size={12} icon="Folder" />}
>
Credentials
</Button>
</div>
<div class=" w-fit" data-tip="Machine must be online"></div>
{/* <Typography hierarchy="label" size="default">

View File

@@ -13,6 +13,7 @@ import {
getValue,
submit,
setValue,
FormStore,
} from "@modular-forms/solid";
import { createEffect, createSignal, JSX, Match, Switch } from "solid-js";
import { TextInput } from "@/src/Form/fields";

View File

@@ -8,12 +8,14 @@ import {
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 { For, JSX, Match, Show, Switch } from "solid-js";
import { TextInput } from "@/src/Form/fields";
import toast from "solid-toast";
import { useNavigate, useParams, useSearchParams } from "@solidjs/router";
import { activeURI } from "@/src/App";
import { StepProps } from "./hardware-step";
import { BackButton } from "@/src/components/BackButton";
import { Button } from "@/src/components/button";
export type VarsValues = FieldValues & Record<string, Record<string, string>>;
@@ -22,6 +24,7 @@ export interface VarsFormProps {
dir: string;
handleSubmit: SubmitHandler<VarsValues>;
generators: SuccessData<"get_generators_closure">;
footer: JSX.Element;
}
export const VarsForm = (props: VarsFormProps) => {
@@ -58,6 +61,10 @@ export const VarsForm = (props: VarsFormProps) => {
>
<div class="max-h-[calc(100vh-20rem)] overflow-y-scroll">
<div class="flex h-full flex-col gap-6 p-4">
<Show
when={props.generators.length > 0}
fallback="No credentials needed"
>
<For each={props.generators}>
{(generator) => (
<Group>
@@ -65,7 +72,8 @@ export const VarsForm = (props: VarsFormProps) => {
{generator.name}
</Typography>
<div>
Bound to module (shared): {generator.share ? "True" : "False"}
Bound to module (shared):{" "}
{generator.share ? "True" : "False"}
</div>
<For each={generator.prompts}>
{(prompt) => (
@@ -123,22 +131,28 @@ export const VarsForm = (props: VarsFormProps) => {
</Group>
)}
</For>
</Show>
</div>
</div>
<button type="submit">Submit</button>
{props.footer}
</Form>
);
};
export const VarsStep = (props: StepProps<VarsValues>) => {
type VarsStepProps = StepProps<VarsValues> & {
fullClosure?: boolean;
};
export const VarsStep = (props: VarsStepProps) => {
const queryClient = useQueryClient();
const generatorsQuery = createQuery(() => ({
queryKey: [props.dir, props.machine_id, "generators"],
queryKey: [props.dir, props.machine_id, "generators", props.fullClosure],
queryFn: async () => {
const result = await callApi("get_generators_closure", {
base_dir: props.dir,
machine_name: props.machine_id,
full_closure: props.fullClosure,
});
if (result.status === "error") throw new Error("Failed to fetch data");
return result.data;
@@ -181,6 +195,7 @@ export const VarsStep = (props: StepProps<VarsValues>) => {
dir={props.dir}
handleSubmit={handleSubmit}
generators={generators()}
footer={props.footer}
/>
)}
</Match>
@@ -196,19 +211,40 @@ export const VarsPage = () => {
if (searchParams?.action === "update") {
navigate(`/machines/${params.id}?action=update`);
} else {
toast.error("Invalid action for vars page");
navigate(-1);
}
};
const fullClosure = searchParams?.full_closure !== undefined;
const footer = (
<footer class="flex justify-end gap-y-3 border-t border-secondary-200 pt-5">
<Button
type="submit"
// disabled={formStore.submitting || !formStore.dirty}
>
Update edits
</Button>
</footer>
);
return (
<div class="p-1">
{/* BackButton and header */}
<BackButton />
<div class="p-2">
<h3 class="text-2xl">{params.id}</h3>
</div>
{/* VarsStep component */}
<Show when={activeURI()}>
{(uri) => (
<VarsStep
machine_id={params.id}
dir={uri()}
handleNext={handleNext}
footer
footer={footer}
fullClosure={fullClosure}
/>
)}
</Show>
</div>
);
};

View File

@@ -684,7 +684,7 @@ def test_api_set_prompts(
generators = get_generators_closure(
machine_name="my_machine",
base_dir=flake.path,
regenerate=True,
full_closure=True,
include_previous_values=True,
)
assert len(generators) == 1

View File

@@ -315,10 +315,10 @@ def _get_previous_value(
def get_closure(
machine: "Machine",
generator_name: str | None,
regenerate: bool,
full_closure: bool,
include_previous_values: bool = False,
) -> list[Generator]:
from .graph import all_missing_closure, full_closure
from . import graph
vars_generators = machine.vars_generators()
generators: dict[str, Generator] = {
@@ -331,12 +331,12 @@ def get_closure(
result_closure = []
if generator_name is None: # all generators selected
if regenerate:
result_closure = full_closure(generators)
if full_closure:
result_closure = graph.full_closure(generators)
else:
result_closure = all_missing_closure(generators)
result_closure = graph.all_missing_closure(generators)
# specific generator selected
elif regenerate:
elif full_closure:
result_closure = requested_closure([generator_name], generators)
else:
result_closure = minimal_closure([generator_name], generators)
@@ -353,7 +353,7 @@ def get_closure(
def get_generators_closure(
machine_name: str,
base_dir: Path,
regenerate: bool = False,
full_closure: bool = False,
include_previous_values: bool = False,
) -> list[Generator]:
from clan_cli.machines.machines import Machine
@@ -361,7 +361,7 @@ def get_generators_closure(
return get_closure(
machine=Machine(name=machine_name, flake=Flake(str(base_dir))),
generator_name=None,
regenerate=regenerate,
full_closure=full_closure,
include_previous_values=include_previous_values,
)