Merge pull request 'UI/treewide: replace all {button,icon} component, other minor fixes' (#2503) from hsjobeki/clan-core:hsjobeki-main into main
This commit is contained in:
@@ -230,10 +230,18 @@ def construct_value(
|
|||||||
raise ClanError(msg, location=f"{loc}")
|
raise ClanError(msg, location=f"{loc}")
|
||||||
return field_value
|
return field_value
|
||||||
|
|
||||||
|
# Enums
|
||||||
if origin is Enum:
|
if origin is Enum:
|
||||||
if field_value not in origin.__members__:
|
if field_value not in origin.__members__:
|
||||||
msg = f"Expected one of {', '.join(origin.__members__)}, got {field_value}"
|
msg = f"Expected one of {', '.join(origin.__members__)}, got {field_value}"
|
||||||
raise ClanError(msg, location=f"{loc}")
|
raise ClanError(msg, location=f"{loc}")
|
||||||
|
return origin.__members__[field_value] # type: ignore
|
||||||
|
|
||||||
|
if isinstance(t, type) and issubclass(t, Enum):
|
||||||
|
if field_value not in t.__members__:
|
||||||
|
msg = f"Expected one of {', '.join(t.__members__)}, got {field_value}"
|
||||||
|
raise ClanError(msg, location=f"{loc}")
|
||||||
|
return t.__members__[field_value] # type: ignore
|
||||||
|
|
||||||
if origin is Annotated:
|
if origin is Annotated:
|
||||||
(base_type,) = get_args(t)
|
(base_type,) = get_args(t)
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ class ClanError(Exception):
|
|||||||
exception_msg += self.msg
|
exception_msg += self.msg
|
||||||
|
|
||||||
if self.description:
|
if self.description:
|
||||||
exception_msg = f" - {self.description}"
|
exception_msg += f" - {self.description}"
|
||||||
super().__init__(exception_msg)
|
super().__init__(exception_msg)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -102,8 +102,11 @@ class Machine:
|
|||||||
or self.deployment.get("deploymentAddress")
|
or self.deployment.get("deploymentAddress")
|
||||||
)
|
)
|
||||||
if val is None:
|
if val is None:
|
||||||
msg = f"the 'clan.core.networking.targetHost' nixos option is not set for machine '{self.name}'"
|
msg = f"'TargetHost' is not set for machine '{self.name}'"
|
||||||
raise ClanError(msg)
|
raise ClanError(
|
||||||
|
msg,
|
||||||
|
description="See https://docs.clan.lol/getting-started/deploy/#setting-the-target-host for more information.",
|
||||||
|
)
|
||||||
return val
|
return val
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ import {
|
|||||||
import cx from "classnames";
|
import cx from "classnames";
|
||||||
import { Label } from "../base/label";
|
import { Label } from "../base/label";
|
||||||
import { SelectInput } from "../fields/Select";
|
import { SelectInput } from "../fields/Select";
|
||||||
|
import { Button } from "@/src/components/button";
|
||||||
|
import Icon from "@/src/components/icon";
|
||||||
|
|
||||||
function generateDefaults(schema: JSONSchema7): unknown {
|
function generateDefaults(schema: JSONSchema7): unknown {
|
||||||
switch (schema.type) {
|
switch (schema.type) {
|
||||||
@@ -385,15 +387,25 @@ export function ListValueDisplay<T extends FieldValues, R extends ResponseData>(
|
|||||||
<div class="flex w-full items-end gap-2 px-4">
|
<div class="flex w-full items-end gap-2 px-4">
|
||||||
{props.children}
|
{props.children}
|
||||||
<div class="ml-4 min-w-fit pb-4">
|
<div class="ml-4 min-w-fit pb-4">
|
||||||
<button class="btn" onClick={moveItemBy(1)} disabled={topMost()}>
|
<Button
|
||||||
↓
|
variant="light"
|
||||||
</button>
|
onClick={moveItemBy(1)}
|
||||||
<button class="btn" onClick={moveItemBy(-1)} disabled={bottomMost()}>
|
disabled={topMost()}
|
||||||
↑
|
startIcon={<Icon icon="ArrowBottom" />}
|
||||||
</button>
|
class="h-12"
|
||||||
<button class="btn btn-error" onClick={removeItem}>
|
></Button>
|
||||||
x
|
<Button
|
||||||
</button>
|
variant="light"
|
||||||
|
onClick={moveItemBy(-1)}
|
||||||
|
disabled={bottomMost()}
|
||||||
|
class="h-12"
|
||||||
|
startIcon={<Icon icon="ArrowTop" />}
|
||||||
|
></Button>
|
||||||
|
<Button
|
||||||
|
class="h-12"
|
||||||
|
startIcon={<Icon icon="Trash" />}
|
||||||
|
onClick={removeItem}
|
||||||
|
></Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -641,7 +653,14 @@ export function ArrayFields<T extends FieldValues, R extends ResponseData>(
|
|||||||
}}
|
}}
|
||||||
// Button for adding new items
|
// Button for adding new items
|
||||||
components={{
|
components={{
|
||||||
before: <button class="btn">Add ↑</button>,
|
before: (
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
endIcon={<Icon icon={"Plus"} />}
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
}}
|
}}
|
||||||
// Add the new item to the FieldArray
|
// Add the new item to the FieldArray
|
||||||
handleSubmit={(values, event) => {
|
handleSubmit={(values, event) => {
|
||||||
@@ -827,8 +846,10 @@ export function ObjectFields<T extends FieldValues, R extends ResponseData>(
|
|||||||
<span class="text-xl font-semibold">
|
<span class="text-xl font-semibold">
|
||||||
{key}
|
{key}
|
||||||
</span>
|
</span>
|
||||||
<button
|
<Button
|
||||||
class="btn btn-warning btn-sm ml-auto"
|
variant="light"
|
||||||
|
class="ml-auto"
|
||||||
|
size="s"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={(_e) => {
|
onClick={(_e) => {
|
||||||
const copy = {
|
const copy = {
|
||||||
@@ -845,8 +866,8 @@ export function ObjectFields<T extends FieldValues, R extends ResponseData>(
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Remove
|
<Icon icon="Trash" />
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
import { For, Show } from "solid-js";
|
|
||||||
import { activeURI } from "./App";
|
|
||||||
import { createQuery } from "@tanstack/solid-query";
|
|
||||||
import { callApi } from "./api";
|
|
||||||
import { A, RouteSectionProps } from "@solidjs/router";
|
|
||||||
import { AppRoute, routes } from "./index";
|
|
||||||
|
|
||||||
export const Sidebar = (props: RouteSectionProps) => {
|
|
||||||
const clanQuery = createQuery(() => ({
|
|
||||||
queryKey: [activeURI(), "meta"],
|
|
||||||
queryFn: async () => {
|
|
||||||
const curr = activeURI();
|
|
||||||
if (curr) {
|
|
||||||
const result = await callApi("show_clan_meta", { uri: curr });
|
|
||||||
if (result.status === "error") throw new Error("Failed to fetch data");
|
|
||||||
return result.data;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<aside class="w-80 rounded-xl border border-slate-900 bg-slate-800 pb-10">
|
|
||||||
<div class="m-4 flex flex-col text-center capitalize text-white">
|
|
||||||
<span class="text-lg">{clanQuery.data?.name}</span>
|
|
||||||
<span class="text-sm">{clanQuery.data?.description}</span>
|
|
||||||
<RouteMenu class="menu px-4 py-2" routes={routes} />
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const RouteMenu = (props: {
|
|
||||||
class?: string;
|
|
||||||
routes: AppRoute[];
|
|
||||||
prefix?: string;
|
|
||||||
}) => (
|
|
||||||
<ul class={props?.class}>
|
|
||||||
<For each={props.routes.filter((r) => !r.hidden)}>
|
|
||||||
{(route) => (
|
|
||||||
<li>
|
|
||||||
<Show
|
|
||||||
when={route.children}
|
|
||||||
fallback={
|
|
||||||
<A href={[props.prefix, route.path].filter(Boolean).join("")}>
|
|
||||||
<button class="text-white">
|
|
||||||
{route.icon && (
|
|
||||||
<span class="material-icons">{route.icon}</span>
|
|
||||||
)}
|
|
||||||
{route.label}
|
|
||||||
</button>
|
|
||||||
</A>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{(children) => (
|
|
||||||
<details id={`disclosure-${route.label}`} open={true}>
|
|
||||||
<summary class="group">
|
|
||||||
{route.icon && (
|
|
||||||
<span class="material-icons">{route.icon}</span>
|
|
||||||
)}
|
|
||||||
{route.label}
|
|
||||||
</summary>
|
|
||||||
<RouteMenu
|
|
||||||
routes={children()}
|
|
||||||
prefix={[props.prefix, route.path].filter(Boolean).join("")}
|
|
||||||
/>
|
|
||||||
</details>
|
|
||||||
)}
|
|
||||||
</Show>
|
|
||||||
</li>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</ul>
|
|
||||||
);
|
|
||||||
@@ -9,7 +9,7 @@ export const BackButton = () => {
|
|||||||
variant="light"
|
variant="light"
|
||||||
class="w-fit"
|
class="w-fit"
|
||||||
onClick={() => navigate(-1)}
|
onClick={() => navigate(-1)}
|
||||||
startIcon={<Icon icon="ArrowLeft" />}
|
startIcon={<Icon icon="CaretRight" />}
|
||||||
></Button>
|
></Button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { activeURI } from "../App";
|
|||||||
import toast from "solid-toast";
|
import toast from "solid-toast";
|
||||||
import { A, useNavigate } from "@solidjs/router";
|
import { A, useNavigate } from "@solidjs/router";
|
||||||
import { RndThumbnail } from "./noiseThumbnail";
|
import { RndThumbnail } from "./noiseThumbnail";
|
||||||
|
import Icon from "./icon";
|
||||||
|
|
||||||
type MachineDetails = SuccessQuery<"list_inventory_machines">["data"][string];
|
type MachineDetails = SuccessQuery<"list_inventory_machines">["data"][string];
|
||||||
|
|
||||||
@@ -138,7 +139,7 @@ export const MachineListItem = (props: MachineListItemProps) => {
|
|||||||
<div>
|
<div>
|
||||||
<Menu
|
<Menu
|
||||||
popoverid={`menu-${props.name}`}
|
popoverid={`menu-${props.name}`}
|
||||||
label={<span class="material-icons">more_vert</span>}
|
label={<Icon icon={"Expand"} />}
|
||||||
>
|
>
|
||||||
<ul class="menu z-[1] w-52 rounded-box bg-base-100 p-2 shadow">
|
<ul class="menu z-[1] w-52 rounded-box bg-base-100 p-2 shadow">
|
||||||
<li>
|
<li>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
shift,
|
shift,
|
||||||
} from "@floating-ui/dom";
|
} from "@floating-ui/dom";
|
||||||
import cx from "classnames";
|
import cx from "classnames";
|
||||||
|
import { Button } from "./button";
|
||||||
|
|
||||||
interface MenuProps {
|
interface MenuProps {
|
||||||
/**
|
/**
|
||||||
@@ -53,7 +54,8 @@ export const Menu = (props: MenuProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<button
|
<Button
|
||||||
|
variant="light"
|
||||||
popovertarget={props.popoverid}
|
popovertarget={props.popoverid}
|
||||||
popovertargetaction="toggle"
|
popovertargetaction="toggle"
|
||||||
ref={setReference}
|
ref={setReference}
|
||||||
@@ -64,7 +66,7 @@ export const Menu = (props: MenuProps) => {
|
|||||||
{...props.buttonProps}
|
{...props.buttonProps}
|
||||||
>
|
>
|
||||||
{props.label}
|
{props.label}
|
||||||
</button>
|
</Button>
|
||||||
<div
|
<div
|
||||||
popover="auto"
|
popover="auto"
|
||||||
id={props.popoverid}
|
id={props.popoverid}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export const SidebarFlyout = () => {
|
|||||||
<div class="sidebar__flyout">
|
<div class="sidebar__flyout">
|
||||||
<div class="sidebar__flyout__inner">
|
<div class="sidebar__flyout__inner">
|
||||||
<List gapSize="small">
|
<List gapSize="small">
|
||||||
<SidebarListItem href="/settings" title="Settings" />
|
<SidebarListItem href="/clans" title="Settings" />
|
||||||
</List>
|
</List>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export const SidebarHeader = (props: SidebarHeader) => {
|
|||||||
color="primary"
|
color="primary"
|
||||||
inverted={true}
|
inverted={true}
|
||||||
>
|
>
|
||||||
{clanName.slice(0, 1)}
|
{clanName.slice(0, 1).toUpperCase()}
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,11 +8,7 @@
|
|||||||
/* Sidebar Structure */
|
/* Sidebar Structure */
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
min-width: theme(width.72);
|
@apply bg-inv-2 h-full border border-solid border-inv-2 min-w-72 rounded-xl;
|
||||||
height: 100%;
|
|
||||||
background-color: var(--clr-bg-inv-2);
|
|
||||||
border: 1px solid var(--clr-border-inv-2);
|
|
||||||
border-radius: theme(borderRadius.xl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar__body {
|
.sidebar__body {
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ export const Button = (props: ButtonProps) => {
|
|||||||
"inline-flex items-center flex-shrink gap-2 justify-center",
|
"inline-flex items-center flex-shrink gap-2 justify-center",
|
||||||
// Styles
|
// Styles
|
||||||
"border border-solid",
|
"border border-solid",
|
||||||
|
"p-4",
|
||||||
sizePaddings[local.size || "default"],
|
sizePaddings[local.size || "default"],
|
||||||
// Colors
|
// Colors
|
||||||
variantColors[local.variant || "dark"],
|
variantColors[local.variant || "dark"],
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, JSX } from "solid-js";
|
import { Component, JSX, splitProps } from "solid-js";
|
||||||
import ArrowBottom from "@/icons/arrow-bottom.svg";
|
import ArrowBottom from "@/icons/arrow-bottom.svg";
|
||||||
import ArrowLeft from "@/icons/arrow-left.svg";
|
import ArrowLeft from "@/icons/arrow-left.svg";
|
||||||
import ArrowRight from "@/icons/arrow-right.svg";
|
import ArrowRight from "@/icons/arrow-right.svg";
|
||||||
@@ -81,10 +81,18 @@ const Icon: Component<IconProps> = (props) => {
|
|||||||
Trash,
|
Trash,
|
||||||
Update,
|
Update,
|
||||||
};
|
};
|
||||||
|
const [local, iconProps] = splitProps(props, ["icon"]);
|
||||||
|
|
||||||
const IconComponent = icons[props.icon];
|
const IconComponent = icons[local.icon];
|
||||||
return IconComponent ? (
|
return IconComponent ? (
|
||||||
<IconComponent width={16} height={16} viewBox="0 0 48 48" />
|
<IconComponent
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
viewBox="0 0 48 48"
|
||||||
|
// @ts-expect-error: dont know, fix this type nit later
|
||||||
|
ref={iconProps.ref}
|
||||||
|
{...iconProps}
|
||||||
|
/>
|
||||||
) : null;
|
) : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -136,9 +136,10 @@ export const routes: AppRoute[] = [
|
|||||||
{
|
{
|
||||||
path: "/welcome",
|
path: "/welcome",
|
||||||
label: "",
|
label: "",
|
||||||
hidden: true,
|
hidden: false,
|
||||||
component: () => <Welcome />,
|
component: () => <Welcome />,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
path: "/api_testing",
|
path: "/api_testing",
|
||||||
label: "api_testing",
|
label: "api_testing",
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import { callApi } from "../api";
|
|||||||
import { Accessor, Show } from "solid-js";
|
import { Accessor, Show } from "solid-js";
|
||||||
import { useNavigate } from "@solidjs/router";
|
import { useNavigate } from "@solidjs/router";
|
||||||
|
|
||||||
import ArrowBottom from "@/icons/arrow-bottom.svg";
|
import Icon from "../components/icon";
|
||||||
|
import { Button } from "../components/button";
|
||||||
|
|
||||||
interface HeaderProps {
|
interface HeaderProps {
|
||||||
clan_dir: Accessor<string | null>;
|
clan_dir: Accessor<string | null>;
|
||||||
@@ -28,7 +29,7 @@ export const Header = (props: HeaderProps) => {
|
|||||||
return (
|
return (
|
||||||
<div class="navbar">
|
<div class="navbar">
|
||||||
<div class="flex-none">
|
<div class="flex-none">
|
||||||
<span class="tooltip tooltip-bottom" data-tip="Menu">
|
<span class="tooltip tooltip-bottom lg:hidden" data-tip="Menu">
|
||||||
<label
|
<label
|
||||||
class="btn btn-square btn-ghost drawer-button"
|
class="btn btn-square btn-ghost drawer-button"
|
||||||
for="toplevel-drawer"
|
for="toplevel-drawer"
|
||||||
@@ -37,44 +38,16 @@ export const Header = (props: HeaderProps) => {
|
|||||||
</label>
|
</label>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1">
|
<div class="flex-1"></div>
|
||||||
<Show when={query.isLoading && !query.data}>
|
|
||||||
<div class="skeleton mx-4 size-11 rounded-full"></div>
|
|
||||||
<span class="flex flex-col gap-2">
|
|
||||||
<div class="skeleton h-3 w-32"></div>
|
|
||||||
<div class="skeleton h-3 w-40"></div>
|
|
||||||
</span>
|
|
||||||
</Show>
|
|
||||||
<Show when={query.data}>
|
|
||||||
{(meta) => (
|
|
||||||
<div class="tooltip tooltip-right" data-tip={activeURI()}>
|
|
||||||
<div class="avatar placeholder online mx-4">
|
|
||||||
<div class="w-10 rounded-full bg-slate-700 text-3xl text-neutral-content">
|
|
||||||
<ArrowBottom />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Show>
|
|
||||||
<span class="flex flex-col">
|
|
||||||
<Show when={query.data}>
|
|
||||||
{(meta) => [
|
|
||||||
<span class="text-primary-800">{meta().name}</span>,
|
|
||||||
<span class="text-neutral">{meta()?.description}</span>,
|
|
||||||
]}
|
|
||||||
</Show>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex-none">
|
<div class="flex-none">
|
||||||
<Show when={activeURI()}>
|
<Show when={activeURI()}>
|
||||||
{(d) => (
|
{(d) => (
|
||||||
<span class="tooltip tooltip-bottom" data-tip="Clan Settings">
|
<span class="tooltip tooltip-bottom" data-tip="Clan Settings">
|
||||||
<button
|
<Button
|
||||||
class="link"
|
variant="light"
|
||||||
onClick={() => navigate(`/clans/${window.btoa(d())}`)}
|
onClick={() => navigate(`/clans/${window.btoa(d())}`)}
|
||||||
>
|
startIcon={<Icon icon="Settings" />}
|
||||||
<span class="material-icons">settings</span>
|
></Button>
|
||||||
</button>
|
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</Show>
|
</Show>
|
||||||
|
|||||||
@@ -18,14 +18,14 @@ export const Layout: Component<RouteSectionProps> = (props) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="h-screen w-full p-4 bg-def-3">
|
<div class="h-screen w-full p-4 bg-def-2">
|
||||||
<div class="drawer h-full lg:drawer-open ">
|
<div class="drawer h-full lg:drawer-open ">
|
||||||
<input
|
<input
|
||||||
id="toplevel-drawer"
|
id="toplevel-drawer"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
class="drawer-toggle hidden"
|
class="drawer-toggle hidden"
|
||||||
/>
|
/>
|
||||||
<div class="drawer-content overflow-x-hidden overflow-y-scroll">
|
<div class="drawer-content overflow-x-hidden overflow-y-scroll p-2">
|
||||||
<Show when={props.location.pathname !== "welcome"}>
|
<Show when={props.location.pathname !== "welcome"}>
|
||||||
<Header clan_dir={activeURI} />
|
<Header clan_dir={activeURI} />
|
||||||
</Show>
|
</Show>
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { callApi } from "@/src/api";
|
|||||||
import { Component, For, Show } from "solid-js";
|
import { Component, For, Show } from "solid-js";
|
||||||
|
|
||||||
import { createQuery } from "@tanstack/solid-query";
|
import { createQuery } from "@tanstack/solid-query";
|
||||||
|
import { Button } from "@/src/components/button";
|
||||||
|
import Icon from "@/src/components/icon";
|
||||||
|
|
||||||
export const BlockDevicesView: Component = () => {
|
export const BlockDevicesView: Component = () => {
|
||||||
const {
|
const {
|
||||||
@@ -22,9 +24,10 @@ export const BlockDevicesView: Component = () => {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div class="tooltip tooltip-bottom" data-tip="Refresh">
|
<div class="tooltip tooltip-bottom" data-tip="Refresh">
|
||||||
<button class="btn btn-ghost" onClick={() => loadDevices()}>
|
<Button
|
||||||
<span class="material-icons ">refresh</span>
|
onClick={() => loadDevices()}
|
||||||
</button>
|
startIcon={<Icon icon="Reload" />}
|
||||||
|
></Button>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex max-w-screen-lg flex-col gap-4">
|
<div class="flex max-w-screen-lg flex-col gap-4">
|
||||||
{isFetching ? (
|
{isFetching ? (
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import toast from "solid-toast";
|
|||||||
import { setActiveURI, setClanList } from "@/src/App";
|
import { setActiveURI, setClanList } from "@/src/App";
|
||||||
import { TextInput } from "@/src/components/TextInput";
|
import { TextInput } from "@/src/components/TextInput";
|
||||||
import { useNavigate } from "@solidjs/router";
|
import { useNavigate } from "@solidjs/router";
|
||||||
|
import { Button } from "@/src/components/button";
|
||||||
|
import Icon from "@/src/components/icon";
|
||||||
|
|
||||||
type CreateForm = Meta & {
|
type CreateForm = Meta & {
|
||||||
template: string;
|
template: string;
|
||||||
@@ -176,13 +178,13 @@ export const CreateClan = () => {
|
|||||||
</Field>
|
</Field>
|
||||||
{
|
{
|
||||||
<div class="card-actions justify-end">
|
<div class="card-actions justify-end">
|
||||||
<button
|
<Button
|
||||||
class="btn btn-primary"
|
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={formStore.submitting}
|
disabled={formStore.submitting}
|
||||||
|
endIcon={<Icon icon="Plus" />}
|
||||||
>
|
>
|
||||||
Create
|
Create
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,16 +1,7 @@
|
|||||||
import {
|
import { callApi, ClanServiceInstance, SuccessQuery } from "@/src/api";
|
||||||
callApi,
|
|
||||||
ClanService,
|
|
||||||
ClanServiceInstance,
|
|
||||||
SuccessQuery,
|
|
||||||
} from "@/src/api";
|
|
||||||
import { BackButton } from "@/src/components/BackButton";
|
import { BackButton } from "@/src/components/BackButton";
|
||||||
import { useParams } from "@solidjs/router";
|
import { useParams } from "@solidjs/router";
|
||||||
import {
|
import { createQuery, useQueryClient } from "@tanstack/solid-query";
|
||||||
createQuery,
|
|
||||||
QueryClient,
|
|
||||||
useQueryClient,
|
|
||||||
} from "@tanstack/solid-query";
|
|
||||||
import { createSignal, For, Match, Switch } from "solid-js";
|
import { createSignal, For, Match, Switch } from "solid-js";
|
||||||
import { Show } from "solid-js";
|
import { Show } from "solid-js";
|
||||||
import {
|
import {
|
||||||
@@ -25,6 +16,8 @@ import {
|
|||||||
import { TextInput } from "@/src/components/TextInput";
|
import { TextInput } from "@/src/components/TextInput";
|
||||||
import toast from "solid-toast";
|
import toast from "solid-toast";
|
||||||
import { get_single_service, set_single_service } from "@/src/api/inventory";
|
import { get_single_service, set_single_service } from "@/src/api/inventory";
|
||||||
|
import { Button } from "@/src/components/button";
|
||||||
|
import Icon from "@/src/components/icon";
|
||||||
|
|
||||||
interface AdminModuleFormProps {
|
interface AdminModuleFormProps {
|
||||||
admin: AdminData;
|
admin: AdminData;
|
||||||
@@ -157,13 +150,12 @@ const EditClanForm = (props: EditClanFormProps) => {
|
|||||||
</Field>
|
</Field>
|
||||||
{
|
{
|
||||||
<div class="card-actions justify-end">
|
<div class="card-actions justify-end">
|
||||||
<button
|
<Button
|
||||||
class="btn btn-primary"
|
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={formStore.submitting || !formStore.dirty}
|
disabled={formStore.submitting || !formStore.dirty}
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -307,41 +299,39 @@ const AdminModuleForm = (props: AdminModuleFormProps) => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
<button
|
<Button
|
||||||
class="btn btn-ghost col-span-1 self-end"
|
variant="light"
|
||||||
|
class="col-span-1 self-end"
|
||||||
|
startIcon={<Icon icon="Trash" />}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setKeys((c) => c.filter((_, i) => i !== idx()));
|
setKeys((c) => c.filter((_, i) => i !== idx()));
|
||||||
setValue(formStore, `allowedKeys.${idx()}.name`, "");
|
setValue(formStore, `allowedKeys.${idx()}.name`, "");
|
||||||
setValue(formStore, `allowedKeys.${idx()}.value`, "");
|
setValue(formStore, `allowedKeys.${idx()}.value`, "");
|
||||||
}}
|
}}
|
||||||
>
|
></Button>
|
||||||
<span class="material-icons">delete</span>
|
|
||||||
</button>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
<div class="my-2 flex w-full gap-2">
|
<div class="my-2 flex w-full gap-2">
|
||||||
<button
|
<Button
|
||||||
class="btn btn-square btn-ghost"
|
variant="light"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setKeys((c) => [...c, 1]);
|
setKeys((c) => [...c, 1]);
|
||||||
}}
|
}}
|
||||||
>
|
startIcon={<Icon icon="Plus" />}
|
||||||
<span class="material-icons">add</span>
|
></Button>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{
|
{
|
||||||
<div class="card-actions justify-end">
|
<div class="card-actions justify-end">
|
||||||
<button
|
<Button
|
||||||
class="btn btn-primary"
|
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={formStore.submitting || !formStore.dirty}
|
disabled={formStore.submitting || !formStore.dirty}
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import { useFloating } from "@/src/floating";
|
|||||||
import { autoUpdate, flip, hide, offset, shift } from "@floating-ui/dom";
|
import { autoUpdate, flip, hide, offset, shift } from "@floating-ui/dom";
|
||||||
import { useNavigate, A } from "@solidjs/router";
|
import { useNavigate, A } from "@solidjs/router";
|
||||||
import { registerClan } from "@/src/hooks";
|
import { registerClan } from "@/src/hooks";
|
||||||
|
import { Button } from "@/src/components/button";
|
||||||
|
import Icon from "@/src/components/icon";
|
||||||
|
|
||||||
interface ClanItemProps {
|
interface ClanItemProps {
|
||||||
clan_dir: string;
|
clan_dir: string;
|
||||||
@@ -61,33 +63,32 @@ const ClanItem = (props: ClanItemProps) => {
|
|||||||
<div class="stat">
|
<div class="stat">
|
||||||
<div class="stat-figure text-primary-800">
|
<div class="stat-figure text-primary-800">
|
||||||
<div class="join">
|
<div class="join">
|
||||||
<button
|
<Button
|
||||||
class="join-item btn-sm"
|
size="s"
|
||||||
|
variant="light"
|
||||||
|
class="join-item"
|
||||||
onClick={() => navigate(`/clans/${window.btoa(clan_dir)}`)}
|
onClick={() => navigate(`/clans/${window.btoa(clan_dir)}`)}
|
||||||
>
|
endIcon={<Icon icon="Settings" />}
|
||||||
<span class="material-icons">edit</span>
|
></Button>
|
||||||
</button>
|
<Button
|
||||||
<button
|
size="s"
|
||||||
class=" join-item btn-sm"
|
variant="light"
|
||||||
classList={{
|
class="join-item "
|
||||||
"btn btn-ghost btn-outline": activeURI() !== clan_dir,
|
|
||||||
"badge badge-primary": activeURI() === clan_dir,
|
|
||||||
}}
|
|
||||||
disabled={activeURI() === clan_dir}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setActiveURI(clan_dir);
|
setActiveURI(clan_dir);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{activeURI() === clan_dir ? "active" : "select"}
|
{activeURI() === clan_dir ? "active" : "select"}
|
||||||
</button>
|
</Button>
|
||||||
<button
|
<Button
|
||||||
|
size="s"
|
||||||
|
variant="light"
|
||||||
popovertarget={`clan-delete-popover-${clan_dir}`}
|
popovertarget={`clan-delete-popover-${clan_dir}`}
|
||||||
popovertargetaction="toggle"
|
popovertargetaction="toggle"
|
||||||
ref={setReference}
|
ref={setReference}
|
||||||
class="btn btn-ghost btn-outline join-item btn-sm"
|
class="btn btn-ghost btn-outline join-item"
|
||||||
>
|
endIcon={<Icon icon="Trash" />}
|
||||||
Remove
|
></Button>
|
||||||
</button>
|
|
||||||
<div
|
<div
|
||||||
popover="auto"
|
popover="auto"
|
||||||
role="tooltip"
|
role="tooltip"
|
||||||
@@ -100,9 +101,14 @@ const ClanItem = (props: ClanItemProps) => {
|
|||||||
}}
|
}}
|
||||||
class="m-0 bg-transparent"
|
class="m-0 bg-transparent"
|
||||||
>
|
>
|
||||||
<button class="btn bg-[#ffdd2c]" onClick={handleRemove}>
|
<Button
|
||||||
|
size="s"
|
||||||
|
onClick={handleRemove}
|
||||||
|
variant="dark"
|
||||||
|
endIcon={<Icon icon="Trash" />}
|
||||||
|
>
|
||||||
Remove from App
|
Remove from App
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -139,19 +145,19 @@ export const ClanList = () => {
|
|||||||
<div class="label-text text-2xl text-neutral">Registered Clans</div>
|
<div class="label-text text-2xl text-neutral">Registered Clans</div>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<span class="tooltip tooltip-top" data-tip="Register clan">
|
<span class="tooltip tooltip-top" data-tip="Register clan">
|
||||||
<button class="btn btn-square btn-ghost" onClick={registerClan}>
|
<Button
|
||||||
<span class="material-icons">post_add</span>
|
variant="light"
|
||||||
</button>
|
onClick={registerClan}
|
||||||
|
startIcon={<Icon icon="List" />}
|
||||||
|
></Button>
|
||||||
</span>
|
</span>
|
||||||
<span class="tooltip tooltip-top" data-tip="Create new clan">
|
<span class="tooltip tooltip-top" data-tip="Create new clan">
|
||||||
<button
|
<Button
|
||||||
class="btn btn-square btn-ghost"
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate("create");
|
navigate("create");
|
||||||
}}
|
}}
|
||||||
>
|
startIcon={<Icon icon="Plus" />}
|
||||||
<span class="material-icons">add</span>
|
></Button>
|
||||||
</button>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { callApi } from "@/src/api";
|
import { callApi } from "@/src/api";
|
||||||
|
import { Button } from "@/src/components/button";
|
||||||
import { FileInput } from "@/src/components/FileInput";
|
import { FileInput } from "@/src/components/FileInput";
|
||||||
|
import Icon from "@/src/components/icon";
|
||||||
import { SelectInput } from "@/src/components/SelectInput";
|
import { SelectInput } from "@/src/components/SelectInput";
|
||||||
import { TextInput } from "@/src/components/TextInput";
|
import { TextInput } from "@/src/components/TextInput";
|
||||||
import {
|
import {
|
||||||
@@ -209,15 +211,15 @@ export const Flash = () => {
|
|||||||
{(field, props) => (
|
{(field, props) => (
|
||||||
<SelectInput
|
<SelectInput
|
||||||
topRightLabel={
|
topRightLabel={
|
||||||
<button
|
<Button
|
||||||
class="btn btn-ghost btn-sm"
|
size="s"
|
||||||
|
variant="light"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
deviceQuery.refetch();
|
deviceQuery.refetch();
|
||||||
}}
|
}}
|
||||||
>
|
startIcon={<Icon icon="Reload" />}
|
||||||
<span class="material-icons text-sm">refresh</span>
|
></Button>
|
||||||
</button>
|
|
||||||
}
|
}
|
||||||
formStore={formStore}
|
formStore={formStore}
|
||||||
selectProps={props}
|
selectProps={props}
|
||||||
@@ -285,17 +287,19 @@ export const Flash = () => {
|
|||||||
adornment={{
|
adornment={{
|
||||||
position: "end",
|
position: "end",
|
||||||
content: (
|
content: (
|
||||||
<button
|
<Button
|
||||||
|
variant="light"
|
||||||
type="button"
|
type="button"
|
||||||
class="flex justify-center opacity-70"
|
class="flex justify-center opacity-70"
|
||||||
onClick={() => togglePasswordVisibility(index())}
|
onClick={() => togglePasswordVisibility(index())}
|
||||||
>
|
startIcon={
|
||||||
<span class="material-icons">
|
passwordVisibility()[index()] ? (
|
||||||
{passwordVisibility()[index()]
|
<Icon icon="EyeClose" />
|
||||||
? "visibility_off"
|
) : (
|
||||||
: "visibility"}
|
<Icon icon="EyeOpen" />
|
||||||
</span>
|
)
|
||||||
</button>
|
}
|
||||||
|
></Button>
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
required
|
required
|
||||||
@@ -304,25 +308,27 @@ export const Flash = () => {
|
|||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
<div class="col-span-1 self-end">
|
<div class="col-span-1 self-end">
|
||||||
<button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-ghost "
|
variant="light"
|
||||||
|
class="h-12"
|
||||||
onClick={() => removeWifiNetwork(index())}
|
onClick={() => removeWifiNetwork(index())}
|
||||||
>
|
startIcon={<Icon icon="Trash" />}
|
||||||
<span class="material-icons">delete</span>
|
></Button>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
<div class="">
|
<div class="">
|
||||||
<button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-ghost btn-sm"
|
size="s"
|
||||||
|
variant="light"
|
||||||
onClick={addWifiNetwork}
|
onClick={addWifiNetwork}
|
||||||
|
startIcon={<Icon icon="Plus" />}
|
||||||
>
|
>
|
||||||
<span class="material-icons">add</span>Add WiFi Network
|
Add WiFi Network
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -434,18 +440,20 @@ export const Flash = () => {
|
|||||||
|
|
||||||
<hr></hr>
|
<hr></hr>
|
||||||
<div class="mt-2 flex justify-end pt-2">
|
<div class="mt-2 flex justify-end pt-2">
|
||||||
<button
|
<Button
|
||||||
class="btn btn-error self-end"
|
class="self-end"
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={formStore.submitting}
|
disabled={formStore.submitting}
|
||||||
|
startIcon={
|
||||||
|
formStore.submitting ? (
|
||||||
|
<Icon icon="Load" />
|
||||||
|
) : (
|
||||||
|
<Icon icon="Flash" />
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{formStore.submitting ? (
|
|
||||||
<span class="loading loading-spinner"></span>
|
|
||||||
) : (
|
|
||||||
<span class="material-icons">bolt</span>
|
|
||||||
)}
|
|
||||||
{formStore.submitting ? "Flashing..." : "Flash Installer"}
|
{formStore.submitting ? "Flashing..." : "Flash Installer"}
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { type Component, createSignal, For, Show } from "solid-js";
|
import { type Component, createSignal, For, Show } from "solid-js";
|
||||||
import { OperationResponse, pyApi } from "@/src/api";
|
import { OperationResponse, pyApi } from "@/src/api";
|
||||||
|
import { Button } from "@/src/components/button";
|
||||||
|
import Icon from "@/src/components/icon";
|
||||||
|
|
||||||
type ServiceModel = Extract<
|
type ServiceModel = Extract<
|
||||||
OperationResponse<"show_mdns">,
|
OperationResponse<"show_mdns">,
|
||||||
@@ -12,12 +14,11 @@ export const HostList: Component = () => {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div class="tooltip tooltip-bottom" data-tip="Refresh install targets">
|
<div class="tooltip tooltip-bottom" data-tip="Refresh install targets">
|
||||||
<button
|
<Button
|
||||||
class="btn btn-ghost"
|
variant="light"
|
||||||
onClick={() => pyApi.show_mdns.dispatch({})}
|
onClick={() => pyApi.show_mdns.dispatch({})}
|
||||||
>
|
startIcon={<Icon icon="Update" />}
|
||||||
<span class="material-icons ">refresh</span>
|
></Button>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-wrap gap-2">
|
<div class="flex flex-wrap gap-2">
|
||||||
<Show when={services()}>
|
<Show when={services()}>
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { callApi, OperationArgs } from "@/src/api";
|
import { callApi, OperationArgs } from "@/src/api";
|
||||||
import { activeURI } from "@/src/App";
|
import { activeURI } from "@/src/App";
|
||||||
|
import { Button } from "@/src/components/button";
|
||||||
|
import Icon from "@/src/components/icon";
|
||||||
import { TextInput } from "@/src/components/TextInput";
|
import { TextInput } from "@/src/components/TextInput";
|
||||||
import { createForm, required, reset } from "@modular-forms/solid";
|
import { createForm, required, reset } from "@modular-forms/solid";
|
||||||
import { useNavigate } from "@solidjs/router";
|
import { useNavigate } from "@solidjs/router";
|
||||||
@@ -108,26 +110,21 @@ export function CreateMachine() {
|
|||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
<div class="mt-12 flex justify-end">
|
<div class="mt-12 flex justify-end">
|
||||||
<button
|
<Button
|
||||||
class="btn btn-primary"
|
|
||||||
type="submit"
|
type="submit"
|
||||||
classList={{
|
disabled={formStore.submitting}
|
||||||
"btn-disabled": formStore.submitting,
|
startIcon={
|
||||||
}}
|
formStore.submitting ? (
|
||||||
|
<Icon icon="Load" />
|
||||||
|
) : (
|
||||||
|
<Icon icon="Plus" />
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Switch
|
<Switch fallback={<>Creating</>}>
|
||||||
fallback={
|
<Match when={!formStore.submitting}>Create</Match>
|
||||||
<>
|
|
||||||
<span class="loading loading-spinner loading-sm"></span>
|
|
||||||
Creating
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Match when={!formStore.submitting}>
|
|
||||||
<span class="material-icons">add</span>Create
|
|
||||||
</Match>
|
|
||||||
</Switch>
|
</Switch>
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ import { set_single_disk_id } from "@/src/api/disk";
|
|||||||
import { get_iwd_service } from "@/src/api/wifi";
|
import { get_iwd_service } from "@/src/api/wifi";
|
||||||
import { activeURI } from "@/src/App";
|
import { activeURI } from "@/src/App";
|
||||||
import { BackButton } from "@/src/components/BackButton";
|
import { BackButton } from "@/src/components/BackButton";
|
||||||
|
import { Button } from "@/src/components/button";
|
||||||
import { FileInput } from "@/src/components/FileInput";
|
import { FileInput } from "@/src/components/FileInput";
|
||||||
|
import Icon from "@/src/components/icon";
|
||||||
import { SelectInput } from "@/src/components/SelectInput";
|
import { SelectInput } from "@/src/components/SelectInput";
|
||||||
import { TextInput } from "@/src/components/TextInput";
|
import { TextInput } from "@/src/components/TextInput";
|
||||||
import { selectSshKeys } from "@/src/hooks";
|
import { selectSshKeys } from "@/src/hooks";
|
||||||
@@ -192,13 +194,15 @@ const InstallMachine = (props: InstallMachineProps) => {
|
|||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
<div class="">
|
<div class="">
|
||||||
<button
|
<Button
|
||||||
class="btn btn-ghost btn-sm btn-wide"
|
variant="light"
|
||||||
|
size="s"
|
||||||
|
class="w-full"
|
||||||
onclick={generateReport}
|
onclick={generateReport}
|
||||||
>
|
>
|
||||||
<span class="material-icons">manage_search</span>
|
<span class="material-icons">manage_search</span>
|
||||||
Generate report
|
Generate report
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -252,25 +256,32 @@ const InstallMachine = (props: InstallMachineProps) => {
|
|||||||
<Show
|
<Show
|
||||||
when={confirmDisk()}
|
when={confirmDisk()}
|
||||||
fallback={
|
fallback={
|
||||||
<button
|
<Button
|
||||||
class="btn btn-primary btn-wide"
|
class="btn btn-primary btn-wide"
|
||||||
onClick={handleDiskConfirm}
|
onClick={handleDiskConfirm}
|
||||||
disabled={!hasDisk()}
|
disabled={!hasDisk()}
|
||||||
|
startIcon={<Icon icon="Flash" />}
|
||||||
>
|
>
|
||||||
<span class="material-icons">check</span>
|
|
||||||
Confirm Disk
|
Confirm Disk
|
||||||
</button>
|
</Button>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<button class="btn btn-primary btn-wide" type="submit">
|
<Button
|
||||||
<span class="material-icons">send_to_mobile</span>
|
class="w-full"
|
||||||
|
type="submit"
|
||||||
|
startIcon={<Icon icon="Flash" />}
|
||||||
|
>
|
||||||
Install
|
Install
|
||||||
</button>
|
</Button>
|
||||||
</Show>
|
</Show>
|
||||||
<form method="dialog">
|
<form method="dialog">
|
||||||
<button onClick={() => setConfirmDisk(false)} class="btn">
|
<Button
|
||||||
|
variant="light"
|
||||||
|
onClick={() => setConfirmDisk(false)}
|
||||||
|
class="btn"
|
||||||
|
>
|
||||||
Close
|
Close
|
||||||
</button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
@@ -519,13 +530,12 @@ const MachineForm = (props: MachineDetailsProps) => {
|
|||||||
|
|
||||||
{
|
{
|
||||||
<div class="card-actions justify-end">
|
<div class="card-actions justify-end">
|
||||||
<button
|
<Button
|
||||||
class="btn btn-primary"
|
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={formStore.submitting || !formStore.dirty}
|
disabled={formStore.submitting || !formStore.dirty}
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -550,15 +560,19 @@ const MachineForm = (props: MachineDetailsProps) => {
|
|||||||
device.
|
device.
|
||||||
</span>
|
</span>
|
||||||
<div class="tooltip w-fit" data-tip="Machine must be online">
|
<div class="tooltip w-fit" data-tip="Machine must be online">
|
||||||
<button
|
<Button
|
||||||
class="btn btn-primary btn-sm btn-wide"
|
class="w-full"
|
||||||
disabled={!online()}
|
// disabled={!online()}
|
||||||
// @ts-expect-error: This string method is not supported by ts
|
onClick={() => {
|
||||||
onClick="install_modal.showModal()"
|
const modal = document.getElementById(
|
||||||
|
"install_modal",
|
||||||
|
) as HTMLDialogElement | null;
|
||||||
|
modal?.showModal();
|
||||||
|
}}
|
||||||
|
endIcon={<Icon icon="Flash" />}
|
||||||
>
|
>
|
||||||
<span class="material-icons">send_to_mobile</span>
|
|
||||||
Install
|
Install
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<dialog id="install_modal" class="modal backdrop:bg-transparent">
|
<dialog id="install_modal" class="modal backdrop:bg-transparent">
|
||||||
@@ -577,14 +591,14 @@ const MachineForm = (props: MachineDetailsProps) => {
|
|||||||
process.
|
process.
|
||||||
</span>
|
</span>
|
||||||
<div class="tooltip w-fit" data-tip="Machine must be online">
|
<div class="tooltip w-fit" data-tip="Machine must be online">
|
||||||
<button
|
<Button
|
||||||
class="btn btn-primary btn-sm btn-wide"
|
class="w-full"
|
||||||
disabled={!online()}
|
disabled={!online()}
|
||||||
onClick={() => handleUpdate()}
|
onClick={() => handleUpdate()}
|
||||||
|
endIcon={<Icon icon="Update" />}
|
||||||
>
|
>
|
||||||
<span class="material-icons">update</span>
|
|
||||||
Update
|
Update
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -761,41 +775,38 @@ function WifiModule(props: MachineWifiProps) {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
<button class="btn btn-ghost self-end">
|
<Button
|
||||||
<span
|
variant="light"
|
||||||
class="material-icons"
|
class="self-end"
|
||||||
onClick={(e) => {
|
type="button"
|
||||||
e.preventDefault();
|
onClick={() => {
|
||||||
setNets((c) => c.filter((_, i) => i !== idx()));
|
setNets((c) => c.filter((_, i) => i !== idx()));
|
||||||
setValue(formStore, `networks.${idx()}.ssid`, undefined);
|
setValue(formStore, `networks.${idx()}.ssid`, undefined);
|
||||||
setValue(formStore, `networks.${idx()}.password`, undefined);
|
setValue(formStore, `networks.${idx()}.password`, undefined);
|
||||||
}}
|
}}
|
||||||
>
|
startIcon={<Icon icon="Trash" />}
|
||||||
delete
|
></Button>
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
<button
|
<Button
|
||||||
class="btn btn-ghost btn-sm my-1 flex items-center justify-center"
|
class="btn btn-ghost btn-sm my-1 flex items-center justify-center"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
|
||||||
setNets([...nets(), 1]);
|
setNets([...nets(), 1]);
|
||||||
}}
|
}}
|
||||||
|
type="button"
|
||||||
|
startIcon={<Icon icon="Plus" />}
|
||||||
>
|
>
|
||||||
<span class="material-icons">add</span>
|
|
||||||
Add Network
|
Add Network
|
||||||
</button>
|
</Button>
|
||||||
{
|
{
|
||||||
<div class="card-actions mt-4 justify-end">
|
<div class="card-actions mt-4 justify-end">
|
||||||
<button
|
<Button
|
||||||
class="btn btn-primary"
|
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={formStore.submitting || !formStore.dirty}
|
disabled={formStore.submitting || !formStore.dirty}
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import {
|
|||||||
useQueryClient,
|
useQueryClient,
|
||||||
} from "@tanstack/solid-query";
|
} from "@tanstack/solid-query";
|
||||||
import { useNavigate } from "@solidjs/router";
|
import { useNavigate } from "@solidjs/router";
|
||||||
|
import { Button } from "@/src/components/button";
|
||||||
|
import Icon from "@/src/components/icon";
|
||||||
|
|
||||||
type MachinesModel = Extract<
|
type MachinesModel = Extract<
|
||||||
OperationResponse<"list_inventory_machines">,
|
OperationResponse<"list_inventory_machines">,
|
||||||
@@ -80,14 +82,18 @@ export const MachineListView: Component = () => {
|
|||||||
<div>
|
<div>
|
||||||
<div class="tooltip tooltip-bottom" data-tip="Open Clan"></div>
|
<div class="tooltip tooltip-bottom" data-tip="Open Clan"></div>
|
||||||
<div class="tooltip tooltip-bottom" data-tip="Refresh">
|
<div class="tooltip tooltip-bottom" data-tip="Refresh">
|
||||||
<button class="btn btn-ghost" onClick={() => refresh()}>
|
<Button
|
||||||
<span class="material-icons ">refresh</span>
|
variant="light"
|
||||||
</button>
|
onClick={() => refresh()}
|
||||||
|
startIcon={<Icon icon="Reload" />}
|
||||||
|
></Button>
|
||||||
</div>
|
</div>
|
||||||
<div class="tooltip tooltip-bottom" data-tip="Create machine">
|
<div class="tooltip tooltip-bottom" data-tip="Create machine">
|
||||||
<button class="btn btn-ghost" onClick={() => navigate("create")}>
|
<Button
|
||||||
<span class="material-icons ">add</span>
|
variant="light"
|
||||||
</button>
|
onClick={() => navigate("create")}
|
||||||
|
startIcon={<Icon icon="Plus" />}
|
||||||
|
></Button>
|
||||||
</div>
|
</div>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={inventoryQuery.isLoading}>
|
<Match when={inventoryQuery.isLoading}>
|
||||||
@@ -117,12 +123,13 @@ export const MachineListView: Component = () => {
|
|||||||
<span class="text-lg text-neutral">
|
<span class="text-lg text-neutral">
|
||||||
No machines defined yet. Click below to define one.
|
No machines defined yet. Click below to define one.
|
||||||
</span>
|
</span>
|
||||||
<button
|
<Button
|
||||||
class="btn btn-square btn-ghost size-28 overflow-hidden p-2"
|
variant="light"
|
||||||
|
class="size-28 overflow-hidden p-2"
|
||||||
onClick={() => navigate("/machines/create")}
|
onClick={() => navigate("/machines/create")}
|
||||||
>
|
>
|
||||||
<span class="material-icons text-6xl font-light">add</span>
|
<span class="material-icons text-6xl font-light">add</span>
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={!inventoryQuery.isLoading}>
|
<Match when={!inventoryQuery.isLoading}>
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ import {
|
|||||||
SubmitHandler,
|
SubmitHandler,
|
||||||
} from "@modular-forms/solid";
|
} from "@modular-forms/solid";
|
||||||
import { DynForm } from "@/src/Form/form";
|
import { DynForm } from "@/src/Form/form";
|
||||||
|
import { Button } from "@/src/components/button";
|
||||||
|
import Icon from "@/src/components/icon";
|
||||||
|
|
||||||
export const ModuleDetails = () => {
|
export const ModuleDetails = () => {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@@ -116,10 +118,9 @@ const Details = (props: DetailsProps) => {
|
|||||||
<SolidMarkdown>{props.data.readme}</SolidMarkdown>
|
<SolidMarkdown>{props.data.readme}</SolidMarkdown>
|
||||||
</div>
|
</div>
|
||||||
<div class="my-2 flex w-full gap-2">
|
<div class="my-2 flex w-full gap-2">
|
||||||
<button class="btn btn-primary" onClick={add}>
|
<Button variant="light" onClick={add} startIcon={<Icon icon="Plus" />}>
|
||||||
<span class="material-icons ">add</span>
|
|
||||||
Add to Clan
|
Add to Clan
|
||||||
</button>
|
</Button>
|
||||||
{/* Add -> Select (required) roles, assign Machine */}
|
{/* Add -> Select (required) roles, assign Machine */}
|
||||||
</div>
|
</div>
|
||||||
<ModuleForm id={props.id} />
|
<ModuleForm id={props.id} />
|
||||||
@@ -192,7 +193,7 @@ export const ModuleForm = (props: { id: string }) => {
|
|||||||
handleSubmit={handleSubmit}
|
handleSubmit={handleSubmit}
|
||||||
schema={schema}
|
schema={schema}
|
||||||
components={{
|
components={{
|
||||||
after: <button class="btn btn-primary">Submit</button>,
|
after: <Button>Submit</Button>,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { setActiveURI } from "@/src/App";
|
import { setActiveURI } from "@/src/App";
|
||||||
|
import { Button } from "@/src/components/button";
|
||||||
import { registerClan } from "@/src/hooks";
|
import { registerClan } from "@/src/hooks";
|
||||||
import { useNavigate } from "@solidjs/router";
|
import { useNavigate } from "@solidjs/router";
|
||||||
|
|
||||||
@@ -11,14 +12,12 @@ export const Welcome = () => {
|
|||||||
<h1 class="text-5xl font-bold">Welcome to Clan</h1>
|
<h1 class="text-5xl font-bold">Welcome to Clan</h1>
|
||||||
<p class="py-6">Own the services you use.</p>
|
<p class="py-6">Own the services you use.</p>
|
||||||
<div class="flex flex-col items-start gap-2">
|
<div class="flex flex-col items-start gap-2">
|
||||||
<button
|
<Button class="w-full" onClick={() => navigate("/clans/create")}>
|
||||||
class="btn btn-primary w-full"
|
|
||||||
onClick={() => navigate("/clans/create")}
|
|
||||||
>
|
|
||||||
Build your own
|
Build your own
|
||||||
</button>
|
</Button>
|
||||||
<button
|
<Button
|
||||||
class="link w-full text-right text-primary-800"
|
variant="light"
|
||||||
|
class="!link w-full text-right !text-primary-800"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const uri = await registerClan();
|
const uri = await registerClan();
|
||||||
if (uri) {
|
if (uri) {
|
||||||
@@ -28,7 +27,7 @@ export const Welcome = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Or select folder
|
Or select folder
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user