Merge pull request 'feat(ui): display add machine in sidebar when machine list is empty' (#5027) from ui/refine-add-machine into main

Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/5027
This commit is contained in:
brianmcgee
2025-08-29 12:27:33 +00:00
2 changed files with 68 additions and 17 deletions

View File

@@ -3,12 +3,13 @@ import { A } from "@solidjs/router";
import { Accordion } from "@kobalte/core/accordion";
import Icon from "../Icon/Icon";
import { Typography } from "@/src/components/Typography/Typography";
import { For, useContext } from "solid-js";
import { For, Show, useContext } from "solid-js";
import { MachineStatus } from "@/src/components/MachineStatus/MachineStatus";
import { buildMachinePath, useClanURI } from "@/src/hooks/clan";
import { useMachineStateQuery } from "@/src/hooks/queries";
import { SidebarProps } from "./Sidebar";
import { ClanContext } from "@/src/routes/Clan/Clan";
import { Button } from "../Button/Button";
interface MachineProps {
clanURI: string;
@@ -71,6 +72,15 @@ export const SidebarBody = (props: SidebarProps) => {
// we want them all to be open by default
const defaultAccordionValues = ["your-machines", ...sectionLabels];
const machines = () => {
if (!ctx.machinesQuery.isSuccess) {
return {};
}
const result = ctx.machinesQuery.data;
return Object.keys(result).length > 0 ? result : undefined;
};
return (
<div class="sidebar-body">
<Accordion
@@ -100,18 +110,42 @@ export const SidebarBody = (props: SidebarProps) => {
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content class="content">
<nav>
<For each={Object.entries(ctx.machinesQuery.data || {})}>
{([id, machine]) => (
<MachineRoute
clanURI={clanURI}
machineID={id}
name={machine.name || id}
serviceCount={0}
/>
)}
</For>
</nav>
<Show
when={machines()}
fallback={
<div class="flex w-full flex-col items-center justify-center gap-2.5">
<Typography
hierarchy="body"
size="s"
weight="medium"
inverted
>
No machines yet
</Typography>
<Button
hierarchy="primary"
size="s"
startIcon="Machine"
onClick={() => ctx.setShowAddMachine(true)}
>
Add machine
</Button>
</div>
}
>
<nav>
<For each={Object.entries(machines()!)}>
{([id, machine]) => (
<MachineRoute
clanURI={clanURI}
machineID={id}
name={machine.name || id}
serviceCount={0}
/>
)}
</For>
</nav>
</Show>
</Accordion.Content>
</Accordion.Item>

View File

@@ -8,6 +8,7 @@ import {
on,
onMount,
Show,
Signal,
useContext,
} from "solid-js";
import {
@@ -49,6 +50,9 @@ interface ClanContextProps {
isLoading(): boolean;
isError(): boolean;
showAddMachine(): boolean;
setShowAddMachine(value: boolean): void;
}
class DefaultClanContext implements ClanContextProps {
@@ -62,6 +66,8 @@ class DefaultClanContext implements ClanContextProps {
allQueries: UseQueryResult[];
showAddMachineSignal: Signal<boolean>;
constructor(
clanURI: string,
machinesQuery: MachinesQueryResult,
@@ -76,6 +82,8 @@ class DefaultClanContext implements ClanContextProps {
this.allClansQueries = [activeClanQuery, ...otherClanQueries];
this.allQueries = [machinesQuery, activeClanQuery, ...otherClanQueries];
this.showAddMachineSignal = createSignal(false);
}
isLoading(): boolean {
@@ -85,6 +93,16 @@ class DefaultClanContext implements ClanContextProps {
isError(): boolean {
return this.activeClanQuery.isError;
}
setShowAddMachine(value: boolean) {
const [_, setShow] = this.showAddMachineSignal;
setShow(value);
}
showAddMachine(): boolean {
const [show, _] = this.showAddMachineSignal;
return show();
}
}
export const ClanContext = createContext<ClanContextProps>();
@@ -140,7 +158,6 @@ const ClanSceneController = (props: RouteSectionProps) => {
const [showService, setShowService] = createSignal(false);
const [showCreate, setShowCreate] = createSignal(false);
const [currentPromise, setCurrentPromise] = createSignal<{
resolve: ({ id }: { id: string }) => void;
reject: (err: unknown) => void;
@@ -148,7 +165,7 @@ const ClanSceneController = (props: RouteSectionProps) => {
const onCreate = async (): Promise<{ id: string }> => {
return new Promise((resolve, reject) => {
setShowCreate(true);
ctx.setShowAddMachine(true);
setCurrentPromise({ resolve, reject });
});
};
@@ -244,7 +261,7 @@ const ClanSceneController = (props: RouteSectionProps) => {
<Show when={loadingError()}>
<ListClansModal error={loadingError()} />
</Show>
<Show when={showCreate()}>
<Show when={ctx.showAddMachine()}>
<AddMachine
onCreated={async (id) => {
const promise = currentPromise();
@@ -255,7 +272,7 @@ const ClanSceneController = (props: RouteSectionProps) => {
}
}}
onClose={() => {
setShowCreate(false);
ctx.setShowAddMachine(false);
}}
/>
</Show>