diff --git a/pkgs/webview-ui/app/src/hooks/index.ts b/pkgs/webview-ui/app/src/hooks/index.ts new file mode 100644 index 000000000..629c8e452 --- /dev/null +++ b/pkgs/webview-ui/app/src/hooks/index.ts @@ -0,0 +1,21 @@ +import { callApi } from "../api"; +import { setActiveURI, setClanList } from "../App"; + +export const registerClan = async () => { + try { + const loc = await callApi("open_file", { + file_request: { mode: "select_folder" }, + }); + if (loc.status === "success" && loc.data) { + const data = loc.data[0]; + setClanList((s) => { + const res = new Set([...s, data]); + return Array.from(res); + }); + setActiveURI(data); + return data; + } + } catch (e) { + // + } +}; diff --git a/pkgs/webview-ui/app/src/index.tsx b/pkgs/webview-ui/app/src/index.tsx index b0d5b5f0b..e5a349ebc 100644 --- a/pkgs/webview-ui/app/src/index.tsx +++ b/pkgs/webview-ui/app/src/index.tsx @@ -7,15 +7,12 @@ import { QueryClient, QueryClientProvider } from "@tanstack/solid-query"; import { MachineDetails } from "./routes/machines/[name]/view"; import { Layout } from "./layout/layout"; import { MachineListView } from "./routes/machines/view"; -import { CreateClan } from "./routes/clan/view"; -import { Settings } from "./routes/settings"; -import { EditClanForm } from "./routes/clan/editClan"; +import { ClanList, CreateClan, ClanDetails } from "./routes/clans"; import { Flash } from "./routes/flash/view"; import { CreateMachine } from "./routes/machines/create"; import { HostList } from "./routes/hosts/view"; import { Welcome } from "./routes/welcome"; import { Toaster } from "solid-toast"; -import { Details } from "./routes/clan/details"; const client = new QueryClient(); @@ -74,7 +71,7 @@ export const routes: AppRoute[] = [ { path: "/", label: "Overview", - component: () => , + component: () => , }, { path: "/create", @@ -85,7 +82,7 @@ export const routes: AppRoute[] = [ path: "/:id", label: "Details", hidden: true, - component: () =>
, + component: () => , }, ], }, diff --git a/pkgs/webview-ui/app/src/layout/header.tsx b/pkgs/webview-ui/app/src/layout/header.tsx index cb91644a5..afb39df0c 100644 --- a/pkgs/webview-ui/app/src/layout/header.tsx +++ b/pkgs/webview-ui/app/src/layout/header.tsx @@ -64,11 +64,18 @@ export const Header = (props: HeaderProps) => {
- - - + + {(d) => ( + + + + )} +
); diff --git a/pkgs/webview-ui/app/src/routes/clan/details.tsx b/pkgs/webview-ui/app/src/routes/clan/details.tsx deleted file mode 100644 index 748d6b0aa..000000000 --- a/pkgs/webview-ui/app/src/routes/clan/details.tsx +++ /dev/null @@ -1,210 +0,0 @@ -import { callApi, SuccessQuery } from "@/src/api"; -import { BackButton } from "@/src/components/BackButton"; -import { useParams } from "@solidjs/router"; -import { createQuery } from "@tanstack/solid-query"; -import { createSignal, For, Match, Switch } from "solid-js"; -import { Show } from "solid-js"; -import { - createForm, - FieldValues, - getValue, - getValues, - setValue, -} from "@modular-forms/solid"; -import { TextInput } from "@/src/components/TextInput"; -import toast from "solid-toast"; - -type AdminData = SuccessQuery<"get_admin_service">["data"]; - -interface ClanDetailsProps { - admin: AdminData; - base_url: string; -} -interface AdminSettings extends FieldValues { - allowedKeys: { name: string; value: string }[]; -} - -const ClanDetails = (props: ClanDetailsProps) => { - const items = () => - Object.entries( - (props.admin?.config?.allowedKeys as Record) || {}, - ); - const [formStore, { Form, Field }] = createForm({ - initialValues: { - allowedKeys: items().map(([name, value]) => ({ name, value })), - }, - }); - - const [keys, setKeys] = createSignal<1[]>( - new Array(items().length || 1).fill(1), - ); - - const handleSubmit = async (values: AdminSettings) => { - console.log("submitting", values, getValues(formStore)); - - const r = await callApi("set_admin_service", { - base_url: props.base_url, - allowed_keys: values.allowedKeys.reduce( - (acc, curr) => ({ ...acc, [curr.name]: curr.value }), - {}, - ), - }); - if (r.status === "success") { - toast.success("Successfully updated admin settings"); - } - if (r.status === "error") { - toast.error(`Failed to update admin settings: ${r.errors[0].message}`); - } - }; - - return ( -
- Clan Admin Settings -
-
- - Each of the following keys can be used to authenticate on any - machine - - - {(name, idx) => ( - <> - - {(field, props) => ( - key - ), - }} - value={field.value ?? ""} - error={field.error} - class="col-span-4" - required - /> - )} - - - {(field, props) => ( - <> - - - - - - )} - - - - )} - -
-
- - -
-
-
- ); -}; - -export const Details = () => { - const params = useParams(); - const clan_dir = window.atob(params.id); - const query = createQuery(() => ({ - queryKey: [clan_dir, "get_admin_service"], - queryFn: async () => { - const result = await callApi("get_admin_service", { - base_url: clan_dir, - }); - if (result.status === "error") throw new Error("Failed to fetch data"); - return result.data || null; - }, - })); - - return ( -
- - } - > - - - {(d) => } - - - -
- ); -}; diff --git a/pkgs/webview-ui/app/src/routes/clan/editClan.tsx b/pkgs/webview-ui/app/src/routes/clan/editClan.tsx deleted file mode 100644 index fc437c409..000000000 --- a/pkgs/webview-ui/app/src/routes/clan/editClan.tsx +++ /dev/null @@ -1,175 +0,0 @@ -import { callApi, OperationResponse, pyApi } from "@/src/api"; -import { Accessor, Match, Show, Switch } from "solid-js"; -import { - createForm, - required, - reset, - SubmitHandler, -} from "@modular-forms/solid"; -import toast from "solid-toast"; -import { createQuery } from "@tanstack/solid-query"; - -type CreateForm = Meta; - -interface EditClanFormProps { - directory: Accessor; - done: () => void; -} -export const EditClanForm = (props: EditClanFormProps) => { - const { directory } = props; - const details = createQuery(() => ({ - queryKey: [directory(), "meta"], - queryFn: async () => { - const result = await callApi("show_clan_meta", { uri: directory() }); - if (result.status === "error") throw new Error("Failed to fetch data"); - return result.data; - }, - })); - - return ( - - - {(data) => ( - - )} - - - ); -}; - -interface FinalEditClanFormProps { - initial: CreateForm; - directory: string; - done: () => void; -} -export const FinalEditClanForm = (props: FinalEditClanFormProps) => { - const [formStore, { Form, Field }] = createForm({ - initialValues: props.initial, - }); - - const handleSubmit: SubmitHandler = async (values, event) => { - await toast.promise( - (async () => { - await callApi("update_clan_meta", { - options: { - directory: props.directory, - meta: values, - }, - }); - })(), - { - loading: "Updating clan...", - success: "Clan Successfully updated", - error: "Failed to update clan", - }, - ); - props.done(); - }; - - return ( -
-
- - {(field, props) => ( - <> -
- - group - - } - > - {(icon) => ( - Clan Logo - )} - -
- - )} -
-
- - {(field, props) => ( - - )} - - - {(field, props) => ( - - )} - - { -
- -
- } -
-
-
- ); -}; - -type Meta = Extract< - OperationResponse<"show_clan_meta">, - { status: "success" } ->["data"]; diff --git a/pkgs/webview-ui/app/src/routes/clan/view.tsx b/pkgs/webview-ui/app/src/routes/clan/view.tsx deleted file mode 100644 index 2226004c8..000000000 --- a/pkgs/webview-ui/app/src/routes/clan/view.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { ClanForm } from "./clanDetails"; - -export const CreateClan = () => { - return ( -
- -
- ); -}; diff --git a/pkgs/webview-ui/app/src/routes/clan/clanDetails.tsx b/pkgs/webview-ui/app/src/routes/clans/create.tsx similarity index 99% rename from pkgs/webview-ui/app/src/routes/clan/clanDetails.tsx rename to pkgs/webview-ui/app/src/routes/clans/create.tsx index 0e33ffeb3..f47367b23 100644 --- a/pkgs/webview-ui/app/src/routes/clan/clanDetails.tsx +++ b/pkgs/webview-ui/app/src/routes/clans/create.tsx @@ -15,7 +15,7 @@ type CreateForm = Meta & { template: string; }; -export const ClanForm = () => { +export const CreateClan = () => { const [formStore, { Form, Field }] = createForm({ initialValues: { name: "", diff --git a/pkgs/webview-ui/app/src/routes/clans/details.tsx b/pkgs/webview-ui/app/src/routes/clans/details.tsx new file mode 100644 index 000000000..67a93ad0b --- /dev/null +++ b/pkgs/webview-ui/app/src/routes/clans/details.tsx @@ -0,0 +1,394 @@ +import { callApi, SuccessQuery } from "@/src/api"; +import { BackButton } from "@/src/components/BackButton"; +import { useParams } from "@solidjs/router"; +import { + createQuery, + QueryClient, + useQueryClient, +} from "@tanstack/solid-query"; +import { createSignal, For, Match, Switch } from "solid-js"; +import { Show } from "solid-js"; +import { + createForm, + FieldValues, + getValue, + getValues, + required, + setValue, + SubmitHandler, +} from "@modular-forms/solid"; +import { TextInput } from "@/src/components/TextInput"; +import toast from "solid-toast"; + +interface AdminModuleFormProps { + admin: AdminData; + base_url: string; +} +interface AdminSettings extends FieldValues { + allowedKeys: { name: string; value: string }[]; +} + +interface EditClanFormProps { + initial: GeneralData; + directory: string; +} + +const EditClanForm = (props: EditClanFormProps) => { + const [formStore, { Form, Field }] = createForm({ + initialValues: props.initial, + }); + const queryClient = useQueryClient(); + + const handleSubmit: SubmitHandler = async (values, event) => { + await toast.promise( + (async () => { + await callApi("update_clan_meta", { + options: { + directory: props.directory, + meta: values, + }, + }); + })(), + { + loading: "Updating clan...", + success: "Clan Successfully updated", + error: "Failed to update clan", + }, + ); + queryClient.invalidateQueries({ + queryKey: [props.directory, "meta"], + }); + }; + const curr_name = () => props.initial.name; + + return ( +
+ + {(field) => ( + <> +
+
+
{curr_name()}'s
+
Wide settings
+
+
+
+ + group + + } + > + {(icon) => ( + Clan Logo + )} + +
+ + )} +
+
+ General + + {(field, props) => ( + + )} + + + {(field, props) => ( + + )} + + { +
+ +
+ } +
+
+ ); +}; + +const AdminModuleForm = (props: AdminModuleFormProps) => { + const items = () => + Object.entries( + (props.admin?.config?.allowedKeys as Record) || {}, + ); + const [formStore, { Form, Field }] = createForm({ + initialValues: { + allowedKeys: items().map(([name, value]) => ({ name, value })), + }, + }); + const queryClient = useQueryClient(); + + const [keys, setKeys] = createSignal<1[]>( + new Array(items().length || 1).fill(1), + ); + + const handleSubmit = async (values: AdminSettings) => { + console.log("submitting", values, getValues(formStore)); + + const r = await callApi("set_admin_service", { + base_url: props.base_url, + allowed_keys: values.allowedKeys.reduce( + (acc, curr) => ({ ...acc, [curr.name]: curr.value }), + {}, + ), + }); + if (r.status === "success") { + toast.success("Successfully updated admin settings"); + } + if (r.status === "error") { + toast.error(`Failed to update admin settings: ${r.errors[0].message}`); + } + queryClient.invalidateQueries({ + queryKey: [props.base_url, "get_admin_service"], + }); + }; + + return ( +
+
+ Administration +
+ + Each of the following keys can be used to authenticate on machines + + + {(name, idx) => ( + <> + + {(field, props) => ( + key + ), + }} + value={field.value ?? ""} + error={field.error} + class="col-span-4" + required + /> + )} + + + {(field, props) => ( + <> + + + + + + )} + + + + )} + +
+ +
+
+ { +
+ +
+ } +
+
+ ); +}; + +type GeneralData = SuccessQuery<"show_clan_meta">["data"]; +type AdminData = SuccessQuery<"get_admin_service">["data"]; + +export const ClanDetails = () => { + const params = useParams(); + const clan_dir = window.atob(params.id); + // Fetch general meta data + const clanQuery = createQuery(() => ({ + queryKey: [clan_dir, "meta"], + queryFn: async () => { + const result = await callApi("show_clan_meta", { uri: clan_dir }); + if (result.status === "error") throw new Error("Failed to fetch data"); + return result.data; + }, + })); + // Fetch admin settings + const adminQuery = createQuery(() => ({ + queryKey: [clan_dir, "get_admin_service"], + queryFn: async () => { + const result = await callApi("get_admin_service", { + base_url: clan_dir, + }); + if (result.status === "error") throw new Error("Failed to fetch data"); + return result.data || null; + }, + })); + + return ( +
+ + + +
+ } + > + + + {(d) => } + + + +
+ + + + } + > + + + {(d) => } + + + + + ); +}; diff --git a/pkgs/webview-ui/app/src/routes/clans/index.ts b/pkgs/webview-ui/app/src/routes/clans/index.ts new file mode 100644 index 000000000..7c0e879c8 --- /dev/null +++ b/pkgs/webview-ui/app/src/routes/clans/index.ts @@ -0,0 +1,3 @@ +export * from "./list"; +export * from "./create"; +export * from "./details"; diff --git a/pkgs/webview-ui/app/src/routes/settings/index.tsx b/pkgs/webview-ui/app/src/routes/clans/list.tsx similarity index 59% rename from pkgs/webview-ui/app/src/routes/settings/index.tsx rename to pkgs/webview-ui/app/src/routes/clans/list.tsx index 629d6fff0..00b581e78 100644 --- a/pkgs/webview-ui/app/src/routes/settings/index.tsx +++ b/pkgs/webview-ui/app/src/routes/clans/list.tsx @@ -4,39 +4,14 @@ import { createSignal, For, Match, Setter, Show, Switch } from "solid-js"; import { createQuery } from "@tanstack/solid-query"; import { useFloating } from "@/src/floating"; import { autoUpdate, flip, hide, offset, shift } from "@floating-ui/dom"; -import { EditClanForm } from "../clan/editClan"; import { useNavigate, A } from "@solidjs/router"; -import { fileURLToPath } from "url"; +import { registerClan } from "@/src/hooks"; -export const registerClan = async () => { - try { - const loc = await callApi("open_file", { - file_request: { mode: "select_folder" }, - }); - if (loc.status === "success" && loc.data) { - const data = loc.data[0]; - setClanList((s) => { - const res = new Set([...s, data]); - return Array.from(res); - }); - setActiveURI(data); - // setRoute((r) => { - // if (r === "welcome") return "machines"; - // return r; - // }); - return data; - } - } catch (e) { - // - } -}; - -interface ClanDetailsProps { +interface ClanItemProps { clan_dir: string; - setEditURI: Setter; } -const ClanDetails = (props: ClanDetailsProps) => { - const { clan_dir, setEditURI } = props; +const ClanItem = (props: ClanItemProps) => { + const { clan_dir } = props; const details = createQuery(() => ({ queryKey: [clan_dir, "meta"], @@ -46,7 +21,7 @@ const ClanDetails = (props: ClanDetailsProps) => { return result.data; }, })); - + const navigate = useNavigate(); const [reference, setReference] = createSignal(); const [floating, setFloating] = createSignal(); @@ -87,10 +62,8 @@ const ClanDetails = (props: ClanDetailsProps) => {
@@ -157,62 +130,37 @@ const ClanDetails = (props: ClanDetailsProps) => { ); }; -export const Settings = () => { - const [editURI, setEditURI] = createSignal(null); - +export const ClanList = () => { const navigate = useNavigate(); return (
- - - {(uri) => ( - { - setEditURI(null); - }} - /> - )} - - -
-
-
- Registered Clans -
-
- - - - - - -
-
-
- - {(value) => ( - - )} - -
+
+
+
Registered Clans
+
+ + + + + +
- - +
+
+ + {(value) => } + +
+
); }; diff --git a/pkgs/webview-ui/app/src/routes/welcome/index.tsx b/pkgs/webview-ui/app/src/routes/welcome/index.tsx index f82ea5f6b..b6dc72434 100644 --- a/pkgs/webview-ui/app/src/routes/welcome/index.tsx +++ b/pkgs/webview-ui/app/src/routes/welcome/index.tsx @@ -1,5 +1,5 @@ -import { setActiveURI, setClanList } from "@/src/App"; -import { registerClan } from "../settings"; +import { setActiveURI } from "@/src/App"; +import { registerClan } from "@/src/hooks"; import { useNavigate } from "@solidjs/router"; export const Welcome = () => {