Merge pull request 'UI: Layout improvements & serde fix.' (#2585) from hsjobeki/clan-core:hsjobeki-main into main
This commit is contained in:
@@ -2,36 +2,21 @@ import {
|
|||||||
createForm,
|
createForm,
|
||||||
Field,
|
Field,
|
||||||
FieldArray,
|
FieldArray,
|
||||||
FieldElement,
|
|
||||||
FieldValues,
|
FieldValues,
|
||||||
FormStore,
|
FormStore,
|
||||||
getValue,
|
|
||||||
minLength,
|
|
||||||
pattern,
|
pattern,
|
||||||
ResponseData,
|
ResponseData,
|
||||||
setValue,
|
setValue,
|
||||||
getValues,
|
getValues,
|
||||||
insert,
|
insert,
|
||||||
SubmitHandler,
|
SubmitHandler,
|
||||||
swap,
|
|
||||||
reset,
|
reset,
|
||||||
remove,
|
remove,
|
||||||
move,
|
move,
|
||||||
setError,
|
|
||||||
setValues,
|
|
||||||
} from "@modular-forms/solid";
|
} from "@modular-forms/solid";
|
||||||
import { JSONSchema7, JSONSchema7Type, validate } from "json-schema";
|
import { JSONSchema7, JSONSchema7Type } from "json-schema";
|
||||||
import { TextInput } from "../fields/TextInput";
|
import { TextInput } from "../fields/TextInput";
|
||||||
import {
|
import { createEffect, For, JSX, Match, Show, Switch } from "solid-js";
|
||||||
children,
|
|
||||||
Component,
|
|
||||||
createEffect,
|
|
||||||
For,
|
|
||||||
JSX,
|
|
||||||
Match,
|
|
||||||
Show,
|
|
||||||
Switch,
|
|
||||||
} from "solid-js";
|
|
||||||
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";
|
||||||
@@ -218,7 +203,7 @@ export function StringField<T extends FieldValues, R extends ResponseData>(
|
|||||||
(r) => r === props.path[props.path.length - 1],
|
(r) => r === props.path[props.path.length - 1],
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
const readonly = props.readonly;
|
const readonly = !!props.readonly;
|
||||||
return (
|
return (
|
||||||
<Switch fallback={<Unsupported schema={props.schema} />}>
|
<Switch fallback={<Unsupported schema={props.schema} />}>
|
||||||
<Match
|
<Match
|
||||||
@@ -388,20 +373,27 @@ export function ListValueDisplay<T extends FieldValues, R extends ResponseData>(
|
|||||||
{props.children}
|
{props.children}
|
||||||
<div class="ml-4 min-w-fit pb-4">
|
<div class="ml-4 min-w-fit pb-4">
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="ghost"
|
||||||
|
size="s"
|
||||||
|
type="button"
|
||||||
onClick={moveItemBy(1)}
|
onClick={moveItemBy(1)}
|
||||||
disabled={topMost()}
|
disabled={topMost()}
|
||||||
startIcon={<Icon icon="ArrowBottom" />}
|
startIcon={<Icon icon="ArrowBottom" />}
|
||||||
class="h-12"
|
class="h-12"
|
||||||
></Button>
|
></Button>
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="s"
|
||||||
onClick={moveItemBy(-1)}
|
onClick={moveItemBy(-1)}
|
||||||
disabled={bottomMost()}
|
disabled={bottomMost()}
|
||||||
class="h-12"
|
class="h-12"
|
||||||
startIcon={<Icon icon="ArrowTop" />}
|
startIcon={<Icon icon="ArrowTop" />}
|
||||||
></Button>
|
></Button>
|
||||||
<Button
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="s"
|
||||||
class="h-12"
|
class="h-12"
|
||||||
startIcon={<Icon icon="Trash" />}
|
startIcon={<Icon icon="Trash" />}
|
||||||
onClick={removeItem}
|
onClick={removeItem}
|
||||||
@@ -600,7 +592,9 @@ export function ArrayFields<T extends FieldValues, R extends ResponseData>(
|
|||||||
each={fieldArray.items}
|
each={fieldArray.items}
|
||||||
fallback={
|
fallback={
|
||||||
// Empty list
|
// Empty list
|
||||||
<span class="text-neutral-500">No items</span>
|
<span class="text-neutral-500">
|
||||||
|
No {itemsSchema().title || "entries"} yet.
|
||||||
|
</span>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(item, idx) => (
|
{(item, idx) => (
|
||||||
@@ -644,7 +638,10 @@ export function ArrayFields<T extends FieldValues, R extends ResponseData>(
|
|||||||
formProps={{
|
formProps={{
|
||||||
class: cx("px-2 w-full"),
|
class: cx("px-2 w-full"),
|
||||||
}}
|
}}
|
||||||
schema={{ ...itemsSchema(), title: "Add entry" }}
|
schema={{
|
||||||
|
...itemsSchema(),
|
||||||
|
title: itemsSchema().title || "thing",
|
||||||
|
}}
|
||||||
initialPath={["root"]}
|
initialPath={["root"]}
|
||||||
// Reset the input field for list items
|
// Reset the input field for list items
|
||||||
resetOnSubmit={true}
|
resetOnSubmit={true}
|
||||||
@@ -655,10 +652,12 @@ export function ArrayFields<T extends FieldValues, R extends ResponseData>(
|
|||||||
components={{
|
components={{
|
||||||
before: (
|
before: (
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="ghost"
|
||||||
|
type="submit"
|
||||||
endIcon={<Icon icon={"Plus"} />}
|
endIcon={<Icon icon={"Plus"} />}
|
||||||
|
class="capitalize"
|
||||||
>
|
>
|
||||||
Add
|
Add {itemsSchema().title}
|
||||||
</Button>
|
</Button>
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
@@ -847,7 +846,7 @@ export function ObjectFields<T extends FieldValues, R extends ResponseData>(
|
|||||||
{key}
|
{key}
|
||||||
</span>
|
</span>
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="ghost"
|
||||||
class="ml-auto"
|
class="ml-auto"
|
||||||
size="s"
|
size="s"
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ export const MachineListItem = (props: MachineListItemProps) => {
|
|||||||
<div>
|
<div>
|
||||||
<Menu
|
<Menu
|
||||||
popoverid={`menu-${props.name}`}
|
popoverid={`menu-${props.name}`}
|
||||||
label={<Icon icon={"Expand"} />}
|
label={<Icon icon={"More"} />}
|
||||||
>
|
>
|
||||||
<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>
|
||||||
|
|||||||
@@ -55,14 +55,12 @@ export const Menu = (props: MenuProps) => {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="ghost"
|
||||||
|
size="s"
|
||||||
popovertarget={props.popoverid}
|
popovertarget={props.popoverid}
|
||||||
popovertargetaction="toggle"
|
popovertargetaction="toggle"
|
||||||
ref={setReference}
|
ref={setReference}
|
||||||
class={cx(
|
class={cx("join-item", props.buttonClass)}
|
||||||
"btn btn-ghost btn-outline join-item btn-sm",
|
|
||||||
props.buttonClass,
|
|
||||||
)}
|
|
||||||
{...props.buttonProps}
|
{...props.buttonProps}
|
||||||
>
|
>
|
||||||
{props.label}
|
{props.label}
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ export const Typography = <H extends Hierarchy>(props: TypographyProps<H>) => {
|
|||||||
inverted,
|
inverted,
|
||||||
hierarchy,
|
hierarchy,
|
||||||
weight = "normal",
|
weight = "normal",
|
||||||
tag,
|
tag = "span",
|
||||||
children,
|
children,
|
||||||
classes,
|
classes,
|
||||||
} = props;
|
} = props;
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { splitProps, type JSX } from "solid-js";
|
import { splitProps, type JSX } from "solid-js";
|
||||||
import cx from "classnames";
|
import cx from "classnames";
|
||||||
|
import { Typography } from "../Typography";
|
||||||
|
|
||||||
type Variants = "dark" | "light";
|
type Variants = "dark" | "light" | "ghost";
|
||||||
type Size = "default" | "s";
|
type Size = "default" | "s";
|
||||||
|
|
||||||
const variantColors: Record<Variants, string> = {
|
const variantColors: Record<Variants, string> = {
|
||||||
dark: cx(
|
dark: cx(
|
||||||
|
"border border-solid",
|
||||||
"border-secondary-950 bg-primary-900 text-white",
|
"border-secondary-950 bg-primary-900 text-white",
|
||||||
"shadow-inner-primary",
|
"shadow-inner-primary",
|
||||||
// Hover state
|
// Hover state
|
||||||
@@ -18,6 +20,7 @@ const variantColors: Record<Variants, string> = {
|
|||||||
"disabled:bg-secondary-200 disabled:text-secondary-700 disabled:border-secondary-300",
|
"disabled:bg-secondary-200 disabled:text-secondary-700 disabled:border-secondary-300",
|
||||||
),
|
),
|
||||||
light: cx(
|
light: cx(
|
||||||
|
"border border-solid",
|
||||||
"border-secondary-800 bg-secondary-100 text-secondary-800",
|
"border-secondary-800 bg-secondary-100 text-secondary-800",
|
||||||
"shadow-inner-secondary",
|
"shadow-inner-secondary",
|
||||||
// Hover state
|
// Hover state
|
||||||
@@ -29,6 +32,17 @@ const variantColors: Record<Variants, string> = {
|
|||||||
// Disabled
|
// Disabled
|
||||||
"disabled:bg-secondary-50 disabled:text-secondary-200 disabled:border-secondary-700",
|
"disabled:bg-secondary-50 disabled:text-secondary-200 disabled:border-secondary-700",
|
||||||
),
|
),
|
||||||
|
ghost: cx(
|
||||||
|
// "shadow-inner-secondary",
|
||||||
|
// Hover state
|
||||||
|
// Focus state
|
||||||
|
// Active state
|
||||||
|
"hover:bg-secondary-200 hover:text-secondary-900",
|
||||||
|
"focus:bg-secondary-200 focus:text-secondary-900",
|
||||||
|
"active:bg-secondary-200 active:text-secondary-950 active:shadow-inner-secondary-active",
|
||||||
|
// Disabled
|
||||||
|
"disabled:bg-secondary-50 disabled:text-secondary-200 disabled:border-secondary-700",
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
const sizePaddings: Record<Size, string> = {
|
const sizePaddings: Record<Size, string> = {
|
||||||
@@ -36,6 +50,11 @@ const sizePaddings: Record<Size, string> = {
|
|||||||
s: cx("rounded-sm py-[0.375rem] px-3"),
|
s: cx("rounded-sm py-[0.375rem] px-3"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const sizeFont: Record<Size, string> = {
|
||||||
|
default: cx("text-[0.8125rem]"),
|
||||||
|
s: cx("text-[0.75rem]"),
|
||||||
|
};
|
||||||
|
|
||||||
interface ButtonProps extends JSX.ButtonHTMLAttributes<HTMLButtonElement> {
|
interface ButtonProps extends JSX.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||||
variant?: Variants;
|
variant?: Variants;
|
||||||
size?: Size;
|
size?: Size;
|
||||||
@@ -60,11 +79,13 @@ export const Button = (props: ButtonProps) => {
|
|||||||
// Layout
|
// Layout
|
||||||
"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",
|
|
||||||
"p-4",
|
"p-4",
|
||||||
sizePaddings[local.size || "default"],
|
sizePaddings[local.size || "default"],
|
||||||
// Colors
|
// Colors
|
||||||
variantColors[local.variant || "dark"],
|
variantColors[local.variant || "dark"],
|
||||||
|
//Font
|
||||||
|
"leading-none font-semibold",
|
||||||
|
sizeFont[local.size || "default"],
|
||||||
)}
|
)}
|
||||||
{...other}
|
{...other}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -100,13 +100,12 @@ interface RndThumbnailProps {
|
|||||||
height?: number;
|
height?: number;
|
||||||
}
|
}
|
||||||
export const RndThumbnail = (props: RndThumbnailProps) => {
|
export const RndThumbnail = (props: RndThumbnailProps) => {
|
||||||
const { name } = props;
|
const seed = () =>
|
||||||
const seed = Array.from(name).reduce(
|
Array.from(props.name).reduce((acc, char) => acc + char.charCodeAt(0), 0); // Seed from name
|
||||||
(acc, char) => acc + char.charCodeAt(0),
|
const imageSrc = () =>
|
||||||
0,
|
generatePatternedImage(seed(), props.width, props.height);
|
||||||
); // Seed from name
|
|
||||||
const imageSrc = generatePatternedImage(seed, props.width, props.height);
|
return <img src={imageSrc()} alt={props.name} />;
|
||||||
return <img src={imageSrc} alt={name} />;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RndThumbnailShow = () => {
|
export const RndThumbnailShow = () => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* @refresh reload */
|
/* @refresh reload */
|
||||||
import { Portal, render } from "solid-js/web";
|
import { Portal, render } from "solid-js/web";
|
||||||
import { RouteDefinition, Router } from "@solidjs/router";
|
import { Navigate, RouteDefinition, Router } from "@solidjs/router";
|
||||||
|
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
import { QueryClient, QueryClientProvider } from "@tanstack/solid-query";
|
import { QueryClient, QueryClientProvider } from "@tanstack/solid-query";
|
||||||
@@ -46,6 +46,12 @@ export type AppRoute = Omit<RouteDefinition, "children"> & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const routes: AppRoute[] = [
|
export const routes: AppRoute[] = [
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
label: "",
|
||||||
|
hidden: true,
|
||||||
|
component: () => <Navigate href="/machines" />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/machines",
|
path: "/machines",
|
||||||
label: "Machines",
|
label: "Machines",
|
||||||
@@ -139,7 +145,6 @@ export const routes: AppRoute[] = [
|
|||||||
hidden: false,
|
hidden: false,
|
||||||
component: () => <Welcome />,
|
component: () => <Welcome />,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
path: "/api_testing",
|
path: "/api_testing",
|
||||||
label: "api_testing",
|
label: "api_testing",
|
||||||
|
|||||||
@@ -1,33 +1,13 @@
|
|||||||
import { createQuery } from "@tanstack/solid-query";
|
import { JSX } from "solid-js";
|
||||||
import { activeURI } from "../App";
|
import { Typography } from "../components/Typography";
|
||||||
import { callApi } from "../api";
|
|
||||||
import { Accessor, Show } from "solid-js";
|
|
||||||
import { useNavigate } from "@solidjs/router";
|
|
||||||
|
|
||||||
import Icon from "../components/icon";
|
|
||||||
import { Button } from "../components/button";
|
|
||||||
|
|
||||||
interface HeaderProps {
|
interface HeaderProps {
|
||||||
clan_dir: Accessor<string | null>;
|
title: string;
|
||||||
|
toolbar?: JSX.Element;
|
||||||
}
|
}
|
||||||
export const Header = (props: HeaderProps) => {
|
export const Header = (props: HeaderProps) => {
|
||||||
const { clan_dir } = props;
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const query = createQuery(() => ({
|
|
||||||
queryKey: [clan_dir(), "meta"],
|
|
||||||
queryFn: async () => {
|
|
||||||
const curr = clan_dir();
|
|
||||||
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 (
|
return (
|
||||||
<div class="navbar">
|
<div class="navbar border-b px-6 py-4 border-def-3">
|
||||||
<div class="flex-none">
|
<div class="flex-none">
|
||||||
<span class="tooltip tooltip-bottom lg:hidden" data-tip="Menu">
|
<span class="tooltip tooltip-bottom lg:hidden" data-tip="Menu">
|
||||||
<label
|
<label
|
||||||
@@ -38,19 +18,13 @@ export const Header = (props: HeaderProps) => {
|
|||||||
</label>
|
</label>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1"></div>
|
<div class="flex-1">
|
||||||
<div class="flex-none">
|
<Typography hierarchy="title" size="m" weight="medium">
|
||||||
<Show when={activeURI()}>
|
{props.title}
|
||||||
{(d) => (
|
</Typography>
|
||||||
<span class="tooltip tooltip-bottom" data-tip="Clan Settings">
|
</div>
|
||||||
<Button
|
<div class="flex-none items-center justify-center gap-3">
|
||||||
variant="light"
|
{props.toolbar}
|
||||||
onClick={() => navigate(`/clans/${window.btoa(d())}`)}
|
|
||||||
startIcon={<Icon icon="Settings" />}
|
|
||||||
></Button>
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</Show>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import { Component, createEffect, Show } from "solid-js";
|
import { Component, createEffect } from "solid-js";
|
||||||
import { Header } from "./header";
|
|
||||||
import { Sidebar } from "@/src/components/Sidebar";
|
import { Sidebar } from "@/src/components/Sidebar";
|
||||||
import { activeURI, clanList } from "../App";
|
import { clanList } from "../App";
|
||||||
import { RouteSectionProps, useNavigate } from "@solidjs/router";
|
import { RouteSectionProps, useNavigate } from "@solidjs/router";
|
||||||
|
|
||||||
export const Layout: Component<RouteSectionProps> = (props) => {
|
export const Layout: Component<RouteSectionProps> = (props) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
console.log("Layout props", props.location);
|
|
||||||
console.log(
|
console.log(
|
||||||
"empty ClanList, redirect to welcome page",
|
"empty ClanList, redirect to welcome page",
|
||||||
clanList().length === 0,
|
clanList().length === 0,
|
||||||
@@ -25,10 +23,7 @@ export const Layout: Component<RouteSectionProps> = (props) => {
|
|||||||
type="checkbox"
|
type="checkbox"
|
||||||
class="drawer-toggle hidden"
|
class="drawer-toggle hidden"
|
||||||
/>
|
/>
|
||||||
<div class="drawer-content overflow-x-hidden overflow-y-scroll p-2">
|
<div class="drawer-content my-2 ml-8 overflow-x-hidden overflow-y-scroll rounded-lg border bg-def-1 border-def-3">
|
||||||
<Show when={props.location.pathname !== "welcome"}>
|
|
||||||
<Header clan_dir={activeURI} />
|
|
||||||
</Show>
|
|
||||||
{props.children}
|
{props.children}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|||||||
22
pkgs/webview-ui/app/src/routes/machines/avatar.tsx
Normal file
22
pkgs/webview-ui/app/src/routes/machines/avatar.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { RndThumbnail } from "@/src/components/noiseThumbnail";
|
||||||
|
import cx from "classnames";
|
||||||
|
interface AvatarProps {
|
||||||
|
name?: string;
|
||||||
|
class?: string;
|
||||||
|
}
|
||||||
|
export const MachineAvatar = (props: AvatarProps) => {
|
||||||
|
return (
|
||||||
|
<figure>
|
||||||
|
<div class="avatar placeholder">
|
||||||
|
<div
|
||||||
|
class={cx(
|
||||||
|
"rounded-lg border p-2 bg-def-1 border-def-3 size-36",
|
||||||
|
props.class,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<RndThumbnail name={props.name || ""} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</figure>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -3,11 +3,14 @@ import { activeURI } from "@/src/App";
|
|||||||
import { Button } from "@/src/components/button";
|
import { Button } from "@/src/components/button";
|
||||||
import Icon from "@/src/components/icon";
|
import Icon from "@/src/components/icon";
|
||||||
import { TextInput } from "@/src/components/TextInput";
|
import { TextInput } from "@/src/components/TextInput";
|
||||||
|
import { Header } from "@/src/layout/header";
|
||||||
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";
|
||||||
import { createQuery, useQueryClient } from "@tanstack/solid-query";
|
import { useQueryClient } from "@tanstack/solid-query";
|
||||||
import { Match, Switch } from "solid-js";
|
import { Match, Switch } from "solid-js";
|
||||||
import toast from "solid-toast";
|
import toast from "solid-toast";
|
||||||
|
import { MachineAvatar } from "./avatar";
|
||||||
|
import { DynForm } from "@/src/Form/form";
|
||||||
|
|
||||||
type CreateMachineForm = OperationArgs<"create_machine">;
|
type CreateMachineForm = OperationArgs<"create_machine">;
|
||||||
|
|
||||||
@@ -66,15 +69,20 @@ export function CreateMachine() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div class="flex w-full justify-center">
|
<>
|
||||||
<div class="mt-4 w-full max-w-3xl self-stretch px-2">
|
<Header title="Create Machine" />
|
||||||
<span class="px-2">Create new Machine</span>
|
<div class="flex w-full p-4">
|
||||||
|
<div class="mt-4 w-full self-stretch px-2">
|
||||||
<Form onSubmit={handleSubmit}>
|
<Form onSubmit={handleSubmit}>
|
||||||
<Field
|
<Field
|
||||||
name="opts.machine.name"
|
name="opts.machine.name"
|
||||||
validate={[required("This field is required")]}
|
validate={[required("This field is required")]}
|
||||||
>
|
>
|
||||||
{(field, props) => (
|
{(field, props) => (
|
||||||
|
<>
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<MachineAvatar name={field.value} />
|
||||||
|
</div>
|
||||||
<TextInput
|
<TextInput
|
||||||
inputProps={props}
|
inputProps={props}
|
||||||
formStore={formStore}
|
formStore={formStore}
|
||||||
@@ -82,7 +90,9 @@ export function CreateMachine() {
|
|||||||
label={"name"}
|
label={"name"}
|
||||||
error={field.error}
|
error={field.error}
|
||||||
required
|
required
|
||||||
|
placeholder="New_machine"
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
<Field name="opts.machine.description">
|
<Field name="opts.machine.description">
|
||||||
@@ -93,9 +103,41 @@ export function CreateMachine() {
|
|||||||
value={`${field.value}`}
|
value={`${field.value}`}
|
||||||
label={"description"}
|
label={"description"}
|
||||||
error={field.error}
|
error={field.error}
|
||||||
|
placeholder="My awesome machine"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
|
<Field name="opts.machine.tags" type="string[]">
|
||||||
|
{(field, props) => (
|
||||||
|
<div class="p-2">
|
||||||
|
<DynForm
|
||||||
|
initialValues={{ tags: ["all"] }}
|
||||||
|
components={{
|
||||||
|
before: <div>Tags</div>,
|
||||||
|
}}
|
||||||
|
schema={{
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
tags: {
|
||||||
|
type: "array",
|
||||||
|
items: {
|
||||||
|
title: "Tag",
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
uniqueItems: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
<div class="collapse collapse-arrow" tabindex="0">
|
||||||
|
<input type="checkbox" />
|
||||||
|
<div class="collapse-title link font-medium ">
|
||||||
|
Deployment Settings
|
||||||
|
</div>
|
||||||
|
<div class="collapse-content">
|
||||||
<Field name="opts.machine.deploy.targetHost">
|
<Field name="opts.machine.deploy.targetHost">
|
||||||
{(field, props) => (
|
{(field, props) => (
|
||||||
<>
|
<>
|
||||||
@@ -103,12 +145,15 @@ export function CreateMachine() {
|
|||||||
inputProps={props}
|
inputProps={props}
|
||||||
formStore={formStore}
|
formStore={formStore}
|
||||||
value={`${field.value}`}
|
value={`${field.value}`}
|
||||||
label={"Deployment target"}
|
label={"Target"}
|
||||||
error={field.error}
|
error={field.error}
|
||||||
|
placeholder="e.g. 192.168.188.64"
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="mt-12 flex justify-end">
|
<div class="mt-12 flex justify-end">
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
@@ -129,5 +174,6 @@ export function CreateMachine() {
|
|||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import { callApi, ClanService, SuccessData, SuccessQuery } from "@/src/api";
|
import { callApi, SuccessData, SuccessQuery } from "@/src/api";
|
||||||
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 { Button } from "@/src/components/button";
|
||||||
import { FileInput } from "@/src/components/FileInput";
|
import { FileInput } from "@/src/components/FileInput";
|
||||||
import Icon from "@/src/components/icon";
|
import Icon from "@/src/components/icon";
|
||||||
import { RndThumbnail } from "@/src/components/noiseThumbnail";
|
|
||||||
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";
|
||||||
import {
|
import {
|
||||||
@@ -16,9 +13,10 @@ import {
|
|||||||
setValue,
|
setValue,
|
||||||
} from "@modular-forms/solid";
|
} from "@modular-forms/solid";
|
||||||
import { useParams } from "@solidjs/router";
|
import { useParams } from "@solidjs/router";
|
||||||
import { createQuery, useQueryClient } from "@tanstack/solid-query";
|
import { createQuery } from "@tanstack/solid-query";
|
||||||
import { createSignal, For, Show, Switch, Match, JSXElement } from "solid-js";
|
import { createSignal, For, Show } from "solid-js";
|
||||||
import toast from "solid-toast";
|
import toast from "solid-toast";
|
||||||
|
import { MachineAvatar } from "./avatar";
|
||||||
|
|
||||||
type MachineFormInterface = MachineData & {
|
type MachineFormInterface = MachineData & {
|
||||||
sshKey?: File;
|
sshKey?: File;
|
||||||
@@ -305,24 +303,9 @@ const MachineForm = (props: MachineDetailsProps) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Form onSubmit={handleSubmit}>
|
<Form onSubmit={handleSubmit}>
|
||||||
<figure>
|
|
||||||
<div
|
|
||||||
class="avatar placeholder"
|
|
||||||
classList={
|
|
||||||
{
|
|
||||||
// online: onlineStatusQuery.data === "Online",
|
|
||||||
// offline: onlineStatusQuery.data === "Offline",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div class="w-32 rounded-lg border p-2 bg-def-4 border-inv-3">
|
|
||||||
<RndThumbnail name={machineName() || "M"} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</figure>
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<span class="text-xl text-primary-800">General</span>
|
<span class="text-xl text-primary-800">General</span>
|
||||||
|
<MachineAvatar name={machineName()} />
|
||||||
<Field name="machine.name">
|
<Field name="machine.name">
|
||||||
{(field, props) => (
|
{(field, props) => (
|
||||||
<TextInput
|
<TextInput
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { createQuery, useQueryClient } from "@tanstack/solid-query";
|
|||||||
import { useNavigate } from "@solidjs/router";
|
import { useNavigate } from "@solidjs/router";
|
||||||
import { Button } from "@/src/components/button";
|
import { Button } from "@/src/components/button";
|
||||||
import Icon from "@/src/components/icon";
|
import Icon from "@/src/components/icon";
|
||||||
|
import { Header } from "@/src/layout/header";
|
||||||
|
|
||||||
type MachinesModel = Extract<
|
type MachinesModel = Extract<
|
||||||
OperationResponse<"list_inventory_machines">,
|
OperationResponse<"list_inventory_machines">,
|
||||||
@@ -65,23 +66,50 @@ export const MachineListView: Component = () => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
<div class="tooltip tooltip-bottom" data-tip="Open Clan"></div>
|
<Header
|
||||||
<div class="tooltip tooltip-bottom" data-tip="Refresh">
|
title="Your Machines"
|
||||||
|
toolbar={
|
||||||
|
<>
|
||||||
|
<span class="tooltip tooltip-bottom" data-tip="Reload">
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="light"
|
||||||
|
size="s"
|
||||||
onClick={() => refresh()}
|
onClick={() => refresh()}
|
||||||
startIcon={<Icon icon="Reload" />}
|
startIcon={<Icon icon="Reload" />}
|
||||||
></Button>
|
></Button>
|
||||||
</div>
|
</span>
|
||||||
|
|
||||||
<div class="tooltip tooltip-bottom" data-tip="Create machine">
|
<div class="border border-def-3">
|
||||||
|
<span class="tooltip tooltip-bottom" data-tip="List View">
|
||||||
|
<Button
|
||||||
|
variant="dark"
|
||||||
|
size="s"
|
||||||
|
startIcon={<Icon icon="List" />}
|
||||||
|
></Button>
|
||||||
|
</span>
|
||||||
|
<span class="tooltip tooltip-bottom" data-tip="Grid View">
|
||||||
<Button
|
<Button
|
||||||
variant="light"
|
variant="light"
|
||||||
onClick={() => navigate("create")}
|
size="s"
|
||||||
startIcon={<Icon icon="Plus" />}
|
startIcon={<Icon icon="Grid" />}
|
||||||
></Button>
|
></Button>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<span class="tooltip tooltip-bottom" data-tip="New Machine">
|
||||||
|
<Button
|
||||||
|
onClick={() => navigate("create")}
|
||||||
|
size="s"
|
||||||
|
variant="light"
|
||||||
|
startIcon={<Icon icon="Plus" />}
|
||||||
|
>
|
||||||
|
New Machine
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
{/* <Show when={filter()}> */}
|
{/* <Show when={filter()}> */}
|
||||||
<div class="my-1 flex w-full gap-2 p-2">
|
<div class="my-1 flex w-full gap-2 p-2">
|
||||||
<div class="size-6 p-1">
|
<div class="size-6 p-1">
|
||||||
@@ -156,5 +184,6 @@ export const MachineListView: Component = () => {
|
|||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user