From 51950329a3412446b420c0db849e24e8a381191d Mon Sep 17 00:00:00 2001 From: Timo Date: Tue, 6 May 2025 07:51:27 +0200 Subject: [PATCH] machine-item: updates design and unifies --- .../app/src/components/MachineListItem.tsx | 224 ------------------ .../app/src/components/Typography/index.tsx | 1 - .../machine-list-item/css/index.css | 77 ++++++ .../components/machine-list-item/index.tsx | 135 +++++++++++ .../app/src/routes/machines/list.tsx | 19 +- .../app/src/routes/modules/list.tsx | 2 +- 6 files changed, 217 insertions(+), 241 deletions(-) delete mode 100644 pkgs/webview-ui/app/src/components/MachineListItem.tsx create mode 100644 pkgs/webview-ui/app/src/components/machine-list-item/css/index.css create mode 100644 pkgs/webview-ui/app/src/components/machine-list-item/index.tsx diff --git a/pkgs/webview-ui/app/src/components/MachineListItem.tsx b/pkgs/webview-ui/app/src/components/MachineListItem.tsx deleted file mode 100644 index c4f67a3b7..000000000 --- a/pkgs/webview-ui/app/src/components/MachineListItem.tsx +++ /dev/null @@ -1,224 +0,0 @@ -import { createSignal, For, Setter, Show } from "solid-js"; -import { callApi, SuccessQuery } from "../api"; -import { Menu } from "./Menu"; -import { activeURI } from "../App"; -import toast from "solid-toast"; -import { A, useNavigate } from "@solidjs/router"; -import { RndThumbnail } from "./noiseThumbnail"; -import Icon from "./icon"; -import { Filter } from "../routes/machines"; -import { Typography } from "./Typography"; -import { Button } from "./button"; - -type MachineDetails = SuccessQuery<"list_machines">["data"][string]; - -interface MachineListItemProps { - name: string; - info?: MachineDetails; - nixOnly?: boolean; - setFilter: Setter; -} - -export const MachineListItem = (props: MachineListItemProps) => { - const { name, info, nixOnly } = props; - - // Bootstrapping - const [installing, setInstalling] = createSignal(false); - - // Later only updates - const [updating, setUpdating] = createSignal(false); - - const navigate = useNavigate(); - - const handleInstall = async () => { - if (!info?.deploy?.targetHost || installing()) { - return; - } - - const active_clan = activeURI(); - if (!active_clan) { - toast.error("No active clan selected"); - return; - } - if (!info?.deploy?.targetHost) { - toast.error( - "Machine does not have a target host. Specify where the machine should be deployed.", - ); - return; - } - setInstalling(true); - await toast.promise( - callApi("install_machine", { - opts: { - machine: { - name: name, - flake: { - identifier: active_clan, - }, - override_target_host: info?.deploy.targetHost, - }, - no_reboot: true, - debug: true, - nix_options: [], - password: null, - }, - }), - { - loading: "Installing...", - success: "Installed", - error: "Failed to install", - }, - ); - setInstalling(false); - }; - - const handleUpdate = async () => { - if (!info?.deploy?.targetHost || installing()) { - return; - } - - const active_clan = activeURI(); - if (!active_clan) { - toast.error("No active clan selected"); - return; - } - if (!info?.deploy.targetHost) { - toast.error( - "Machine does not have a target host. Specify where the machine should be deployed.", - ); - return; - } - setUpdating(true); - await toast.promise( - callApi("update_machines", { - base_path: active_clan, - machines: [ - { - name: name, - deploy: { - targetHost: info?.deploy.targetHost, - }, - }, - ], - }), - { - loading: "Updating...", - success: "Updated", - error: "Failed to update", - }, - ); - setUpdating(false); - }; - return ( -
-
- -
-
-
- - - {name} - - -
-
- - - - - - {(d) => d()?.description || "no description"} - - -
- -
- } - > -
    -
  • - -
  • -
  • - -
  • -
  • - -
  • -
