diff --git a/Gemfile.lock b/Gemfile.lock
index f1dbdf773ee78..92f3b806f7c8e 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -509,6 +509,8 @@ GEM
net-smtp (0.3.4)
net-protocol
nio4r (2.7.3)
+ nokogiri (1.16.7-arm64-darwin)
+ racc (~> 1.4)
nokogiri (1.16.7-x86_64-linux)
racc (~> 1.4)
oauth (1.1.0)
@@ -798,6 +800,7 @@ GEM
zeitwerk (2.6.18)
PLATFORMS
+ arm64-darwin-23
x86_64-linux
DEPENDENCIES
diff --git a/decidim-admin/app/packs/stylesheets/decidim/admin/_table-list.scss b/decidim-admin/app/packs/stylesheets/decidim/admin/_table-list.scss
index 9685ca30ebcb9..35a01b1fbeb69 100644
--- a/decidim-admin/app/packs/stylesheets/decidim/admin/_table-list.scss
+++ b/decidim-admin/app/packs/stylesheets/decidim/admin/_table-list.scss
@@ -167,3 +167,7 @@
@apply inline-block;
}
}
+
+.table-list__title-ellipsis {
+ @apply whitespace-nowrap overflow-hidden text-ellipsis max-w-[200px] sm:max-w-sm md:max-w-md lg:max-w-lg xl:max-w-2xl inline-block align-middle;
+}
diff --git a/decidim-assemblies/app/controllers/decidim/assemblies/admin/assemblies_controller.rb b/decidim-assemblies/app/controllers/decidim/assemblies/admin/assemblies_controller.rb
index d142ead2a9ea5..25647dd429829 100644
--- a/decidim-assemblies/app/controllers/decidim/assemblies/admin/assemblies_controller.rb
+++ b/decidim-assemblies/app/controllers/decidim/assemblies/admin/assemblies_controller.rb
@@ -9,7 +9,8 @@ class AssembliesController < Decidim::Assemblies::Admin::ApplicationController
include Decidim::Assemblies::Admin::Filterable
include Decidim::Admin::ParticipatorySpaceAdminContext
include Decidim::Admin::HasTrashableResources
- helper_method :current_assembly, :parent_assembly, :current_participatory_space
+
+ helper_method :current_assembly, :parent_assembly, :parent_assembly_id, :current_participatory_space
layout "decidim/admin/assemblies"
@@ -31,7 +32,7 @@ def create
CreateAssembly.call(@form) do
on(:ok) do |assembly|
flash[:notice] = I18n.t("assemblies.create.success", scope: "decidim.admin")
- redirect_to assemblies_path(q: { parent_id_eq: assembly.parent_id })
+ redirect_to components_path(assembly)
end
on(:invalid) do
diff --git a/decidim-assemblies/app/controllers/decidim/assemblies/admin/assembly_copies_controller.rb b/decidim-assemblies/app/controllers/decidim/assemblies/admin/assembly_copies_controller.rb
index 7e93c4aefb749..943905dd1f3e1 100644
--- a/decidim-assemblies/app/controllers/decidim/assemblies/admin/assembly_copies_controller.rb
+++ b/decidim-assemblies/app/controllers/decidim/assemblies/admin/assembly_copies_controller.rb
@@ -20,7 +20,7 @@ def create
CopyAssembly.call(@form, current_assembly, current_user) do
on(:ok) do
flash[:notice] = I18n.t("assemblies_copies.create.success", scope: "decidim.admin")
- redirect_to assemblies_path(parent_id: current_assembly.parent_id)
+ redirect_to assemblies_path
end
on(:invalid) do
diff --git a/decidim-assemblies/app/helpers/decidim/assemblies/admin/assemblies_helper.rb b/decidim-assemblies/app/helpers/decidim/assemblies/admin/assemblies_helper.rb
index 4879173bfc46b..13f0580aed5a9 100644
--- a/decidim-assemblies/app/helpers/decidim/assemblies/admin/assemblies_helper.rb
+++ b/decidim-assemblies/app/helpers/decidim/assemblies/admin/assemblies_helper.rb
@@ -15,10 +15,32 @@ def processes_selected
end
end
- # Public: A collection of Assemblies that can be selected as parent
- # assemblies for another assembly; to be used in forms.
- def parent_assemblies_for_select
- @parent_assemblies_for_select ||= ParentAssembliesForSelect.for(current_organization, current_assembly)
+ # Public: select options representing a collection of Assemblies that
+ # can be selected as parent assemblies for another assembly; to be used in forms.
+ def parent_assemblies_options
+ options = []
+ root_assemblies = ParentAssembliesForSelect.for(current_organization, current_assembly).where(parent_id: nil).sort_by(&:weight)
+
+ root_assemblies.each do |assembly|
+ build_assembly_options(assembly, options)
+ end
+
+ options
+ end
+
+ private
+
+ # Recursively build the options for the assembly tree
+ def build_assembly_options(assembly, options, level = 0)
+ name = sanitize("#{" " * 4 * level} #{assembly.translated_title}")
+ options << [name, assembly.id]
+
+ # Skip the current assembly to avoid selecting a child as parent
+ return if assembly == current_assembly
+
+ assembly.children.each do |child|
+ build_assembly_options(child, options, level + 1)
+ end
end
end
end
diff --git a/decidim-assemblies/app/packs/entrypoints/decidim_assemblies_admin_list.js b/decidim-assemblies/app/packs/entrypoints/decidim_assemblies_admin_list.js
new file mode 100644
index 0000000000000..f4bf48fe7f8b8
--- /dev/null
+++ b/decidim-assemblies/app/packs/entrypoints/decidim_assemblies_admin_list.js
@@ -0,0 +1 @@
+import "src/decidim/assemblies/admin/assemblies_list"
diff --git a/decidim-assemblies/app/packs/src/decidim/assemblies/admin/assemblies_list.js b/decidim-assemblies/app/packs/src/decidim/assemblies/admin/assemblies_list.js
new file mode 100644
index 0000000000000..321749a34d5f3
--- /dev/null
+++ b/decidim-assemblies/app/packs/src/decidim/assemblies/admin/assemblies_list.js
@@ -0,0 +1,72 @@
+/* eslint-disable require-jsdoc */
+
+class AdminAssembliesListComponent {
+ run() {
+ this.rebindArrows();
+ }
+
+ rebindArrows() {
+ this.unbindArrows();
+ this.bindArrows();
+ }
+
+ bindArrows() {
+ document.querySelectorAll("[data-arrow-up]").forEach((element) => {
+ element.addEventListener("click", this._onClickUpArrow);
+ });
+ document.querySelectorAll("[data-arrow-down]").forEach((element) => {
+ element.addEventListener("click", this._onClickDownArrow);
+ });
+ }
+
+ unbindArrows() {
+ document.querySelectorAll("[data-arrow-up]").forEach((element) => {
+ element.removeEventListener("click", this._onClickUpArrow);
+ });
+ document.querySelectorAll("[data-arrow-down]").forEach((element) => {
+ element.removeEventListener("click", this._onClickDownArrow);
+ });
+ }
+
+ _onClickDownArrow(event) {
+ event.preventDefault();
+
+ const target = event.currentTarget;
+ const assembly = target.closest("[data-assembly-id]");
+ const upArrow = assembly.querySelector("[data-arrow-up]");
+
+ target.classList.toggle("hidden");
+ upArrow.classList.toggle("hidden");
+ }
+
+ _onClickUpArrow(event) {
+ event.preventDefault();
+
+ const target = event.currentTarget;
+ const assembly = target.closest("[data-assembly-id]");
+ const parentLevel = assembly.dataset.level;
+ const downArrow = assembly.querySelector("[data-arrow-down]");
+
+ target.classList.toggle("hidden");
+ downArrow.classList.toggle("hidden");
+
+ // Get all following tr elements
+ let nextElement = assembly.nextElementSibling;
+ while (nextElement) {
+ const currentLevel = nextElement.dataset.level;
+ const nextSibling = nextElement.nextElementSibling;
+
+ if (currentLevel > parentLevel) {
+ nextElement.remove();
+ } else {
+ break;
+ }
+ nextElement = nextSibling;
+ }
+ }
+}
+
+window.Decidim.AdminAssembliesListComponent = AdminAssembliesListComponent;
+const component = new AdminAssembliesListComponent();
+
+component.run();
diff --git a/decidim-assemblies/app/views/decidim/assemblies/admin/assemblies/_assembly_row.html.erb b/decidim-assemblies/app/views/decidim/assemblies/admin/assemblies/_assembly_row.html.erb
index 90cf9f275f13a..70ed82442a5c6 100644
--- a/decidim-assemblies/app/views/decidim/assemblies/admin/assemblies/_assembly_row.html.erb
+++ b/decidim-assemblies/app/views/decidim/assemblies/admin/assemblies/_assembly_row.html.erb
@@ -1,15 +1,33 @@
-
+
+ <% if parent_assembly_id %>
+ <% [assembly.ancestors.count - 1, 0].max.times do |index| %>
+
+ <% end %>
+ |
+ <% end %>
+
<% if assembly.promoted? %>
<%= icon_with_tooltip "star-s-fill", t("models.assembly.fields.promoted", scope: "decidim.admin") %>
<% end %>
- <% if allowed_to? :update, :assembly, assembly: assembly %>
- <%= link_to translated_attribute(assembly.title), edit_assembly_path(assembly) %>
- <% elsif allowed_to? :read, :component, assembly: assembly %>
- <%= link_to translated_attribute(assembly.title), components_path(assembly) %>
- <% else %>
- <%= translated_attribute(assembly.title) %>
+
+ <% if allowed_to? :update, :assembly, assembly: assembly %>
+ <%= link_to translated_attribute(assembly.title), edit_assembly_path(assembly) %>
+ <% elsif allowed_to? :read, :component, assembly: assembly %>
+ <%= link_to translated_attribute(assembly.title), components_path(assembly) %>
+ <% else %>
+ <%= translated_attribute(assembly.title) %>
+ <% end %>
+
+
+ <% if assembly.children.count.positive? %>
+ <%= link_to url_for(query_params_with(parent_id_eq: assembly.id)), remote: true, data: { arrow_down: true } do %>
+ <%= icon "arrow-down-s-line", class: "w-4 h-4 ml-2" %>
+ <% end %>
+ <%= link_to "#", class: "hidden", data: { arrow_up: true } do %>
+ <%= icon "arrow-up-s-line", class: "w-4 h-4 ml-2" %>
+ <% end %>
<% end %>
|
@@ -47,14 +65,6 @@
<% else %>
<% end %>
- <% if assembly.children.count.positive? || allowed_to?(:read, :assembly, assembly:) %>
- <%= icon_link_to "government-line",
- url_for(query_params_with(parent_id_eq: assembly.id)),
- t("decidim.admin.titles.assemblies"),
- class: "action-icon--dial #{"highlighted" if assembly.children.count.positive?}" %>
- <% else %>
-
- <% end %>
<% if allowed_to? :copy, :assembly, assembly: assembly, assembly: parent_assembly %>
<%= icon_link_to "file-copy-line", new_assembly_copy_path(assembly), t("actions.duplicate", scope: "decidim.admin"), class: "action-icon--copy" %>
<% else %>
diff --git a/decidim-assemblies/app/views/decidim/assemblies/admin/assemblies/_form.html.erb b/decidim-assemblies/app/views/decidim/assemblies/admin/assemblies/_form.html.erb
index 30650f3b0e6c0..23af062e0e2b7 100644
--- a/decidim-assemblies/app/views/decidim/assemblies/admin/assemblies/_form.html.erb
+++ b/decidim-assemblies/app/views/decidim/assemblies/admin/assemblies/_form.html.erb
@@ -224,12 +224,7 @@
<% else %>
<%= form.select :parent_id,
- options_from_collection_for_select(
- parent_assemblies_for_select,
- :id,
- :translated_title,
- selected: current_assembly.try(:parent_id)
- ),
+ parent_assemblies_options,
include_blank: t(".select_parent_assembly") %>
<% end %>
diff --git a/decidim-assemblies/app/views/decidim/assemblies/admin/assemblies/index.html.erb b/decidim-assemblies/app/views/decidim/assemblies/admin/assemblies/index.html.erb
index 3717ad8515e62..de5edb0f3f9a0 100644
--- a/decidim-assemblies/app/views/decidim/assemblies/admin/assemblies/index.html.erb
+++ b/decidim-assemblies/app/views/decidim/assemblies/admin/assemblies/index.html.erb
@@ -40,3 +40,5 @@
<%= decidim_paginate @assemblies %>
+
+<%= append_javascript_pack_tag "decidim_assemblies_admin_list" %>
diff --git a/decidim-assemblies/app/views/decidim/assemblies/admin/assemblies/index.js.erb b/decidim-assemblies/app/views/decidim/assemblies/admin/assemblies/index.js.erb
new file mode 100644
index 0000000000000..fce1b92c9be03
--- /dev/null
+++ b/decidim-assemblies/app/views/decidim/assemblies/admin/assemblies/index.js.erb
@@ -0,0 +1,10 @@
+$('[data-assembly-id="<%= parent_assembly_id %>"]').after(
+ '<%= j(render partial: "decidim/assemblies/admin/assemblies/assembly_row",
+ collection: @assemblies,
+ as: :assembly,
+ locals: { view: :index }).strip.html_safe %>'
+);
+
+var component = new window.Decidim.AdminAssembliesListComponent();
+
+component.run();
diff --git a/decidim-assemblies/config/assets.rb b/decidim-assemblies/config/assets.rb
index 2a4c08f703996..3f0d945b05c8c 100644
--- a/decidim-assemblies/config/assets.rb
+++ b/decidim-assemblies/config/assets.rb
@@ -5,5 +5,6 @@
Decidim::Webpacker.register_path("#{base_path}/app/packs")
Decidim::Webpacker.register_entrypoints(
decidim_assemblies: "#{base_path}/app/packs/entrypoints/decidim_assemblies.js",
- decidim_assemblies_admin: "#{base_path}/app/packs/entrypoints/decidim_assemblies_admin.js"
+ decidim_assemblies_admin: "#{base_path}/app/packs/entrypoints/decidim_assemblies_admin.js",
+ decidim_assemblies_admin_list: "#{base_path}/app/packs/entrypoints/decidim_assemblies_admin_list.js"
)
diff --git a/decidim-assemblies/config/locales/en.yml b/decidim-assemblies/config/locales/en.yml
index 4fea63424d5d8..8fc58d0964bc0 100644
--- a/decidim-assemblies/config/locales/en.yml
+++ b/decidim-assemblies/config/locales/en.yml
@@ -104,7 +104,7 @@ en:
assemblies:
create:
error: There was a problem creating a new assembly.
- success: Assembly created successfully.
+ success: Assembly created successfully. You can now add components and configure it.
edit:
update: Update
index:
diff --git a/decidim-assemblies/spec/shared/manage_assemblies_examples.rb b/decidim-assemblies/spec/shared/manage_assemblies_examples.rb
index c68893ab06886..16cf616c161e8 100644
--- a/decidim-assemblies/spec/shared/manage_assemblies_examples.rb
+++ b/decidim-assemblies/spec/shared/manage_assemblies_examples.rb
@@ -204,8 +204,4 @@
expect(page).to have_admin_callout("successfully")
end
end
-
- it "shows the Assemblies link to manage nested assemblies" do
- expect(page).to have_link("Assemblies", href: decidim_admin_assemblies.assemblies_path(q: { parent_id_eq: assembly.id }))
- end
end
diff --git a/decidim-assemblies/spec/system/admin/admin_manages_assemblies_spec.rb b/decidim-assemblies/spec/system/admin/admin_manages_assemblies_spec.rb
index 2ce717000a6be..2949307828454 100644
--- a/decidim-assemblies/spec/system/admin/admin_manages_assemblies_spec.rb
+++ b/decidim-assemblies/spec/system/admin/admin_manages_assemblies_spec.rb
@@ -98,8 +98,7 @@
expect(last_assembly.taxonomies).to contain_exactly(taxonomy)
within "[data-content]" do
- expect(page).to have_current_path decidim_admin_assemblies.assemblies_path(q: { parent_id_eq: parent_assembly&.id })
- expect(page).to have_content(translated(attributes[:title]))
+ expect(page).to have_current_path decidim_admin_assemblies.components_path(last_assembly)
end
visit decidim_admin.root_path
@@ -192,7 +191,7 @@ def assembly_without_type(type)
end
end
- context "when managing child assemblies" do
+ context "when navigating child assemblies" do
let!(:parent_assembly) { create(:assembly, organization:) }
let!(:child_assembly) { create(:assembly, :with_content_blocks, organization:, parent: parent_assembly, blocks_manifests: [:announcement]) }
let(:assembly) { child_assembly }
@@ -201,24 +200,23 @@ def assembly_without_type(type)
switch_to_host(organization.host)
login_as user, scope: :user
visit decidim_admin_assemblies.assemblies_path
- within "tr", text: translated(parent_assembly.title) do
- click_on "Assemblies"
- end
end
- it_behaves_like "manage assemblies"
- it_behaves_like "creating an assembly"
- it_behaves_like "manage assemblies announcements"
-
describe "listing child assemblies" do
- it_behaves_like "filtering collection by published/unpublished" do
- let!(:published_space) { child_assembly }
- let!(:unpublished_space) { create(:assembly, :unpublished, parent: parent_assembly, organization:) }
- end
+ it "expands the parent assembly" do
+ expect(page).to have_no_content(translated(child_assembly.title))
+
+ within "tr", text: translated(parent_assembly.title) do
+ find("a[data-arrow-down]").click
+ end
+
+ expect(page).to have_content(translated(child_assembly.title))
+
+ within "tr", text: translated(parent_assembly.title) do
+ find("a[data-arrow-up]").click
+ end
- it_behaves_like "filtering collection by private/public" do
- let!(:public_space) { child_assembly }
- let!(:private_space) { create(:assembly, :private, parent: parent_assembly, organization:) }
+ expect(page).to have_no_content(translated(child_assembly.title))
end
end
end
diff --git a/decidim-assemblies/spec/system/admin/admin_manages_assemblies_with_parent_selector_spec.rb b/decidim-assemblies/spec/system/admin/admin_manages_assemblies_with_parent_selector_spec.rb
deleted file mode 100644
index a4b3991bd8223..0000000000000
--- a/decidim-assemblies/spec/system/admin/admin_manages_assemblies_with_parent_selector_spec.rb
+++ /dev/null
@@ -1,139 +0,0 @@
-# frozen_string_literal: true
-
-require "spec_helper"
-
-describe "Admin manages assemblies with parent selector" do
- include_context "when admin administrating an assembly"
-
- let(:image1_filename) { "city.jpeg" }
- let(:image1_path) { Decidim::Dev.asset(image1_filename) }
- let(:image2_filename) { "city2.jpeg" }
- let(:image2_path) { Decidim::Dev.asset(image2_filename) }
-
- before do
- switch_to_host(organization.host)
- login_as user, scope: :user
- visit decidim_admin_assemblies.assemblies_path
- end
-
- context "when selecting a parent_assembly in the form" do
- before do
- click_on "New assembly"
- end
-
- it "creates a new assembly" do
- within ".new_assembly" do
- fill_in_i18n(
- :assembly_title,
- "#assembly-title-tabs",
- en: "My assembly",
- es: "Mi proceso participativo",
- ca: "El meu procés participatiu"
- )
- fill_in_i18n(
- :assembly_subtitle,
- "#assembly-subtitle-tabs",
- en: "Subtitle",
- es: "Subtítulo",
- ca: "Subtítol"
- )
- fill_in_i18n_editor(
- :assembly_short_description,
- "#assembly-short_description-tabs",
- en: "Short description",
- es: "Descripción corta",
- ca: "Descripció curta"
- )
- fill_in_i18n_editor(
- :assembly_description,
- "#assembly-description-tabs",
- en: "A longer description",
- es: "Descripción más larga",
- ca: "Descripció més llarga"
- )
-
- select assembly.title["en"], from: :assembly_parent_id
-
- fill_in :assembly_slug, with: "slug"
- fill_in :assembly_hashtag, with: "#hashtag"
- fill_in :assembly_weight, with: 1
- end
-
- dynamically_attach_file(:assembly_hero_image, image1_path)
- dynamically_attach_file(:assembly_banner_image, image2_path)
-
- within ".new_assembly" do
- find("*[type=submit]").click
- end
-
- expect(page).to have_admin_callout("successfully")
-
- within "[data-content]" do
- expect(page).to have_current_path decidim_admin_assemblies.assemblies_path(q: { parent_id_eq: assembly.id })
- expect(page).to have_content("My assembly")
- end
- end
- end
-
- context "when managing child assemblies" do
- let!(:child_assembly) { create(:assembly, organization:, parent: assembly) }
-
- before do
- within "tr", text: translated(assembly.title) do
- click_on "Assemblies"
- end
- click_on "New assembly"
- end
-
- it "creates a new assembly" do
- within ".new_assembly" do
- fill_in_i18n(
- :assembly_title,
- "#assembly-title-tabs",
- en: "My assembly",
- es: "Mi proceso participativo",
- ca: "El meu procés participatiu"
- )
- fill_in_i18n(
- :assembly_subtitle,
- "#assembly-subtitle-tabs",
- en: "Subtitle",
- es: "Subtítulo",
- ca: "Subtítol"
- )
- fill_in_i18n_editor(
- :assembly_short_description,
- "#assembly-short_description-tabs",
- en: "Short description",
- es: "Descripción corta",
- ca: "Descripció curta"
- )
- fill_in_i18n_editor(
- :assembly_description,
- "#assembly-description-tabs",
- en: "A longer description",
- es: "Descripción más larga",
- ca: "Descripció més llarga"
- )
-
- fill_in :assembly_slug, with: "slug"
- fill_in :assembly_hashtag, with: "#hashtag"
- fill_in :assembly_weight, with: 1
- end
-
- dynamically_attach_file(:assembly_hero_image, image1_path)
- dynamically_attach_file(:assembly_banner_image, image2_path)
-
- within ".new_assembly" do
- find("*[type=submit]").click
- end
-
- expect(page).to have_admin_callout("successfully")
-
- within "[data-content]" do
- expect(page).to have_current_path decidim_admin_assemblies.assemblies_path(q: { parent_id_eq: assembly.id })
- expect(page).to have_content("My assembly")
- end
- end
- end
-end
diff --git a/decidim-assemblies/spec/system/admin/assembly_admin_accesses_admin_sections_spec.rb b/decidim-assemblies/spec/system/admin/assembly_admin_accesses_admin_sections_spec.rb
index 6bf30b336c8ff..8e3bae38024cd 100644
--- a/decidim-assemblies/spec/system/admin/assembly_admin_accesses_admin_sections_spec.rb
+++ b/decidim-assemblies/spec/system/admin/assembly_admin_accesses_admin_sections_spec.rb
@@ -57,12 +57,7 @@
let!(:child_assembly) { create(:assembly, parent: assembly, organization:, hashtag: "child") }
before do
- visit decidim_admin_assemblies.assemblies_path
- within "tr", text: translated(assembly.title) do
- click_on "Assemblies"
- end
-
- click_on "Configure"
+ visit decidim_admin_assemblies.edit_assembly_path(child_assembly)
end
context "when is a public assembly" do
|