From 73e066c3307a9d6278f03f2136bc35176f348cb4 Mon Sep 17 00:00:00 2001 From: Chili Palmer Date: Sat, 2 May 2026 11:24:51 +0200 Subject: [PATCH] fix: tag editor hopefully working and fixes to test runner --- lib/bds/desktop/shell_live/tags_editor.ex | 36 ++++++++++-- .../tags_editor_html/tags_editor.html.heex | 38 +++++++++---- priv/ui/live.js | 29 ++++++++++ test/bds/desktop/shell_live_test.exs | 55 +++++++++++++++++++ test/test_helper.exs | 4 ++ 5 files changed, 146 insertions(+), 16 deletions(-) diff --git a/lib/bds/desktop/shell_live/tags_editor.ex b/lib/bds/desktop/shell_live/tags_editor.ex index b18738d..d480c22 100644 --- a/lib/bds/desktop/shell_live/tags_editor.ex +++ b/lib/bds/desktop/shell_live/tags_editor.ex @@ -13,6 +13,8 @@ defmodule BDS.Desktop.ShellLive.TagsEditor do embed_templates("tags_editor_html/*") + @tags_sections ~w(cloud manage merge) + @spec assign_socket(term()) :: term() def assign_socket(socket) do assign(socket, :tags_editor, build(socket.assigns)) @@ -198,9 +200,13 @@ defmodule BDS.Desktop.ShellLive.TagsEditor do @spec sync(term(), term(), term()) :: term() def sync(socket, reload, append_output) do - _ = append_output - :ok = Tags.sync_tags_json(socket.assigns.projects.active_project_id) - reload.(socket, socket.assigns.workbench) + case Tags.sync_tags_from_posts(socket.assigns.projects.active_project_id) do + {:ok, _tags} -> reload.(socket, socket.assigns.workbench) + {:error, reason} -> + socket + |> append_output.(translated("Tags"), inspect(reason), nil, "error") + |> reload.(socket.assigns.workbench) + end end @spec build(term()) :: term() @@ -226,6 +232,8 @@ defmodule BDS.Desktop.ShellLive.TagsEditor do select: %{slug: template.slug, title: template.title} ) + selected_section = current_tags_section(assigns) + %{ tags: Enum.map(tags, fn tag -> @@ -235,7 +243,8 @@ defmodule BDS.Desktop.ShellLive.TagsEditor do new_tag: Map.get(assigns, :tags_editor_new_tag, %{"name" => "", "color" => ""}), edit_draft: edit_draft, templates: templates, - merge_target: Map.get(assigns, :tags_editor_merge_target, List.first(selected) || "") + merge_target: Map.get(assigns, :tags_editor_merge_target, List.first(selected) || ""), + selected_section: selected_section } end @@ -293,6 +302,25 @@ defmodule BDS.Desktop.ShellLive.TagsEditor do end end + defp current_tags_section(assigns) do + assigns + |> current_tab_meta() + |> Map.get(:sidebar_item_id, "tags-cloud") + |> to_string() + |> String.replace_prefix("tags-", "") + |> case do + section when section in @tags_sections -> section + _other -> "cloud" + end + end + + defp current_tab_meta(assigns) do + case Map.get(assigns, :current_tab) do + %{type: type, id: id} -> Map.get(assigns[:tab_meta] || %{}, {type, id}, %{}) + _other -> %{} + end + end + defp tag_counts(project_id) do Repo.all(from post in Post, where: post.project_id == ^project_id, select: post.tags) |> List.flatten() diff --git a/lib/bds/desktop/shell_live/tags_editor_html/tags_editor.html.heex b/lib/bds/desktop/shell_live/tags_editor_html/tags_editor.html.heex index a82fac5..fb285ce 100644 --- a/lib/bds/desktop/shell_live/tags_editor_html/tags_editor.html.heex +++ b/lib/bds/desktop/shell_live/tags_editor_html/tags_editor.html.heex @@ -1,24 +1,38 @@ -
+

<%= translated("Tags") %>

-
+

<%= translated("Tag Cloud") %>

-
- <%= for tag <- @tags_editor.tags do %> - - <% end %> -
+ <%= if Enum.empty?(@tags_editor.tags) do %> +
+

<%= translated("No tags found") %>

+ +
+ <% else %> +
+ <%= for tag <- @tags_editor.tags do %> + + <% end %> +
+ <% end %>
-
+

<%= translated("Create / Edit") %>

@@ -48,7 +62,7 @@
-
+

<%= translated("Merge Tags") %>

@@ -64,7 +78,7 @@
-
+

<%= translated("Sync") %>

diff --git a/priv/ui/live.js b/priv/ui/live.js index 607c176..595b028 100644 --- a/priv/ui/live.js +++ b/priv/ui/live.js @@ -830,6 +830,35 @@ document.addEventListener("DOMContentLoaded", () => { } }, + TagsSectionScroll: { + mounted() { + this.lastTargetId = null; + this.scrollToSelectedSection(); + }, + + updated() { + this.scrollToSelectedSection(); + }, + + scrollToSelectedSection() { + const targetId = this.el.dataset.tagsScrollTarget; + + if (!targetId || targetId === this.lastTargetId) { + return; + } + + this.lastTargetId = targetId; + + window.requestAnimationFrame(() => { + const target = document.getElementById(targetId); + + if (target && this.el.contains(target)) { + target.scrollIntoView({ block: "start", behavior: "smooth" }); + } + }); + } + }, + ChatSurface: { mounted() { this.stickToBottom = true; diff --git a/test/bds/desktop/shell_live_test.exs b/test/bds/desktop/shell_live_test.exs index 2fa83e3..bdfc499 100644 --- a/test/bds/desktop/shell_live_test.exs +++ b/test/bds/desktop/shell_live_test.exs @@ -330,6 +330,61 @@ defmodule BDS.Desktop.ShellLiveTest do assert html =~ ~s(data-settings-scroll-target="settings-section-ai") end + test "tags sidebar selections expose a scroll target for the tags editor" do + {:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive) + + _html = render_click(view, "select_view", %{"view" => "tags"}) + + html = + view + |> element("[data-testid='sidebar-open-item'][data-item-id='tags-merge']") + |> render_click() + + assert html =~ ~s(data-testid="tags-editor") + assert html =~ ~s(phx-hook="TagsSectionScroll") + assert html =~ ~s(data-selected-tags-section="merge") + assert html =~ ~s(data-tags-scroll-target="tags-section-merge") + end + + test "tags discover materializes post tags and enables merge from the tags editor", %{ + project: project + } do + assert {:ok, post} = + Posts.create_post(%{ + project_id: project.id, + title: "Tagged Post", + content: "Body", + tags: ["Alpha", "Beta"] + }) + + assert Tags.list_tags(project.id) == [] + + {:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive) + + _html = render_click(view, "select_view", %{"view" => "tags"}) + + _html = + view + |> element("[data-testid='sidebar-open-item'][data-item-id='tags-cloud']") + |> render_click() + + html = render_click(view, "sync_tags_editor", %{}) + + assert Enum.map(Tags.list_tags(project.id), & &1.name) == ["Alpha", "Beta"] + assert html =~ "Alpha" + assert html =~ "Beta" + + _html = render_click(view, "toggle_tag_selection", %{"name" => "Alpha"}) + _html = render_click(view, "toggle_tag_selection", %{"name" => "Beta"}) + _html = render_change(view, "change_merge_target", %{"target" => "Alpha"}) + + html = render_click(view, "merge_tags_editor", %{}) + + assert Enum.map(Tags.list_tags(project.id), & &1.name) == ["Alpha"] + assert Repo.get!(Post, post.id).tags == ["Alpha"] + assert html =~ "Alpha" + end + test "database-backed sidebar entries require confirmation before deletion", %{ project: project, temp_dir: temp_dir diff --git a/test/test_helper.exs b/test/test_helper.exs index 961956c..30f929c 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -2,6 +2,10 @@ cache_root = Path.join(System.tmp_dir!(), "bds-test-cache-#{System.unique_intege File.mkdir_p!(cache_root) Application.put_env(:bds, :project_cache_root, cache_root) +Enum.each(["LC_ALL", "LC_MESSAGES", "LANG"], fn variable -> + System.put_env(variable, "en_US.UTF-8") +end) + ExUnit.start() ExUnit.after_suite(fn _results ->