From 518de45d41051d9b73d1a64c000a7356b3ae0092 Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Thu, 7 Aug 2025 12:53:58 +0200 Subject: [PATCH] ui/install: hook up stepper store and api --- .../ui/src/workflows/Install/install.tsx | 8 ++ .../Install/steps/createInstaller.tsx | 109 ++++++++++++++---- 2 files changed, 96 insertions(+), 21 deletions(-) diff --git a/pkgs/clan-app/ui/src/workflows/Install/install.tsx b/pkgs/clan-app/ui/src/workflows/Install/install.tsx index ed25edc38..5bfbc5fab 100644 --- a/pkgs/clan-app/ui/src/workflows/Install/install.tsx +++ b/pkgs/clan-app/ui/src/workflows/Install/install.tsx @@ -50,6 +50,14 @@ const steps = [ ] as const; export type InstallSteps = typeof steps; +export interface InstallStoreType { + flash: { + language: string; + keymap: string; + ssh_file: string; + device: string; + }; +} export const InstallModal = (props: InstallModalProps) => { const stepper = createStepper( diff --git a/pkgs/clan-app/ui/src/workflows/Install/steps/createInstaller.tsx b/pkgs/clan-app/ui/src/workflows/Install/steps/createInstaller.tsx index 0b8212be7..a4b3707c8 100644 --- a/pkgs/clan-app/ui/src/workflows/Install/steps/createInstaller.tsx +++ b/pkgs/clan-app/ui/src/workflows/Install/steps/createInstaller.tsx @@ -1,4 +1,4 @@ -import { useStepper } from "@/src/hooks/stepper"; +import { getStepStore, useStepper } from "@/src/hooks/stepper"; import { createForm, getError, @@ -6,8 +6,7 @@ import { valiForm, } from "@modular-forms/solid"; import * as v from "valibot"; -import { InstallSteps } from "../install"; -import { callApi } from "@/src/hooks/api"; +import { InstallSteps, InstallStoreType } from "../install"; import { Fieldset } from "@/src/components/Form/Fieldset"; import { HostFileInput } from "@/src/components/Form/HostFileInput"; import { Select } from "@/src/components/Select/Select"; @@ -17,6 +16,12 @@ import { Alert } from "@/src/components/Alert/Alert"; import { LoadingBar } from "@/src/components/LoadingBar/LoadingBar"; import { Button } from "@/src/components/Button/Button"; import Icon from "@/src/components/Icon/Icon"; +import { + useMachineFlashOptions, + useSystemStorageOptions, +} from "@/src/hooks/queries"; +import { useClanURI } from "@/src/hooks/clan"; +import { useApiClient } from "@/src/hooks/ApiClient"; const Prose = () => ( ( -
+
Let's walk through it. @@ -101,15 +106,23 @@ const ConfigureImage = () => { }); const stepSignal = useStepper(); - // TODO: push values to the parent form Store + const [store, set] = getStepStore(stepSignal); + const handleSubmit: SubmitHandler = (values, event) => { - console.log("ISO creation submitted", values); - // Here you would typically trigger the ISO creation process + // Push values to the store + set("flash", (s) => ({ + ...s, + language: values.language, + keymap: values.keymap, + ssh_file: values.ssh_key, + })); + stepSignal.next(); }; + const client = useApiClient(); const onSelectFile = async () => { - const req = callApi("get_system_file", { + const req = client.fetch("get_system_file", { file_request: { mode: "select_folder", title: "Select a folder for you new Clan", @@ -131,6 +144,9 @@ const ConfigureImage = () => { throw new Error("No data returned from api call"); }; + const currClan = useClanURI(); + const optionsQuery = useMachineFlashOptions(currClan); + return (
{ label: "Language", description: "Select your preferred language", }} - options={[ - { value: "en", label: "English" }, - { value: "fr", label: "Français" }, - ]} + getOptions={async () => { + if (!optionsQuery.data) { + await optionsQuery.refetch(); + } + + return (optionsQuery.data?.languages ?? []).map( + (lang) => ({ + // TODO: Pretty label ? + value: lang, + label: lang, + }), + ); + }} placeholder="Language" name={field.name} /> @@ -188,10 +213,19 @@ const ConfigureImage = () => { label: "Keymap", description: "Select your keyboard layout", }} - options={[ - { value: "EN_US", label: "QWERTY" }, - { value: "DE_DE", label: "QWERTZ" }, - ]} + getOptions={async () => { + if (!optionsQuery.data) { + await optionsQuery.refetch(); + } + + return (optionsQuery.data?.keymaps ?? []).map( + (keymap) => ({ + // TODO: Pretty label ? + value: keymap, + label: keymap, + }), + ); + }} placeholder="Keymap" name={field.name} /> @@ -226,9 +260,34 @@ const ChooseDisk = () => { const [formStore, { Form, Field }] = createForm({ validate: valiForm(ChooseDiskSchema), }); + const [store, set] = getStepStore(stepSignal); + const client = useApiClient(); + const systemStorageQuery = useSystemStorageOptions(); const handleSubmit: SubmitHandler = (values, event) => { - console.log("Disk selected", values); + // Just for completeness, set the disk in the store + console.log("Flashing", store.flash); + set("flash", (s) => ({ + ...s, + device: values.disk, + })); + const call = client.fetch("run_machine_flash", { + system_config: { + keymap: store.flash.keymap, + language: store.flash.language, + ssh_keys_path: [store.flash.ssh_file], + }, + disks: [ + { + name: "main", + device: values.disk, + }, + ], + }); + // TOOD: Pass the "call" Promise to the "progress step" + + console.log("Flashing", store.flash); + // Here you would typically trigger the disk selection process stepSignal.next(); }; @@ -249,10 +308,18 @@ const ChooseDisk = () => { label: "USB Stick", description: "Select the usb stick", }} - options={[ - { value: "1", label: "sda1" }, - { value: "2", label: "sdb2" }, - ]} + getOptions={async () => { + if (!systemStorageQuery.data) { + await systemStorageQuery.refetch(); + } + + return (systemStorageQuery.data?.blockdevices ?? []).map( + (dev) => ({ + value: dev.path, + label: dev.name, + }), + ); + }} placeholder="Choose Device" name={field.name} />