diff --git a/web/NodesView.tsx b/web/NodesView.tsx index 8ccc1c2..fc5df7b 100644 --- a/web/NodesView.tsx +++ b/web/NodesView.tsx @@ -103,6 +103,12 @@ const Ownership = ({ node, busy, onError }: OwnershipProps) => { } }; + const applyTags = (ev?: React.FormEvent) => { + ev?.preventDefault(); + if (!tagsDirty) return; + void run(() => setTags.mutateAsync({ id: node.id, tags: nextTags })); + }; + return (
Owner and tags
@@ -148,13 +154,12 @@ const Ownership = ({ node, busy, onError }: OwnershipProps) => {

)} -
+
setDraftTags(ev.target.value)} onKeyDown={(ev) => { - if (ev.key === 'Enter' && tagsDirty) void run(() => setTags.mutateAsync({ id: node.id, tags: nextTags })); if (ev.key === 'Escape') setDraftTags(node.tags.join(' ')); }} placeholder="tag:server tag:eu — space separated" @@ -164,19 +169,16 @@ const Ownership = ({ node, busy, onError }: OwnershipProps) => { /> {tagsDirty && ( <> - - )} -
+

Tags are what the access policy targets. A tag no rule mentions does nothing; removing one a rule depends on cuts the node off from it. The tag: prefix is added for you. @@ -204,7 +206,8 @@ const NodeCard = ({ node, onError }: NodeCardProps) => { } }; - const submitRename = async () => { + const submitRename = async (ev?: React.FormEvent) => { + ev?.preventDefault(); const name = draftName.trim(); setRenaming(false); if (!name || name === node.name) return; @@ -218,29 +221,24 @@ const NodeCard = ({ node, onError }: NodeCardProps) => {

{renaming ? ( -
+
setDraftName(ev.target.value)} onKeyDown={(ev) => { - if (ev.key === 'Enter') void submitRename(); if (ev.key === 'Escape') setRenaming(false); }} autoFocus spellCheck={false} className="min-w-0 flex-1 rounded-md border border-white/10 bg-black/40 px-2 py-1 text-sm text-zinc-100 outline-none focus:border-primary/50" /> - -
+ ) : (