Clan-app: Add loading animations & improve async data handling
This commit is contained in:
@@ -1,86 +1,67 @@
|
||||
import { createSignal, Match, Show, Switch } from "solid-js";
|
||||
import { ErrorData, pyApi, SuccessData } from "../api";
|
||||
import { Accessor, createEffect, Show } from "solid-js";
|
||||
import { SuccessData } from "../api";
|
||||
import { Menu } from "./Menu";
|
||||
import { setRoute } from "../App";
|
||||
|
||||
type MachineDetails = SuccessData<"list_inventory_machines">["data"][string];
|
||||
|
||||
interface MachineListItemProps {
|
||||
name: string;
|
||||
info?: MachineDetails;
|
||||
nixOnly?: boolean;
|
||||
}
|
||||
|
||||
type HWInfo = Record<string, SuccessData<"show_machine_hardware_info">["data"]>;
|
||||
type DeploymentInfo = Record<
|
||||
string,
|
||||
SuccessData<"show_machine_deployment_target">["data"]
|
||||
>;
|
||||
|
||||
type MachineErrors = Record<string, ErrorData<"show_machine">["errors"]>;
|
||||
|
||||
const [hwInfo, setHwInfo] = createSignal<HWInfo>({});
|
||||
|
||||
const [deploymentInfo, setDeploymentInfo] = createSignal<DeploymentInfo>({});
|
||||
|
||||
const [errors, setErrors] = createSignal<MachineErrors>({});
|
||||
|
||||
// pyApi.show_machine_hardware_info.receive((r) => {
|
||||
// const { op_key } = r;
|
||||
// if (r.status === "error") {
|
||||
// console.error(r.errors);
|
||||
// if (op_key) {
|
||||
// setHwInfo((d) => ({ ...d, [op_key]: { system: null } }));
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
// if (op_key) {
|
||||
// setHwInfo((d) => ({ ...d, [op_key]: r.data }));
|
||||
// }
|
||||
// });
|
||||
|
||||
// pyApi.show_machine_deployment_target.receive((r) => {
|
||||
// const { op_key } = r;
|
||||
// if (r.status === "error") {
|
||||
// console.error(r.errors);
|
||||
// if (op_key) {
|
||||
// setDeploymentInfo((d) => ({ ...d, [op_key]: null }));
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
// if (op_key) {
|
||||
// setDeploymentInfo((d) => ({ ...d, [op_key]: r.data }));
|
||||
// }
|
||||
// });
|
||||
|
||||
export const MachineListItem = (props: MachineListItemProps) => {
|
||||
const { name, info } = props;
|
||||
const { name, info, nixOnly } = props;
|
||||
|
||||
return (
|
||||
<li>
|
||||
<div class="card card-side m-2 bg-base-100 shadow-lg">
|
||||
<div class="card card-side m-2 bg-base-200">
|
||||
<figure class="pl-2">
|
||||
<span class="material-icons content-center text-5xl">
|
||||
<span
|
||||
class="material-icons content-center text-5xl"
|
||||
classList={{
|
||||
"text-neutral-500": nixOnly,
|
||||
}}
|
||||
>
|
||||
devices_other
|
||||
</span>
|
||||
</figure>
|
||||
<div class="card-body flex-row justify-between">
|
||||
<div class="card-body flex-row justify-between ">
|
||||
<div class="flex flex-col">
|
||||
<h2 class="card-title">{name}</h2>
|
||||
<h2
|
||||
class="card-title"
|
||||
classList={{
|
||||
"text-neutral-500": nixOnly,
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
</h2>
|
||||
<div class="text-slate-600">
|
||||
<Show when={info}>{(d) => d()?.description}</Show>
|
||||
</div>
|
||||
<div class="flex flex-row flex-wrap gap-4 py-2"></div>
|
||||
{/* Show only the first error at the bottom */}
|
||||
<Show when={errors()[name]?.[0]}>
|
||||
{(error) => (
|
||||
<div class="badge badge-error py-4">
|
||||
Error: {error().message}: {error().description}
|
||||
</div>
|
||||
)}
|
||||
</Show>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-ghost">
|
||||
<span class="material-icons">more_vert</span>
|
||||
</button>
|
||||
<Menu
|
||||
popoverid={`menu-${props.name}`}
|
||||
label={<span class="material-icons">more_vert</span>}
|
||||
>
|
||||
<ul class="menu z-[1] w-52 rounded-box bg-base-100 p-2 shadow">
|
||||
<li>
|
||||
<a
|
||||
onClick={() => {
|
||||
setRoute("machines/edit");
|
||||
}}
|
||||
>
|
||||
Edit
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a>Deploy</a>
|
||||
</li>
|
||||
</ul>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
84
pkgs/webview-ui/app/src/components/Menu.tsx
Normal file
84
pkgs/webview-ui/app/src/components/Menu.tsx
Normal file
@@ -0,0 +1,84 @@
|
||||
import { children, Component, createSignal, type JSX } from "solid-js";
|
||||
import { useFloating } from "@/src/floating";
|
||||
import {
|
||||
autoUpdate,
|
||||
flip,
|
||||
hide,
|
||||
offset,
|
||||
Placement,
|
||||
shift,
|
||||
} from "@floating-ui/dom";
|
||||
import cx from "classnames";
|
||||
|
||||
interface MenuProps {
|
||||
/**
|
||||
* Used by the html API to associate the popover with the dispatcher button
|
||||
*/
|
||||
popoverid: string;
|
||||
|
||||
label: JSX.Element;
|
||||
|
||||
children?: JSX.Element;
|
||||
buttonProps?: JSX.ButtonHTMLAttributes<HTMLButtonElement>;
|
||||
buttonClass?: string;
|
||||
/**
|
||||
* @default "bottom"
|
||||
*/
|
||||
placement?: Placement;
|
||||
}
|
||||
export const Menu = (props: MenuProps) => {
|
||||
const c = children(() => props.children);
|
||||
const [reference, setReference] = createSignal<HTMLElement>();
|
||||
const [floating, setFloating] = createSignal<HTMLElement>();
|
||||
|
||||
// `position` is a reactive object.
|
||||
const position = useFloating(reference, floating, {
|
||||
placement: "bottom",
|
||||
|
||||
// pass options. Ensure the cleanup function is returned.
|
||||
whileElementsMounted: (reference, floating, update) =>
|
||||
autoUpdate(reference, floating, update, {
|
||||
animationFrame: true,
|
||||
}),
|
||||
middleware: [
|
||||
offset(5),
|
||||
shift(),
|
||||
flip(),
|
||||
|
||||
hide({
|
||||
strategy: "referenceHidden",
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
popovertarget={props.popoverid}
|
||||
popovertargetaction="toggle"
|
||||
ref={setReference}
|
||||
class={cx(
|
||||
"btn btn-ghost btn-outline join-item btn-sm",
|
||||
props.buttonClass,
|
||||
)}
|
||||
{...props.buttonProps}
|
||||
>
|
||||
{props.label}
|
||||
</button>
|
||||
<div
|
||||
popover="auto"
|
||||
id={props.popoverid}
|
||||
ref={setFloating}
|
||||
style={{
|
||||
margin: 0,
|
||||
position: position.strategy,
|
||||
top: `${position.y ?? 0}px`,
|
||||
left: `${position.x ?? 0}px`,
|
||||
}}
|
||||
class="bg-transparent"
|
||||
>
|
||||
{c()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user