diff --git a/app/routes/machines/components/machine-row.tsx b/app/routes/machines/components/machine-row.tsx
index 2c7f662..aa4dea9 100644
--- a/app/routes/machines/components/machine-row.tsx
+++ b/app/routes/machines/components/machine-row.tsx
@@ -9,7 +9,7 @@ import { ExpiryTag } from '~/components/tags/Expiry';
import { HeadplaneAgentTag } from '~/components/tags/HeadplaneAgent';
import { SubnetTag } from '~/components/tags/Subnet';
import { TailscaleSSHTag } from '~/components/tags/TailscaleSSH';
-import type { Machine, User } from '~/types';
+import type { User } from '~/types';
import cn from '~/utils/cn';
import * as hinfo from '~/utils/host-info';
import { PopulatedNode } from '~/utils/node-info';
@@ -22,7 +22,7 @@ interface Props {
isAgent?: boolean;
magic?: string;
isDisabled?: boolean;
- nodeList?: Machine[];
+ existingTags?: string[];
}
export default function MachineRow({
@@ -31,7 +31,7 @@ export default function MachineRow({
isAgent,
magic,
isDisabled,
- nodeList,
+ existingTags,
}: Props) {
const uiTags = useMemo(() => {
const tags = uiTagsForNode(node, isAgent);
@@ -148,10 +148,10 @@ export default function MachineRow({
|
diff --git a/app/routes/machines/components/menu.tsx b/app/routes/machines/components/menu.tsx
index 2da92ae..cb0d8d6 100644
--- a/app/routes/machines/components/menu.tsx
+++ b/app/routes/machines/components/menu.tsx
@@ -2,7 +2,7 @@ import { Cog, Ellipsis, SquareTerminal } from 'lucide-react';
import { useState } from 'react';
import Button from '~/components/Button';
import Menu from '~/components/Menu';
-import type { Machine, User } from '~/types';
+import type { User } from '~/types';
import cn from '~/utils/cn';
import { PopulatedNode } from '~/utils/node-info';
import Delete from '../dialogs/delete';
@@ -18,7 +18,7 @@ interface MenuProps {
magic?: string;
isFullButton?: boolean;
isDisabled?: boolean;
- nodeList?: Machine[];
+ existingTags?: string[];
}
type Modal = 'rename' | 'expire' | 'remove' | 'routes' | 'move' | 'tags' | null;
@@ -29,7 +29,7 @@ export default function MachineMenu({
users,
isFullButton,
isDisabled,
- nodeList,
+ existingTags,
}: MenuProps) {
const [modal, setModal] = useState(null);
const supportsTailscaleSSH =
@@ -77,9 +77,9 @@ export default function MachineMenu({
)}
{modal === 'tags' && (
{
if (!isOpen) setModal(null);
}}
diff --git a/app/routes/machines/dialogs/tags.tsx b/app/routes/machines/dialogs/tags.tsx
index 504d5bc..bbc280f 100644
--- a/app/routes/machines/dialogs/tags.tsx
+++ b/app/routes/machines/dialogs/tags.tsx
@@ -12,14 +12,14 @@ interface TagsProps {
machine: Machine;
isOpen: boolean;
setIsOpen: (isOpen: boolean) => void;
- nodeList?: Machine[];
+ existingTags?: string[];
}
export default function Tags({
machine,
isOpen,
setIsOpen,
- nodeList,
+ existingTags,
}: TagsProps) {
const [tags, setTags] = useState(machine.forcedTags);
const [tag, setTag] = useState('tag:');
@@ -28,13 +28,7 @@ export default function Tags({
}, [tag, tags]);
const validNodeTags = useMemo(() => {
- if (!nodeList?.length) return [];
- const allNodeTags = Array.from(
- new Set(
- nodeList.flatMap((node) => [...node.validTags, ...node.forcedTags]),
- ),
- ).sort();
- return allNodeTags.filter((nodeTag) => !tags.includes(nodeTag));
+ return existingTags?.filter((nodeTag) => !tags.includes(nodeTag)) || [];
}, [tags]);
return (
diff --git a/app/routes/machines/machine.tsx b/app/routes/machines/machine.tsx
index 86b7cf0..0b751f0 100644
--- a/app/routes/machines/machine.tsx
+++ b/app/routes/machines/machine.tsx
@@ -10,7 +10,7 @@ import StatusCircle from '~/components/StatusCircle';
import Tooltip from '~/components/Tooltip';
import cn from '~/utils/cn';
import { getOSInfo, getTSVersion } from '~/utils/host-info';
-import { mapNodes } from '~/utils/node-info';
+import { mapNodes, sortNodeTags } from '~/utils/node-info';
import type { Route } from './+types/machine';
import { mapTagsToComponents, uiTagsForNode } from './components/machine-row';
import MenuOptions from './components/menu';
@@ -35,11 +35,8 @@ export async function loader({ request, params, context }: Route.LoaderArgs) {
}
const api = context.hsApi.getRuntimeClient(session.api_key);
- const [node, users, allNodes] = await Promise.all([
- api.getNode(params.id),
- api.getUsers(),
- api.getNodes(),
- ]);
+ const [nodes, users] = await Promise.all([api.getNodes(), api.getUsers()]);
+ const node = nodes.find((node) => node.id === params.id);
const lookup = await context.agents?.lookup([node.nodeKey]);
const [enhancedNode] = mapNodes([node], lookup);
@@ -54,14 +51,14 @@ export async function loader({ request, params, context }: Route.LoaderArgs) {
magic,
agent: context.agents?.agentID(),
stats: lookup?.[enhancedNode.nodeKey],
- nodeList: allNodes,
+ existingTags: sortNodeTags(nodes),
};
}
export const action = machineAction;
export default function Page({
- loaderData: { node, tags, users, magic, agent, stats, nodeList },
+ loaderData: { node, tags, users, magic, agent, stats, existingTags },
}: Route.ComponentProps) {
const [showRouting, setShowRouting] = useState(false);
@@ -90,10 +87,10 @@ export default function Page({
diff --git a/app/routes/machines/overview.tsx b/app/routes/machines/overview.tsx
index d3c12d0..3b23f8a 100644
--- a/app/routes/machines/overview.tsx
+++ b/app/routes/machines/overview.tsx
@@ -4,7 +4,7 @@ import Link from '~/components/Link';
import Tooltip from '~/components/Tooltip';
import { Capabilities } from '~/server/web/roles';
import cn from '~/utils/cn';
-import { mapNodes } from '~/utils/node-info';
+import { mapNodes, sortNodeTags } from '~/utils/node-info';
import type { Route } from './+types/overview';
import MachineRow from './components/machine-row';
import NewMachine from './dialogs/new';
@@ -126,6 +126,7 @@ export default function Page({ loaderData }: Route.ComponentProps) {
>
{loaderData.populatedNodes.map((node) => (
))}
diff --git a/app/utils/node-info.ts b/app/utils/node-info.ts
index ec0598e..3e54f3c 100644
--- a/app/utils/node-info.ts
+++ b/app/utils/node-info.ts
@@ -52,3 +52,14 @@ export function mapNodes(
};
});
}
+
+export function sortNodeTags(nodes: Machine[]): string[] {
+ return Array.from(
+ new Set(
+ nodes.flatMap(({ validTags, forcedTags }) => [
+ ...validTags,
+ ...forcedTags,
+ ]),
+ ),
+ ).sort();
+}