Merge pull request 'Webview: migrate create clan form to async api' (#1757) from hsjobeki/clan-core:hsjobeki-main into main

This commit is contained in:
clan-bot
2024-07-15 18:44:32 +00:00
5 changed files with 111 additions and 98 deletions

View File

@@ -7,6 +7,7 @@ import { BlockDevicesView } from "./routes/blockdevices/view";
import { Flash } from "./routes/flash/view"; import { Flash } from "./routes/flash/view";
import { Settings } from "./routes/settings"; import { Settings } from "./routes/settings";
import { Welcome } from "./routes/welcome"; import { Welcome } from "./routes/welcome";
import { Deploy } from "./routes/deploy";
export type Route = keyof typeof routes; export type Route = keyof typeof routes;
@@ -51,6 +52,11 @@ export const routes = {
label: "welcome", label: "welcome",
icon: "settings", icon: "settings",
}, },
deploy: {
child: Deploy,
label: "deploy",
icon: "content_copy",
},
}; };
interface RouterProps { interface RouterProps {

View File

@@ -1,6 +1,21 @@
import { createQuery } from "@tanstack/solid-query";
import { activeURI, setRoute } from "../App"; import { activeURI, setRoute } from "../App";
import { callApi } from "../api";
import { Show } from "solid-js";
export const Header = () => { export const Header = () => {
const { isLoading, data } = createQuery(() => ({
queryKey: [`${activeURI()}:meta`],
queryFn: async () => {
const currUri = activeURI();
if (currUri) {
const result = await callApi("show_clan_meta", { uri: currUri });
if (result.status === "error") throw new Error("Failed to fetch data");
return result.data;
}
},
}));
return ( return (
<div class="navbar bg-base-100"> <div class="navbar bg-base-100">
<div class="flex-none"> <div class="flex-none">
@@ -14,7 +29,16 @@ export const Header = () => {
</span> </span>
</div> </div>
<div class="flex-1"> <div class="flex-1">
<a class="text-xl">{activeURI()}</a> <div class="tooltip tooltip-right" data-tip={data?.name || activeURI()}>
<div class="avatar placeholder online mx-4">
<div class="w-10 rounded-full bg-slate-700 text-neutral-content">
<span class="text-xl">C</span>
<Show when={data?.name}>
{(name) => <span class="text-xl">{name()}</span>}
</Show>
</div>
</div>
</div>
</div> </div>
<div class="flex-none"> <div class="flex-none">
<span class="tooltip tooltip-bottom" data-tip="Settings"> <span class="tooltip tooltip-bottom" data-tip="Settings">

View File

@@ -1,26 +1,14 @@
import { OperationResponse, pyApi } from "@/src/api"; import { OperationResponse, callApi, pyApi } from "@/src/api";
import { import { Show } from "solid-js";
For,
JSX,
Match,
Show,
Switch,
createEffect,
createSignal,
} from "solid-js";
import { import {
SubmitHandler, SubmitHandler,
createForm, createForm,
required, required,
custom, reset,
} from "@modular-forms/solid"; } from "@modular-forms/solid";
import toast from "solid-toast"; import toast from "solid-toast";
import { setActiveURI, setRoute } from "@/src/App"; import { setActiveURI, setRoute } from "@/src/App";
interface ClanDetailsProps {
directory: string;
}
type CreateForm = Meta & { type CreateForm = Meta & {
template_url: string; template_url: string;
}; };
@@ -28,69 +16,44 @@ type CreateForm = Meta & {
export const ClanForm = () => { export const ClanForm = () => {
const [formStore, { Form, Field }] = createForm<CreateForm>({ const [formStore, { Form, Field }] = createForm<CreateForm>({
initialValues: { initialValues: {
name: "",
description: "",
template_url: "git+https://git.clan.lol/clan/clan-core#templates.minimal", template_url: "git+https://git.clan.lol/clan/clan-core#templates.minimal",
}, },
}); });
const handleSubmit: SubmitHandler<CreateForm> = async (values, event) => { const handleSubmit: SubmitHandler<CreateForm> = async (values, event) => {
const { template_url, ...meta } = values; const { template_url, ...meta } = values;
pyApi.open_file.dispatch({
file_request: {
mode: "save",
},
op_key: "create_clan", const response = await callApi("open_file", {
file_request: { mode: "save" },
}); });
// await new Promise<void>((done) => { if (response.status !== "success") {
// pyApi.open_file.receive((r) => { toast.error("Cannot select clan directory");
// if (r.op_key !== "create_clan") { return;
// done(); }
// return; const target_dir = response?.data;
// } if (!target_dir) {
// if (r.status !== "success") { toast.error("Cannot select clan directory");
// toast.error("Cannot select clan directory"); return;
// done(); }
// return;
// }
// const target_dir = r?.data;
// if (!target_dir) {
// toast.error("Cannot select clan directory");
// done();
// return;
// }
// console.log({ formStore }); await toast.promise(
(async () => {
// toast.promise( await callApi("create_clan", {
// new Promise<void>((resolve, reject) => { options: { directory: target_dir, meta, template_url },
// pyApi.create_clan.receive((r) => { });
// done(); setActiveURI(target_dir);
// if (r.status === "error") { setRoute("machines");
// reject(); })(),
// console.error(r.errors); {
// return; loading: "Creating clan...",
// } success: "Clan Successfully Created",
// resolve(); error: "Failed to create clan",
}
// // Navigate to the new clan );
// setCurrClanURI(target_dir); reset(formStore);
// setRoute("machines");
// });
// pyApi.create_clan.dispatch({
// options: { directory: target_dir, meta, template_url },
// op_key: "create_clan",
// });
// }),
// {
// loading: "Creating clan...",
// success: "Clan Successfully Created",
// error: "Failed to create clan",
// }
// );
// });
// });
}; };
return ( return (
@@ -135,6 +98,7 @@ export const ClanForm = () => {
<input <input
{...props} {...props}
disabled={formStore.submitting}
required required
placeholder="Clan Name" placeholder="Clan Name"
class="input input-bordered" class="input input-bordered"
@@ -158,6 +122,7 @@ export const ClanForm = () => {
<input <input
{...props} {...props}
disabled={formStore.submitting}
required required
type="text" type="text"
placeholder="Some words about your clan" placeholder="Some words about your clan"
@@ -188,6 +153,7 @@ export const ClanForm = () => {
<input <input
{...props} {...props}
required required
disabled={formStore.submitting}
type="text" type="text"
placeholder="Template to use" placeholder="Template to use"
class="input input-bordered" class="input input-bordered"

View File

@@ -0,0 +1,6 @@
import { callApi } from "@/src/api";
import { createQuery } from "@tanstack/solid-query";
export const Deploy = () => {
return <div>Deloy view</div>;
};

View File

@@ -1,6 +1,7 @@
import { route } from "@/src/App"; import { route } from "@/src/App";
import { OperationArgs, OperationResponse, pyApi } from "@/src/api"; import { OperationArgs, OperationResponse, callApi, pyApi } from "@/src/api";
import { SubmitHandler, createForm, required } from "@modular-forms/solid"; import { SubmitHandler, createForm, required } from "@modular-forms/solid";
import { createQuery } from "@tanstack/solid-query";
import { For, createSignal } from "solid-js"; import { For, createSignal } from "solid-js";
import { effect } from "solid-js/web"; import { effect } from "solid-js/web";
@@ -28,36 +29,35 @@ type BlockDevices = Extract<
export const Flash = () => { export const Flash = () => {
const [formStore, { Form, Field }] = createForm<FlashFormValues>({}); const [formStore, { Form, Field }] = createForm<FlashFormValues>({});
const [devices, setDevices] = createSignal<BlockDevices>([]); const {
// pyApi.show_block_devices.receive((r) => { data: devices,
// console.log("block devices", r); refetch: loadDevices,
// if (r.status === "success") { isFetching,
// setDevices(r.data.blockdevices); } = createQuery(() => ({
// } queryKey: ["TanStack Query"],
// }); queryFn: async () => {
const result = await callApi("show_block_devices", {});
if (result.status === "error") throw new Error("Failed to fetch data");
return result.data;
},
staleTime: 1000 * 60 * 1, // 1 minutes
}));
const handleSubmit: SubmitHandler<FlashFormValues> = (values, event) => { const handleSubmit = async (values: FlashFormValues) => {
// pyApi.open_file.dispatch({ file_request: { mode: "save" } }); // TODO: Rework Flash machine API
// pyApi.open_file.receive((r) => { // Its unusable in its current state
// if (r.status === "success") { // await callApi("flash_machine", {
// if (r.data) { // machine: {
// pyApi.create_clan.dispatch({ // name: "",
// options: { directory: r.data, meta: values }, // },
// }); // disks: {values.disk },
// } // dry_run: true,
// return;
// }
// }); // });
console.log("submit", values); console.log("submit", values);
}; };
// effect(() => {
// if (route() === "flash") {
// pyApi.show_block_devices.dispatch({});
// }
// });
return ( return (
<div class=""> <div class="px-2">
<Form onSubmit={handleSubmit}> <Form onSubmit={handleSubmit}>
<Field <Field
name="machine.flake" name="machine.flake"
@@ -70,7 +70,7 @@ export const Flash = () => {
<input <input
type="text" type="text"
class="grow" class="grow"
placeholder="Clan URI" placeholder="machine.flake"
required required
{...props} {...props}
/> />
@@ -96,7 +96,7 @@ export const Flash = () => {
<input <input
type="text" type="text"
class="grow" class="grow"
placeholder="Machine Name" placeholder="machine.name"
required required
{...props} {...props}
/> />
@@ -115,9 +115,15 @@ export const Flash = () => {
{(field, props) => ( {(field, props) => (
<> <>
<label class="form-control input-bordered flex w-full items-center gap-2"> <label class="form-control input-bordered flex w-full items-center gap-2">
<select required class="select w-full" {...props}> <select
required
class="select select-bordered w-full"
{...props}
>
{/* <span class="material-icons">devices</span> */} {/* <span class="material-icons">devices</span> */}
<For each={devices()}> <option disabled>Select a disk</option>
<For each={devices?.blockdevices}>
{(device) => ( {(device) => (
<option value={device.name}> <option value={device.name}>
{device.name} / {device.size} bytes {device.name} / {device.size} bytes
@@ -126,6 +132,11 @@ export const Flash = () => {
</For> </For>
</select> </select>
<div class="label"> <div class="label">
{isFetching && (
<span class="label-text-alt">
<span class="loading loading-bars"></span>
</span>
)}
{field.error && ( {field.error && (
<span class="label-text-alt font-bold text-error"> <span class="label-text-alt font-bold text-error">
{field.error} {field.error}