-
-
-
- {/*
- - {(d) => ( - <> - - {(tags) => ( - - - {(tag) => ( - - )} - - - )} - - {d()?.deploy?.targetHost} - - )} - -
*/} -
-
-
- ); -}; diff --git a/pkgs/webview-ui/app/src/components/Typography/index.tsx b/pkgs/webview-ui/app/src/components/Typography/index.tsx index fcc829ab0..e55991473 100644 --- a/pkgs/webview-ui/app/src/components/Typography/index.tsx +++ b/pkgs/webview-ui/app/src/components/Typography/index.tsx @@ -84,7 +84,6 @@ interface _TypographyProps { inverted?: boolean; tag?: Tag; class?: string; - classList?: Record; } export const Typography = (props: _TypographyProps) => { diff --git a/pkgs/webview-ui/app/src/components/machine-list-item/css/index.css b/pkgs/webview-ui/app/src/components/machine-list-item/css/index.css new file mode 100644 index 000000000..341b601cf --- /dev/null +++ b/pkgs/webview-ui/app/src/components/machine-list-item/css/index.css @@ -0,0 +1,77 @@ +.machine-item { + @apply col-span-1 flex flex-col items-center; + + position: relative; + padding: theme(padding.2); + + cursor: pointer; +} + +.machine-item__thumb-wrapper { + position: relative; + padding: theme(padding.4); + + border-radius: theme(borderRadius.md); + overflow: hidden; +} + +.machine-item__thumb { + @apply rounded-md bg-secondary-100; + + position: relative; + z-index: 20; + overflow: hidden; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05); + + transition: transform 0.24s ease-in-out; +} + +.machine-item__header { + @apply flex flex-col justify-center items-center; + + position: relative; + z-index: 20; + + transition: transform 0.18s 0.04s ease-in-out; +} + +.machine-item__pseudo { + @apply bg-secondary-50; + + position: absolute; + z-index: 10; + top: 0; + left: 0; + width: 100%; + height: 100%; + border: 1px solid theme(borderColor.secondary.100); + border-radius: theme(borderRadius.md); + + transition: + transform 0.16s ease-in-out, + opacity 0.08s ease-in-out; +} + +.machine-item:hover { + & .machine-item__pseudo { + transform: scale(1); + opacity: 1; + } + + & .machine-item__thumb { + transform: scale(1.02); + box-shadow: + 0 4px 6px rgba(0, 0, 0, 0.1), + 0 8px 20px rgba(0, 0, 0, 0.15), + 0 12px 40px rgba(0, 0, 0, 0.08); + } + + & .machine-item__header { + transform: translateY(4px); + } +} + +.machine-item:not(:hover) .machine-item__pseudo { + transform: scale(0.94); + opacity: 0; +} diff --git a/pkgs/webview-ui/app/src/components/machine-list-item/index.tsx b/pkgs/webview-ui/app/src/components/machine-list-item/index.tsx new file mode 100644 index 000000000..38b1d122c --- /dev/null +++ b/pkgs/webview-ui/app/src/components/machine-list-item/index.tsx @@ -0,0 +1,135 @@ +import { createSignal, For, Setter, Show } from "solid-js"; +import { callApi, SuccessQuery } from "../../api"; + +import { activeURI } from "../../App"; +import toast from "solid-toast"; +import { A, useNavigate } from "@solidjs/router"; +import { RndThumbnail } from "../noiseThumbnail"; + +import { Filter } from "../../routes/machines"; +import { Typography } from "../Typography"; +import "./css/index.css"; + +type MachineDetails = SuccessQuery<"list_machines">["data"][string]; + +interface MachineListItemProps { + name: string; + info?: MachineDetails; + nixOnly?: boolean; + setFilter: Setter; +} + +export const MachineListItem = (props: MachineListItemProps) => { + const { name, info, nixOnly } = props; + + // Bootstrapping + const [installing, setInstalling] = createSignal(false); + + // Later only updates + const [updating, setUpdating] = createSignal(false); + + const navigate = useNavigate(); + + const handleInstall = async () => { + if (!info?.deploy?.targetHost || installing()) { + return; + } + + const active_clan = activeURI(); + if (!active_clan) { + toast.error("No active clan selected"); + return; + } + if (!info?.deploy?.targetHost) { + toast.error( + "Machine does not have a target host. Specify where the machine should be deployed.", + ); + return; + } + setInstalling(true); + await toast.promise( + callApi("install_machine", { + opts: { + machine: { + name: name, + flake: { + identifier: active_clan, + }, + override_target_host: info?.deploy.targetHost, + }, + no_reboot: true, + debug: true, + nix_options: [], + password: null, + }, + }), + { + loading: "Installing...", + success: "Installed", + error: "Failed to install", + }, + ); + setInstalling(false); + }; + + const handleUpdate = async () => { + if (!info?.deploy?.targetHost || installing()) { + return; + } + + const active_clan = activeURI(); + if (!active_clan) { + toast.error("No active clan selected"); + return; + } + if (!info?.deploy.targetHost) { + toast.error( + "Machine does not have a target host. Specify where the machine should be deployed.", + ); + return; + } + setUpdating(true); + await toast.promise( + callApi("update_machines", { + base_path: active_clan, + machines: [ + { + name: name, + deploy: { + targetHost: info?.deploy.targetHost, + }, + }, + ], + }), + { + loading: "Updating...", + success: "Updated", + error: "Failed to update", + }, + ); + setUpdating(false); + }; + return ( +
+ + + ); +}; diff --git a/pkgs/webview-ui/app/src/routes/machines/list.tsx b/pkgs/webview-ui/app/src/routes/machines/list.tsx index 903954fd0..3d211003b 100644 --- a/pkgs/webview-ui/app/src/routes/machines/list.tsx +++ b/pkgs/webview-ui/app/src/routes/machines/list.tsx @@ -9,7 +9,7 @@ import { import { activeURI } from "@/src/App"; import { callApi, OperationResponse } from "@/src/api"; import toast from "solid-toast"; -import { MachineListItem } from "@/src/components/MachineListItem"; +import { MachineListItem } from "@/src/components/machine-list-item"; import { createQuery, useQueryClient } from "@tanstack/solid-query"; import { useNavigate } from "@solidjs/router"; import { Button } from "@/src/components/button"; @@ -114,16 +114,6 @@ export const MachineListView: Component = () => { />
-
-
- -
-
{(tag) => (
- {/* */} {/* Loading skeleton */} @@ -180,10 +169,10 @@ export const MachineListView: Component = () => {
diff --git a/pkgs/webview-ui/app/src/routes/modules/list.tsx b/pkgs/webview-ui/app/src/routes/modules/list.tsx index 9bf0c035f..ddb627810 100644 --- a/pkgs/webview-ui/app/src/routes/modules/list.tsx +++ b/pkgs/webview-ui/app/src/routes/modules/list.tsx @@ -121,7 +121,7 @@ export const ModuleList = () => { return ( <>