Merge pull request 'UI/install: add loading animation' (#4723) from install-ui into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4723
This commit is contained in:
@@ -22,7 +22,7 @@ export interface ButtonProps
|
||||
startIcon?: IconVariant;
|
||||
endIcon?: IconVariant;
|
||||
class?: string;
|
||||
onAction?: Action;
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
const iconSizes: Record<Size, string> = {
|
||||
@@ -40,31 +40,12 @@ export const Button = (props: ButtonProps) => {
|
||||
"startIcon",
|
||||
"endIcon",
|
||||
"class",
|
||||
"onAction",
|
||||
"loading",
|
||||
]);
|
||||
|
||||
const size = local.size || "default";
|
||||
const hierarchy = local.hierarchy || "primary";
|
||||
|
||||
const [loading, setLoading] = createSignal(false);
|
||||
|
||||
const onClick = async () => {
|
||||
if (!local.onAction) {
|
||||
console.error("this should not be possible");
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
await local.onAction();
|
||||
} catch (error) {
|
||||
console.error("Error while executing action", error);
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const iconSize = iconSizes[local.size || "default"];
|
||||
|
||||
const loadingClass =
|
||||
@@ -81,16 +62,19 @@ export const Button = (props: ButtonProps) => {
|
||||
hierarchy,
|
||||
{
|
||||
icon: local.icon,
|
||||
loading: loading(),
|
||||
loading: props.loading,
|
||||
ghost: local.ghost,
|
||||
},
|
||||
)}
|
||||
onClick={local.onAction ? onClick : undefined}
|
||||
onClick={props.onClick}
|
||||
{...other}
|
||||
>
|
||||
<Loader
|
||||
hierarchy={hierarchy}
|
||||
class={cx({ [idleClass]: !loading(), [loadingClass]: loading() })}
|
||||
class={cx({
|
||||
[idleClass]: !props.loading,
|
||||
[loadingClass]: props.loading,
|
||||
})}
|
||||
/>
|
||||
|
||||
{local.startIcon && (
|
||||
|
||||
@@ -99,8 +99,12 @@ const welcome = (props: {
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [loading, setLoading] = createSignal(false);
|
||||
|
||||
const selectFolder = async () => {
|
||||
setLoading(true);
|
||||
const uri = await selectClanFolder();
|
||||
setLoading(false);
|
||||
navigateToClan(navigate, uri);
|
||||
};
|
||||
|
||||
@@ -148,7 +152,12 @@ const welcome = (props: {
|
||||
</Typography>
|
||||
<Divider orientation="horizontal" />
|
||||
</div>
|
||||
<Button hierarchy="primary" ghost={true} onAction={selectFolder}>
|
||||
<Button
|
||||
hierarchy="primary"
|
||||
ghost={true}
|
||||
loading={loading()}
|
||||
onClick={selectFolder}
|
||||
>
|
||||
Select folder
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
createForm,
|
||||
FieldValues,
|
||||
getError,
|
||||
getValue,
|
||||
SubmitHandler,
|
||||
valiForm,
|
||||
} from "@modular-forms/solid";
|
||||
@@ -13,7 +14,7 @@ import { getStepStore, useStepper } from "@/src/hooks/stepper";
|
||||
import { InstallSteps, InstallStoreType, PromptValues } from "../install";
|
||||
import { TextInput } from "@/src/components/Form/TextInput";
|
||||
import { Alert } from "@/src/components/Alert/Alert";
|
||||
import { For, Match, Show, Switch } from "solid-js";
|
||||
import { createSignal, For, Match, Show, Switch } from "solid-js";
|
||||
import { Divider } from "@/src/components/Divider/Divider";
|
||||
import { Orienter } from "@/src/components/Form/Orienter";
|
||||
import { Button } from "@/src/components/Button/Button";
|
||||
@@ -29,6 +30,7 @@ import {
|
||||
import { useClanURI } from "@/src/hooks/clan";
|
||||
import { useApiClient } from "@/src/hooks/ApiClient";
|
||||
import { ProcessMessage, useNotifyOrigin } from "@/src/hooks/notify";
|
||||
import { Loader } from "@/src/components/Loader/Loader";
|
||||
|
||||
export const InstallHeader = (props: { machineName: string }) => {
|
||||
return (
|
||||
@@ -58,8 +60,9 @@ const ConfigureAddress = () => {
|
||||
},
|
||||
});
|
||||
|
||||
const [isReachable, setIsReachable] = createSignal<string | null>(null);
|
||||
|
||||
const client = useApiClient();
|
||||
const clanUri = useClanURI();
|
||||
// TODO: push values to the parent form Store
|
||||
const handleSubmit: SubmitHandler<ConfigureAdressForm> = async (
|
||||
values,
|
||||
@@ -72,6 +75,24 @@ const ConfigureAddress = () => {
|
||||
stepSignal.next();
|
||||
};
|
||||
|
||||
const tryReachable = async () => {
|
||||
const address = getValue(formStore, "targetHost");
|
||||
if (!address) {
|
||||
return;
|
||||
}
|
||||
|
||||
const call = client.fetch("check_machine_ssh_login", {
|
||||
remote: {
|
||||
address,
|
||||
},
|
||||
});
|
||||
const result = await call.result;
|
||||
console.log("SSH login check result:", result);
|
||||
if (result.status === "success") {
|
||||
setIsReachable(address);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Form onSubmit={handleSubmit} class="h-full">
|
||||
<StepLayout
|
||||
@@ -98,12 +119,28 @@ const ConfigureAddress = () => {
|
||||
)}
|
||||
</Field>
|
||||
</Fieldset>
|
||||
<Button
|
||||
disabled={!getValue(formStore, "targetHost")}
|
||||
endIcon="ArrowRight"
|
||||
onClick={tryReachable}
|
||||
hierarchy="secondary"
|
||||
>
|
||||
Test Connection
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
footer={
|
||||
<div class="flex justify-between">
|
||||
<BackButton />
|
||||
<NextButton type="submit">Next</NextButton>
|
||||
<NextButton
|
||||
type="submit"
|
||||
disabled={
|
||||
!isReachable() ||
|
||||
isReachable() !== getValue(formStore, "targetHost")
|
||||
}
|
||||
>
|
||||
Next
|
||||
</NextButton>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
@@ -157,15 +194,18 @@ const CheckHardware = () => {
|
||||
Hardware Report
|
||||
</Typography>
|
||||
<Button
|
||||
disabled={hardwareQuery.isLoading}
|
||||
hierarchy="secondary"
|
||||
startIcon="Report"
|
||||
onClick={handleUpdateSummary}
|
||||
class="flex gap-3"
|
||||
loading={hardwareQuery.isFetching}
|
||||
>
|
||||
Update hardware report
|
||||
</Button>
|
||||
</Orienter>
|
||||
<Divider orientation="horizontal" />
|
||||
<Show when={hardwareQuery.isLoading}>Loading...</Show>
|
||||
|
||||
<Show when={hardwareQuery.data}>
|
||||
{(d) => (
|
||||
<Alert
|
||||
@@ -545,13 +585,16 @@ const InstallSummary = () => {
|
||||
<StepLayout
|
||||
body={
|
||||
<div class="flex flex-col gap-4">
|
||||
<Fieldset legend="Address Configuration">
|
||||
<Fieldset legend="Machine">
|
||||
<Orienter orientation="horizontal">
|
||||
{/* TOOD: Display the values emited from previous steps */}
|
||||
<Display label="Target" value="flash-installer.local" />
|
||||
<Display label="Name" value={store.install.machineName} />
|
||||
</Orienter>
|
||||
<Divider orientation="horizontal" />
|
||||
<Orienter orientation="horizontal">
|
||||
<Display label="Address" value={store.install.targetHost} />
|
||||
</Orienter>
|
||||
</Fieldset>
|
||||
<Fieldset legend="Disk Configuration">
|
||||
<Fieldset legend="Disk">
|
||||
<Orienter orientation="horizontal">
|
||||
<Display label="Disk Schema" value="Single" />
|
||||
</Orienter>
|
||||
|
||||
Reference in New Issue
Block a user