From 44be18988ba0ed3c5e486709b8542788e0d40adb Mon Sep 17 00:00:00 2001 From: LukeGus Date: Sat, 3 Oct 2026 16:22:13 -0500 Subject: [PATCH 1/4] fix: restore space to add host tags and redesign predefined tags editor --- src/ui/locales/en.json | 3 +- src/ui/sidebar/AdminHostTags.tsx | 101 ++++++++++++-------- src/ui/sidebar/AdminSettingsSections.tsx | 4 +- src/ui/sidebar/HostEditorGeneralTab.tsx | 10 +- src/ui/tests/sidebar/AdminHostTags.test.tsx | 44 +++++++++ 5 files changed, 113 insertions(+), 49 deletions(-) create mode 100644 src/ui/tests/sidebar/AdminHostTags.test.tsx diff --git a/src/ui/locales/en.json b/src/ui/locales/en.json index 4b987d315..1b67f29e5 100644 --- a/src/ui/locales/en.json +++ b/src/ui/locales/en.json @@ -1516,8 +1516,7 @@ "sslManualTitle": "Upload a certificate", "updateExternalAutoProvisionFailed": "Failed to update the auto-create setting", "hostTags": "Predefined host tags", - "hostTagsDesc": "One tag per line, available to all users. Editing this list changes suggestions only; tags already assigned to hosts are kept.", - "hostTagsSaved": "Host tag suggestions saved.", + "hostTagsDesc": "Tags every user sees as suggestions in the host editor. Press Space or Enter to add one. Removing a tag here does not remove it from hosts.", "hostTagsError": "Could not load or save host tag suggestions." }, "newUi": { diff --git a/src/ui/sidebar/AdminHostTags.tsx b/src/ui/sidebar/AdminHostTags.tsx index 9bb344384..90736c975 100644 --- a/src/ui/sidebar/AdminHostTags.tsx +++ b/src/ui/sidebar/AdminHostTags.tsx @@ -1,80 +1,99 @@ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { Button } from "@/components/button"; +import { toast } from "sonner"; +import { X } from "lucide-react"; import { getHostTags, saveHostTags } from "@/api/host-tags-api"; export function AdminHostTags() { const { t } = useTranslation(); - const [value, setValue] = useState(""); + const [tags, setTags] = useState([]); + const [input, setInput] = useState(""); const [loaded, setLoaded] = useState(false); const [saving, setSaving] = useState(false); - const [message, setMessage] = useState(""); useEffect(() => { let cancelled = false; getHostTags() - .then((tags) => { + .then((list) => { if (cancelled) return; - setValue(tags.join("\n")); + setTags(list); setLoaded(true); }) .catch(() => { - if (!cancelled) setMessage(t("admin.hostTagsError")); + if (!cancelled) toast.error(t("admin.hostTagsError")); }); return () => { cancelled = true; }; }, [t]); - async function save() { + async function save(next: string[]) { + const previous = tags; + setTags(next); setSaving(true); - setMessage(""); try { - const tags = await saveHostTags( - value - .split("\n") - .map((tag) => tag.trim()) - .filter(Boolean), - ); - setValue(tags.join("\n")); - setMessage(t("admin.hostTagsSaved")); + setTags(await saveHostTags(next)); window.dispatchEvent(new Event("termix:host-tags-changed")); } catch { - setMessage(t("admin.hostTagsError")); + setTags(previous); + toast.error(t("admin.hostTagsError")); } finally { setSaving(false); } } + function add(raw: string) { + const tag = raw.trim(); + setInput(""); + if (!tag || tags.includes(tag)) return; + void save([...tags, tag]); + } + return ( -
+
-

+ {t("admin.hostTagsDesc")} -

-