clan-app: Finish flash view. clan-cli: Flash cli now verifies if language and keymap are valid.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { createEffect, createSignal, type Component } from "solid-js";
|
||||
import { type Component, createEffect, createSignal } from "solid-js";
|
||||
import { Layout } from "./layout/layout";
|
||||
import { Route, Router } from "./Routes";
|
||||
import { Toaster } from "solid-toast";
|
||||
@@ -18,7 +18,7 @@ const [activeURI, setActiveURI] = makePersisted(
|
||||
{
|
||||
name: "activeURI",
|
||||
storage: localStorage,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export { activeURI, setActiveURI };
|
||||
|
||||
@@ -58,11 +58,11 @@ const registry: ObserverRegistry = operationNames.reduce(
|
||||
...acc,
|
||||
[opName]: {},
|
||||
}),
|
||||
{} as ObserverRegistry
|
||||
{} as ObserverRegistry,
|
||||
);
|
||||
|
||||
function createFunctions<K extends OperationNames>(
|
||||
operationName: K
|
||||
operationName: K,
|
||||
): {
|
||||
dispatch: (args: OperationArgs<K>) => void;
|
||||
receive: (fn: (response: OperationResponse<K>) => void, id: string) => void;
|
||||
@@ -104,7 +104,7 @@ function download(filename: string, text: string) {
|
||||
const element = document.createElement("a");
|
||||
element.setAttribute(
|
||||
"href",
|
||||
"data:text/plain;charset=utf-8," + encodeURIComponent(text)
|
||||
"data:text/plain;charset=utf-8," + encodeURIComponent(text),
|
||||
);
|
||||
element.setAttribute("download", filename);
|
||||
|
||||
@@ -118,7 +118,7 @@ function download(filename: string, text: string) {
|
||||
|
||||
export const callApi = <K extends OperationNames>(
|
||||
method: K,
|
||||
args: OperationArgs<K>
|
||||
args: OperationArgs<K>,
|
||||
) => {
|
||||
return new Promise<OperationResponse<K>>((resolve, reject) => {
|
||||
const id = nanoid();
|
||||
@@ -134,21 +134,19 @@ export const callApi = <K extends OperationNames>(
|
||||
});
|
||||
};
|
||||
|
||||
const deserialize =
|
||||
<T>(fn: (response: T) => void) =>
|
||||
(str: string) => {
|
||||
try {
|
||||
const r = JSON.parse(str) as T;
|
||||
console.log("Received: ", r);
|
||||
fn(r);
|
||||
} catch (e) {
|
||||
console.log("Error parsing JSON: ", e);
|
||||
window.localStorage.setItem("error", str);
|
||||
console.error(str);
|
||||
console.error("See localStorage 'error'");
|
||||
alert(`Error parsing JSON: ${e}`);
|
||||
}
|
||||
};
|
||||
const deserialize = <T>(fn: (response: T) => void) => (str: string) => {
|
||||
try {
|
||||
const r = JSON.parse(str) as T;
|
||||
console.log("Received: ", r);
|
||||
fn(r);
|
||||
} catch (e) {
|
||||
console.log("Error parsing JSON: ", e);
|
||||
window.localStorage.setItem("error", str);
|
||||
console.error(str);
|
||||
console.error("See localStorage 'error'");
|
||||
alert(`Error parsing JSON: ${e}`);
|
||||
}
|
||||
};
|
||||
|
||||
// Create the API object
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Match, Show, Switch, createSignal } from "solid-js";
|
||||
import { ErrorData, SuccessData, pyApi } from "../api";
|
||||
import { createSignal, Match, Show, Switch } from "solid-js";
|
||||
import { ErrorData, pyApi, SuccessData } from "../api";
|
||||
|
||||
type MachineDetails = SuccessData<"list_machines">["data"][string];
|
||||
|
||||
@@ -94,21 +94,18 @@ export const MachineListItem = (props: MachineListItemProps) => {
|
||||
<div class="flex flex-row flex-wrap gap-4 py-2">
|
||||
<div class="badge badge-primary flex flex-row gap-1 py-4 align-middle">
|
||||
<span>System:</span>
|
||||
{hwInfo()[name]?.system ? (
|
||||
<span class="text-primary">{hwInfo()[name]?.system}</span>
|
||||
) : (
|
||||
<span class="text-warning">Not set</span>
|
||||
)}
|
||||
{hwInfo()[name]?.system
|
||||
? <span class="text-primary">{hwInfo()[name]?.system}</span>
|
||||
: <span class="text-warning">Not set</span>}
|
||||
</div>
|
||||
|
||||
<div class="badge badge-ghost flex flex-row gap-1 py-4 align-middle">
|
||||
<span>Target Host:</span>
|
||||
{deploymentInfo()[name] ? (
|
||||
<span class="text-primary">{deploymentInfo()[name]}</span>
|
||||
) : (
|
||||
<span class="text-warning">Not set</span>
|
||||
)}
|
||||
{/* <Show
|
||||
{deploymentInfo()[name]
|
||||
? <span class="text-primary">{deploymentInfo()[name]}</span>
|
||||
: <span class="text-warning">Not set</span>}
|
||||
{
|
||||
/* <Show
|
||||
when={deploymentInfo()[name]}
|
||||
fallback={
|
||||
<Switch fallback={<div class="skeleton h-8 w-full"></div>}>
|
||||
@@ -119,7 +116,8 @@ export const MachineListItem = (props: MachineListItemProps) => {
|
||||
}
|
||||
>
|
||||
{(i) => + i()}
|
||||
</Show> */}
|
||||
</Show> */
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{/* Show only the first error at the bottom */}
|
||||
|
||||
@@ -13,9 +13,9 @@ export interface UseFloatingOptions<
|
||||
whileElementsMounted?: (
|
||||
reference: R,
|
||||
floating: F,
|
||||
update: () => void
|
||||
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
||||
) => void | (() => void);
|
||||
update: () => void,
|
||||
) => // eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
||||
void | (() => void);
|
||||
}
|
||||
|
||||
interface UseFloatingState extends Omit<ComputePositionReturn, "x" | "y"> {
|
||||
@@ -30,7 +30,7 @@ export interface UseFloatingResult extends UseFloatingState {
|
||||
export function useFloating<R extends ReferenceElement, F extends HTMLElement>(
|
||||
reference: () => R | undefined | null,
|
||||
floating: () => F | undefined | null,
|
||||
options?: UseFloatingOptions<R, F>
|
||||
options?: UseFloatingOptions<R, F>,
|
||||
): UseFloatingResult {
|
||||
const placement = () => options?.placement ?? "bottom";
|
||||
const strategy = () => options?.strategy ?? "absolute";
|
||||
@@ -77,7 +77,7 @@ export function useFloating<R extends ReferenceElement, F extends HTMLElement>(
|
||||
},
|
||||
(err) => {
|
||||
setError(err);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -95,7 +95,7 @@ export function useFloating<R extends ReferenceElement, F extends HTMLElement>(
|
||||
const cleanup = options.whileElementsMounted(
|
||||
currentReference,
|
||||
currentFloating,
|
||||
update
|
||||
update,
|
||||
);
|
||||
|
||||
if (cleanup) {
|
||||
|
||||
@@ -13,7 +13,7 @@ window.clan = window.clan || {};
|
||||
|
||||
if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
|
||||
throw new Error(
|
||||
"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?",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -30,5 +30,5 @@ render(
|
||||
</QueryClientProvider>
|
||||
),
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
root!
|
||||
root!,
|
||||
);
|
||||
|
||||
@@ -32,7 +32,8 @@ export const Layout: Component<LayoutProps> = (props) => {
|
||||
for="toplevel-drawer"
|
||||
aria-label="close sidebar"
|
||||
class="drawer-overlay"
|
||||
></label>
|
||||
>
|
||||
</label>
|
||||
<Sidebar route={route} setRoute={setRoute} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -27,35 +27,35 @@ export const BlockDevicesView: Component = () => {
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex max-w-screen-lg flex-col gap-4">
|
||||
{isFetching ? (
|
||||
<span class="loading loading-bars"></span>
|
||||
) : (
|
||||
<Show when={devices}>
|
||||
{(devices) => (
|
||||
<For each={devices().blockdevices}>
|
||||
{(device) => (
|
||||
<div class="stats shadow">
|
||||
<div class="stat w-28 py-8">
|
||||
<div class="stat-title">Name</div>
|
||||
<div class="stat-value">
|
||||
{" "}
|
||||
<span class="material-icons">storage</span>{" "}
|
||||
{device.name}
|
||||
{isFetching
|
||||
? <span class="loading loading-bars"></span>
|
||||
: (
|
||||
<Show when={devices}>
|
||||
{(devices) => (
|
||||
<For each={devices().blockdevices}>
|
||||
{(device) => (
|
||||
<div class="stats shadow">
|
||||
<div class="stat w-28 py-8">
|
||||
<div class="stat-title">Name</div>
|
||||
<div class="stat-value">
|
||||
{" "}
|
||||
<span class="material-icons">storage</span>{" "}
|
||||
{device.name}
|
||||
</div>
|
||||
<div class="stat-desc"></div>
|
||||
</div>
|
||||
<div class="stat-desc"></div>
|
||||
</div>
|
||||
|
||||
<div class="stat w-28">
|
||||
<div class="stat-title">Size</div>
|
||||
<div class="stat-value">{device.size}</div>
|
||||
<div class="stat-desc"></div>
|
||||
<div class="stat w-28">
|
||||
<div class="stat-title">Size</div>
|
||||
<div class="stat-value">{device.size}</div>
|
||||
<div class="stat-desc"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
)}
|
||||
</Show>
|
||||
)}
|
||||
)}
|
||||
</For>
|
||||
)}
|
||||
</Show>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { OperationResponse, callApi, pyApi } from "@/src/api";
|
||||
import { callApi, OperationResponse, pyApi } from "@/src/api";
|
||||
import { Show } from "solid-js";
|
||||
import {
|
||||
SubmitHandler,
|
||||
createForm,
|
||||
required,
|
||||
reset,
|
||||
SubmitHandler,
|
||||
} from "@modular-forms/solid";
|
||||
import toast from "solid-toast";
|
||||
import { setActiveURI, setRoute } from "@/src/App";
|
||||
@@ -43,7 +43,7 @@ export const ClanForm = () => {
|
||||
(async () => {
|
||||
await callApi("create_clan", {
|
||||
options: {
|
||||
directory: target_dir,
|
||||
directory: target_dir[0],
|
||||
template_url,
|
||||
initial: {
|
||||
meta,
|
||||
@@ -52,14 +52,14 @@ export const ClanForm = () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
setActiveURI(target_dir);
|
||||
setActiveURI(target_dir[0]);
|
||||
setRoute("machines");
|
||||
})(),
|
||||
{
|
||||
loading: "Creating clan...",
|
||||
success: "Clan Successfully Created",
|
||||
error: "Failed to create clan",
|
||||
}
|
||||
},
|
||||
);
|
||||
reset(formStore);
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { OperationResponse, callApi, pyApi } from "@/src/api";
|
||||
import { Accessor, Show, Switch, Match } from "solid-js";
|
||||
import { callApi, OperationResponse, pyApi } from "@/src/api";
|
||||
import { Accessor, Match, Show, Switch } from "solid-js";
|
||||
import {
|
||||
SubmitHandler,
|
||||
createForm,
|
||||
required,
|
||||
reset,
|
||||
SubmitHandler,
|
||||
} from "@modular-forms/solid";
|
||||
import toast from "solid-toast";
|
||||
import { createQuery } from "@tanstack/solid-query";
|
||||
@@ -65,7 +65,7 @@ export const FinalEditClanForm = (props: FinalEditClanFormProps) => {
|
||||
loading: "Updating clan...",
|
||||
success: "Clan Successfully updated",
|
||||
error: "Failed to update clan",
|
||||
}
|
||||
},
|
||||
);
|
||||
props.done();
|
||||
};
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import { route } from "@/src/App";
|
||||
import { OperationArgs, OperationResponse, callApi, pyApi } from "@/src/api";
|
||||
import { SubmitHandler, createForm, required } from "@modular-forms/solid";
|
||||
import { callApi, OperationArgs, OperationResponse, pyApi } from "@/src/api";
|
||||
import {
|
||||
createForm,
|
||||
required,
|
||||
setValue,
|
||||
SubmitHandler,
|
||||
} from "@modular-forms/solid";
|
||||
import { createQuery } from "@tanstack/solid-query";
|
||||
import { For, createSignal } from "solid-js";
|
||||
import { createEffect, createSignal, For } from "solid-js";
|
||||
import { effect } from "solid-js/web";
|
||||
|
||||
// type FlashMachineArgs = {
|
||||
// machine: Omit<OperationArgs<"flash_machine">["machine"], "cached_deployment">;
|
||||
// } & Omit<Omit<OperationArgs<"flash_machine">, "machine">, "system_config">;
|
||||
|
||||
// type FlashMachineArgs = OperationArgs<"flash_machine">;
|
||||
|
||||
// type k = keyof FlashMachineArgs;
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
||||
type FlashFormValues = {
|
||||
machine: {
|
||||
name: string;
|
||||
devicePath: string;
|
||||
flake: string;
|
||||
};
|
||||
disk: string;
|
||||
language: string;
|
||||
keymap: string;
|
||||
sshKeys: string[];
|
||||
};
|
||||
|
||||
type BlockDevices = Extract<
|
||||
@@ -28,10 +28,39 @@ type BlockDevices = Extract<
|
||||
|
||||
export const Flash = () => {
|
||||
const [formStore, { Form, Field }] = createForm<FlashFormValues>({});
|
||||
const [sshKeys, setSshKeys] = createSignal<string[]>([]);
|
||||
const [isFlashing, setIsFlashing] = createSignal(false);
|
||||
|
||||
const selectSshPubkey = async () => {
|
||||
try {
|
||||
const loc = await callApi("open_file", {
|
||||
file_request: {
|
||||
title: "Select SSH Key",
|
||||
mode: "open_multiple_files",
|
||||
filters: { patterns: ["*.pub"] },
|
||||
initial_folder: "~/.ssh",
|
||||
},
|
||||
});
|
||||
console.log({ loc }, loc.status);
|
||||
if (loc.status === "success" && loc.data) {
|
||||
setSshKeys(loc.data);
|
||||
return loc.data;
|
||||
}
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
};
|
||||
|
||||
// Create an effect that updates the form when externalUsername changes
|
||||
createEffect(() => {
|
||||
const newSshKeys = sshKeys();
|
||||
if (newSshKeys) {
|
||||
setValue(formStore, "sshKeys", newSshKeys);
|
||||
}
|
||||
});
|
||||
|
||||
const {
|
||||
data: devices,
|
||||
refetch: loadDevices,
|
||||
isFetching,
|
||||
} = createQuery(() => ({
|
||||
queryKey: ["block_devices"],
|
||||
@@ -40,20 +69,61 @@ export const Flash = () => {
|
||||
if (result.status === "error") throw new Error("Failed to fetch data");
|
||||
return result.data;
|
||||
},
|
||||
staleTime: 1000 * 60 * 1, // 1 minutes
|
||||
staleTime: 1000 * 60 * 2, // 1 minutes
|
||||
}));
|
||||
|
||||
const {
|
||||
data: keymaps,
|
||||
isFetching: isFetchingKeymaps,
|
||||
} = createQuery(() => ({
|
||||
queryKey: ["list_keymaps"],
|
||||
queryFn: async () => {
|
||||
const result = await callApi("list_possible_keymaps", {});
|
||||
if (result.status === "error") throw new Error("Failed to fetch data");
|
||||
return result.data;
|
||||
},
|
||||
staleTime: 1000 * 60 * 15, // 15 minutes
|
||||
}));
|
||||
|
||||
const {
|
||||
data: languages,
|
||||
isFetching: isFetchingLanguages,
|
||||
} = createQuery(() => ({
|
||||
queryKey: ["list_languages"],
|
||||
queryFn: async () => {
|
||||
const result = await callApi("list_possible_languages", {});
|
||||
if (result.status === "error") throw new Error("Failed to fetch data");
|
||||
return result.data;
|
||||
},
|
||||
staleTime: 1000 * 60 * 15, // 15 minutes
|
||||
}));
|
||||
|
||||
const handleSubmit = async (values: FlashFormValues) => {
|
||||
// TODO: Rework Flash machine API
|
||||
// Its unusable in its current state
|
||||
// await callApi("flash_machine", {
|
||||
// machine: {
|
||||
// name: "",
|
||||
// },
|
||||
// disks: {values.disk },
|
||||
// dry_run: true,
|
||||
// });
|
||||
console.log("submit", values);
|
||||
setIsFlashing(true);
|
||||
try {
|
||||
await callApi("flash_machine", {
|
||||
machine: {
|
||||
name: values.machine.devicePath,
|
||||
flake: {
|
||||
loc: values.machine.flake,
|
||||
},
|
||||
},
|
||||
mode: "format",
|
||||
disks: { "main": values.disk },
|
||||
system_config: {
|
||||
language: values.language,
|
||||
keymap: values.keymap,
|
||||
ssh_keys_path: values.sshKeys,
|
||||
},
|
||||
dry_run: false,
|
||||
write_efi_boot_entries: false,
|
||||
debug: false,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error submitting form:", error);
|
||||
} finally {
|
||||
setIsFlashing(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -70,7 +140,8 @@ export const Flash = () => {
|
||||
<input
|
||||
type="text"
|
||||
class="grow"
|
||||
placeholder="machine.flake"
|
||||
//placeholder="machine.flake"
|
||||
value="git+https://git.clan.lol/clan/clan-core"
|
||||
required
|
||||
{...props}
|
||||
/>
|
||||
@@ -86,7 +157,7 @@ export const Flash = () => {
|
||||
)}
|
||||
</Field>
|
||||
<Field
|
||||
name="machine.name"
|
||||
name="machine.devicePath"
|
||||
validate={[required("This field is required")]}
|
||||
>
|
||||
{(field, props) => (
|
||||
@@ -96,7 +167,7 @@ export const Flash = () => {
|
||||
<input
|
||||
type="text"
|
||||
class="grow"
|
||||
placeholder="machine.name"
|
||||
value="flash-installer"
|
||||
required
|
||||
{...props}
|
||||
/>
|
||||
@@ -120,13 +191,11 @@ export const Flash = () => {
|
||||
class="select select-bordered w-full"
|
||||
{...props}
|
||||
>
|
||||
{/* <span class="material-icons">devices</span> */}
|
||||
<option disabled>Select a disk</option>
|
||||
|
||||
<option value="" disabled>Select a disk</option>
|
||||
<For each={devices?.blockdevices}>
|
||||
{(device) => (
|
||||
<option value={device.name}>
|
||||
{device.name} / {device.size} bytes
|
||||
<option value={device.path}>
|
||||
{device.path} -- {device.size} bytes
|
||||
</option>
|
||||
)}
|
||||
</For>
|
||||
@@ -147,8 +216,106 @@ export const Flash = () => {
|
||||
</>
|
||||
)}
|
||||
</Field>
|
||||
<button class="btn btn-error" type="submit">
|
||||
<span class="material-icons">bolt</span>Flash Installer
|
||||
<Field name="language" validate={[required("This field is required")]}>
|
||||
{(field, props) => (
|
||||
<>
|
||||
<label class="form-control input-bordered flex w-full items-center gap-2">
|
||||
<select
|
||||
required
|
||||
class="select select-bordered w-full"
|
||||
{...props}
|
||||
>
|
||||
<option>en_US.UTF-8</option>
|
||||
<For each={languages}>
|
||||
{(language) => (
|
||||
<option value={language}>
|
||||
{language}
|
||||
</option>
|
||||
)}
|
||||
</For>
|
||||
</select>
|
||||
<div class="label">
|
||||
{isFetchingLanguages && (
|
||||
<span class="label-text-alt">
|
||||
<span class="loading loading-bars">en_US.UTF-8</span>
|
||||
</span>
|
||||
)}
|
||||
{field.error && (
|
||||
<span class="label-text-alt font-bold text-error">
|
||||
{field.error}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</label>
|
||||
</>
|
||||
)}
|
||||
</Field>
|
||||
<Field name="keymap" validate={[required("This field is required")]}>
|
||||
{(field, props) => (
|
||||
<>
|
||||
<label class="form-control input-bordered flex w-full items-center gap-2">
|
||||
<select
|
||||
required
|
||||
class="select select-bordered w-full"
|
||||
{...props}
|
||||
>
|
||||
<option>en</option>
|
||||
<For each={keymaps}>
|
||||
{(keymap) => (
|
||||
<option value={keymap}>
|
||||
{keymap}
|
||||
</option>
|
||||
)}
|
||||
</For>
|
||||
</select>
|
||||
<div class="label">
|
||||
{isFetchingKeymaps && (
|
||||
<span class="label-text
|
||||
-alt">
|
||||
<span class="loading loading-bars"></span>
|
||||
</span>
|
||||
)}
|
||||
{field.error && (
|
||||
<span class="label-text-alt font-bold text-error">
|
||||
{field.error}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</label>
|
||||
</>
|
||||
)}
|
||||
</Field>
|
||||
<Field name="sshKeys" validate={[]} type="string[]">
|
||||
{(field, props) => (
|
||||
<>
|
||||
<label class="input input-bordered flex items-center gap-2">
|
||||
<span class="material-icons">key</span>
|
||||
<input
|
||||
type="text"
|
||||
class="grow"
|
||||
placeholder="Select SSH Key"
|
||||
value={field.value ? field.value.join(", ") : ""}
|
||||
readOnly
|
||||
onClick={() => selectSshPubkey()}
|
||||
required
|
||||
{...props}
|
||||
/>
|
||||
</label>
|
||||
<div class="label">
|
||||
{field.error && (
|
||||
<span class="label-text-alt font-bold text-error">
|
||||
{field.error}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</Field>
|
||||
<button class="btn btn-error" type="submit" disabled={isFlashing()}>
|
||||
{isFlashing()
|
||||
? <span class="loading loading-spinner"></span>
|
||||
: <span class="material-icons">bolt</span>}
|
||||
{isFlashing() ? "Flashing..." : "Flash Installer"}
|
||||
</button>
|
||||
</Form>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {
|
||||
For,
|
||||
Show,
|
||||
type Component,
|
||||
createEffect,
|
||||
createSignal,
|
||||
type Component,
|
||||
For,
|
||||
Show,
|
||||
} from "solid-js";
|
||||
import { route } from "@/src/App";
|
||||
import { OperationResponse, pyApi } from "@/src/api";
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import {
|
||||
type Component,
|
||||
createEffect,
|
||||
createSignal,
|
||||
For,
|
||||
Match,
|
||||
Show,
|
||||
Switch,
|
||||
createEffect,
|
||||
createSignal,
|
||||
type Component,
|
||||
} from "solid-js";
|
||||
import { activeURI, route, setActiveURI, setRoute } from "@/src/App";
|
||||
import { OperationResponse, callApi, pyApi } from "@/src/api";
|
||||
import { callApi, OperationResponse, pyApi } from "@/src/api";
|
||||
import toast from "solid-toast";
|
||||
import { MachineListItem } from "@/src/components/MachineListItem";
|
||||
|
||||
@@ -91,7 +91,8 @@ export const MachineListView: Component = () => {
|
||||
<span class="material-icons ">add</span>
|
||||
</button>
|
||||
</div>
|
||||
{/* <Show when={services()}>
|
||||
{
|
||||
/* <Show when={services()}>
|
||||
{(services) => (
|
||||
<For each={Object.values(services())}>
|
||||
{(service) => (
|
||||
@@ -137,7 +138,8 @@ export const MachineListView: Component = () => {
|
||||
)}
|
||||
</For>
|
||||
)}
|
||||
</Show> */}
|
||||
</Show> */
|
||||
}
|
||||
<Switch>
|
||||
<Match when={loading()}>
|
||||
{/* Loading skeleton */}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { callApi } from "@/src/api";
|
||||
import {
|
||||
SubmitHandler,
|
||||
createForm,
|
||||
required,
|
||||
setValue,
|
||||
SubmitHandler,
|
||||
} from "@modular-forms/solid";
|
||||
import {
|
||||
activeURI,
|
||||
setClanList,
|
||||
setActiveURI,
|
||||
setRoute,
|
||||
clanList,
|
||||
setActiveURI,
|
||||
setClanList,
|
||||
setRoute,
|
||||
} from "@/src/App";
|
||||
import {
|
||||
createEffect,
|
||||
@@ -140,7 +140,7 @@ const ClanDetails = (props: ClanDetailsProps) => {
|
||||
s.filter((v, idx) => {
|
||||
if (v == clan_dir) {
|
||||
setActiveURI(
|
||||
clanList()[idx - 1] || clanList()[idx + 1] || null
|
||||
clanList()[idx - 1] || clanList()[idx + 1] || null,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user