chore(machines): conditionally show owner change menu item based on version

Adds conditional rendering for the "Change owner" menu item in machine management, hiding it for versions 0.28.0-beta.1 and above. This prevents users from attempting to change node ownership on newer versions where this functionality is not supported. The change involves passing a new `supportsNodeOwnerChange` prop through the component hierarchy and updating the menu rendering logic accordingly.
This commit is contained in:
Paul Kronenwetter
2026-01-24 17:30:48 -05:00
parent fad0c99fc9
commit 0d0b5f88a1
4 changed files with 13 additions and 2 deletions
@@ -27,6 +27,7 @@ interface Props {
magic?: string;
isDisabled?: boolean;
existingTags?: string[];
supportsNodeOwnerChange: boolean;
}
export default function MachineRow({
@@ -36,6 +37,7 @@ export default function MachineRow({
magic,
isDisabled,
existingTags,
supportsNodeOwnerChange,
}: Props) {
const uiTags = useMemo(() => uiTagsForNode(node, isAgent), [node, isAgent]);
@@ -142,6 +144,7 @@ export default function MachineRow({
magic={magic}
node={node}
users={users}
supportsNodeOwnerChange={supportsNodeOwnerChange}
/>
</td>
</tr>
+3 -1
View File
@@ -19,6 +19,7 @@ interface MenuProps {
isFullButton?: boolean;
isDisabled?: boolean;
existingTags?: string[];
supportsNodeOwnerChange: boolean;
}
type Modal = 'rename' | 'expire' | 'remove' | 'routes' | 'move' | 'tags' | null;
@@ -30,6 +31,7 @@ export default function MachineMenu({
isFullButton,
isDisabled,
existingTags,
supportsNodeOwnerChange,
}: MenuProps) {
const [modal, setModal] = useState<Modal>(null);
const supportsTailscaleSSH =
@@ -164,7 +166,7 @@ export default function MachineMenu({
<Menu.Item key="rename">Edit machine name</Menu.Item>
<Menu.Item key="routes">Edit route settings</Menu.Item>
<Menu.Item key="tags">Edit ACL tags</Menu.Item>
<Menu.Item key="move">Change owner</Menu.Item>
{supportsNodeOwnerChange && <Menu.Item key="move">Change owner</Menu.Item>}
</Menu.Section>
<Menu.Section>
<Menu.Item key="expire" textValue="Expire">
+4 -1
View File
@@ -44,6 +44,7 @@ export async function loader({ request, params, context }: Route.LoaderArgs) {
const lookup = await context.agents?.lookup([node.nodeKey]);
const [enhancedNode] = mapNodes([node], lookup);
const tags = [...node.tags].sort();
const supportsNodeOwnerChange = ! context.hsApi.clientHelpers.isAtleast("0.28.0-beta.1");
return {
node: enhancedNode,
@@ -53,13 +54,14 @@ export async function loader({ request, params, context }: Route.LoaderArgs) {
agent: context.agents?.agentID(),
stats: lookup?.[enhancedNode.nodeKey],
existingTags: sortNodeTags(nodes),
supportsNodeOwnerChange: supportsNodeOwnerChange,
};
}
export const action = machineAction;
export default function Page({
loaderData: { node, tags, users, magic, agent, stats, existingTags },
loaderData: { node, tags, users, magic, agent, stats, existingTags, supportsNodeOwnerChange },
}: Route.ComponentProps) {
const [showRouting, setShowRouting] = useState(false);
@@ -93,6 +95,7 @@ export default function Page({
magic={magic}
node={node}
users={users}
supportsNodeOwnerChange={supportsNodeOwnerChange}
/>
</div>
<div className="mb-4 flex gap-1">
+3
View File
@@ -48,6 +48,7 @@ export async function loader({ request, context }: Route.LoaderArgs) {
const stats = await context.agents?.lookup(nodes.map((node) => node.nodeKey));
const populatedNodes = mapNodes(nodes, stats);
const supportsNodeOwnerChange = ! context.hsApi.clientHelpers.isAtleast("0.28.0-beta.1");
return {
populatedNodes,
@@ -63,6 +64,7 @@ export async function loader({ request, context }: Route.LoaderArgs) {
Capabilities.generate_authkeys,
),
subject: user.subject,
supportsNodeOwnerChange: supportsNodeOwnerChange,
};
}
@@ -380,6 +382,7 @@ export default function Page({ loaderData }: Route.ComponentProps) {
magic={loaderData.magic}
node={node}
users={loaderData.users}
supportsNodeOwnerChange={loaderData.supportsNodeOwnerChange}
/>
))
)}