clan-cli: Add --wifi option to set wifi credentials. clan-app: Add wifi settings form to flash view

This commit is contained in:
Qubasa
2024-08-07 21:34:46 +02:00
parent 5739851d5f
commit 9b7322fdab
6 changed files with 289 additions and 5 deletions

View File

@@ -4,10 +4,11 @@ import { type JSX } from "solid-js";
interface TextInputProps<T extends FieldValues, R extends ResponseData> {
formStore: FormStore<T, R>;
value: string;
inputProps: JSX.HTMLAttributes<HTMLInputElement>;
inputProps: JSX.InputHTMLAttributes<HTMLInputElement>;
label: JSX.Element;
error?: string;
required?: boolean;
type?: string;
inlineLabel?: JSX.Element;
}
@@ -36,7 +37,7 @@ export function TextInput<T extends FieldValues, R extends ResponseData>(
<input
{...props.inputProps}
value={props.value}
type="text"
type={props.type ? props.type : "text"}
class="grow"
classList={{
"input-disabled": props.formStore.submitting,

View File

@@ -2,11 +2,22 @@ import { callApi, OperationResponse } from "@/src/api";
import { FileInput } from "@/src/components/FileInput";
import { SelectInput } from "@/src/components/SelectInput";
import { TextInput } from "@/src/components/TextInput";
import { createForm, required, FieldValues } from "@modular-forms/solid";
import {
createForm,
required,
FieldValues,
setValue,
getValue,
} from "@modular-forms/solid";
import { createQuery } from "@tanstack/solid-query";
import { For } from "solid-js";
import { createEffect, createSignal, For } from "solid-js";
import toast from "solid-toast";
interface Wifi extends FieldValues {
ssid: string;
password: string;
}
interface FlashFormValues extends FieldValues {
machine: {
devicePath: string;
@@ -15,6 +26,7 @@ interface FlashFormValues extends FieldValues {
disk: string;
language: string;
keymap: string;
wifi: Wifi[];
sshKeys: File[];
}
@@ -30,6 +42,44 @@ export const Flash = () => {
},
});
/* ==== WIFI NETWORK ==== */
const [wifiNetworks, setWifiNetworks] = createSignal<Wifi[]>([]);
const [passwordVisibility, setPasswordVisibility] = createSignal<boolean[]>(
[],
);
createEffect(() => {
const formWifi = getValue(formStore, "wifi");
if (formWifi !== undefined) {
setWifiNetworks(formWifi as Wifi[]);
setPasswordVisibility(new Array(formWifi.length).fill(false));
}
});
const addWifiNetwork = () => {
const updatedNetworks = [...wifiNetworks(), { ssid: "", password: "" }];
setWifiNetworks(updatedNetworks);
setPasswordVisibility([...passwordVisibility(), false]);
setValue(formStore, "wifi", updatedNetworks);
};
const removeWifiNetwork = (index: number) => {
const updatedNetworks = wifiNetworks().filter((_, i) => i !== index);
setWifiNetworks(updatedNetworks);
const updatedVisibility = passwordVisibility().filter(
(_, i) => i !== index,
);
setPasswordVisibility(updatedVisibility);
setValue(formStore, "wifi", updatedNetworks);
};
const togglePasswordVisibility = (index: number) => {
const updatedVisibility = [...passwordVisibility()];
updatedVisibility[index] = !updatedVisibility[index];
setPasswordVisibility(updatedVisibility);
};
/* ==== END OF WIFI NETWORK ==== */
const deviceQuery = createQuery(() => ({
queryKey: ["block_devices"],
queryFn: async () => {
@@ -86,6 +136,7 @@ export const Flash = () => {
};
const handleSubmit = async (values: FlashFormValues) => {
console.log("Submit WiFi Networks:", values.wifi);
console.log(
"Submit SSH Keys:",
values.sshKeys.map((file) => file.name),
@@ -104,6 +155,10 @@ export const Flash = () => {
language: values.language,
keymap: values.keymap,
ssh_keys_path: values.sshKeys.map((file) => file.name),
wifi_settings: values.wifi.map((network) => ({
ssid: network.ssid,
password: network.password,
})),
},
dry_run: false,
write_efi_boot_entries: false,
@@ -250,6 +305,78 @@ export const Flash = () => {
</>
)}
</Field>
{/* WiFi Networks */}
<div class="mb-4">
<h3 class="text-lg font-semibold mb-2">WiFi Networks</h3>
<For each={wifiNetworks()}>
{(network, index) => (
<div class="flex gap-2 mb-2">
<Field
name={`wifi.${index()}.ssid`}
validate={[required("SSID is required")]}
>
{(field, props) => (
<TextInput
formStore={formStore}
inputProps={props}
label="SSID"
value={field.value ?? ""}
error={field.error}
required
/>
)}
</Field>
<Field
name={`wifi.${index()}.password`}
validate={[required("Password is required")]}
>
{(field, props) => (
<div class="relative w-full">
<TextInput
formStore={formStore}
inputProps={props}
type={
passwordVisibility()[index()] ? "text" : "password"
}
label="Password"
value={field.value ?? ""}
error={field.error}
required
/>
<button
type="button"
class="absolute inset-y-14 right-0 pr-3 flex items-center text-sm leading-5"
onClick={() => togglePasswordVisibility(index())}
>
<span class="material-icons">
{passwordVisibility()[index()]
? "visibility_off"
: "visibility"}
</span>
</button>
</div>
)}
</Field>
<button
type="button"
class="btn btn-error"
onClick={() => removeWifiNetwork(index())}
>
<span class="material-icons">delete</span>
</button>
</div>
)}
</For>
<button
type="button"
class="btn btn-primary"
onClick={addWifiNetwork}
>
<span class="material-icons">add</span> Add WiFi Network
</button>
</div>
<button
class="btn btn-error"
type="submit"