diff --git a/README.md b/README.md index 850e2f19..f405e774 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ This is the open-source repository for "participa", based on [Decidim](https://g - `Decidim::Regulations`, adds Regulations, a new type of Participatory process. - `Decidim::Admin::Extended`, customize admin menu adding custom configurations. - `Decidim::Recaptcha`, use recaptcha instead invisible captcha. +- Proposals admin index: adds an "Attachments" column with ascending/descending sort support (see `app/overrides/decidim/admin/proposals_index_attachments_th.rb`, `app/overrides/decidim/admin/proposals_tr_attachments_td.rb`, and `app/decorators/decidim/proposals/proposal_decorator.rb`). ## Deploying the app diff --git a/app/decorators/decidim/proposals/proposal_decorator.rb b/app/decorators/decidim/proposals/proposal_decorator.rb new file mode 100644 index 00000000..9c1ebabf --- /dev/null +++ b/app/decorators/decidim/proposals/proposal_decorator.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Decidim::Proposals::ProposalDecorator + def self.decorate + Decidim::Proposals::Proposal.class_eval do + scope :sort_by_attachments_count_asc, lambda { + order(Arel.sql( + "(SELECT COUNT(*) FROM decidim_attachments " \ + "WHERE decidim_attachments.attached_to_type = 'Decidim::Proposals::Proposal' " \ + "AND decidim_attachments.attached_to_id = decidim_proposals_proposals.id) ASC" + )) + } + + scope :sort_by_attachments_count_desc, lambda { + order(Arel.sql( + "(SELECT COUNT(*) FROM decidim_attachments " \ + "WHERE decidim_attachments.attached_to_type = 'Decidim::Proposals::Proposal' " \ + "AND decidim_attachments.attached_to_id = decidim_proposals_proposals.id) DESC" + )) + } + end + end +end + +Decidim::Proposals::ProposalDecorator.decorate diff --git a/app/overrides/decidim/admin/proposals_index_attachments_th.rb b/app/overrides/decidim/admin/proposals_index_attachments_th.rb new file mode 100644 index 00000000..bc386aab --- /dev/null +++ b/app/overrides/decidim/admin/proposals_index_attachments_th.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +# Inserts the "Attachments" column header after the "Valuators" . +Deface::Override.new( + virtual_path: "decidim/proposals/admin/proposals/index", + name: "proposals_admin_index_attachments_th", + insert_after: "thead th:nth-last-of-type(3)", + text: <<~EOHTML + <% if current_component.settings.attachments_allowed? %> + + <%= sort_link(query, :attachments_count, t("models.proposal.fields.attachments", scope: "decidim.proposals")) %> + + <% end %> + EOHTML +) diff --git a/app/overrides/decidim/admin/proposals_tr_attachments_td.rb b/app/overrides/decidim/admin/proposals_tr_attachments_td.rb new file mode 100644 index 00000000..f37b233f --- /dev/null +++ b/app/overrides/decidim/admin/proposals_tr_attachments_td.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +# Inserts the attachments count after the "Valuators" . +Deface::Override.new( + virtual_path: "decidim/proposals/admin/proposals/_proposal-tr", + name: "proposals_admin_tr_attachments_td", + insert_after: "td.valuators-count", + text: <<~EOHTML + <% if current_component.settings.attachments_allowed? %> + + <%= proposal.attachments.size %> + + <% end %> + EOHTML +) diff --git a/config/locales/ca_proposals.yml b/config/locales/ca_proposals.yml index 224a3a2e..65310948 100644 --- a/config/locales/ca_proposals.yml +++ b/config/locales/ca_proposals.yml @@ -43,6 +43,10 @@ ca:
  • external_author/name: Nom de l'autor de la proposta extern a la plataforma Decidim
  • meeting_url: Url de la trobada a la plataforma Decidim
  • + models: + proposal: + fields: + attachments: Adjunts proposals: filters: amendment_type: Tipus diff --git a/config/locales/en_proposals.yml b/config/locales/en_proposals.yml index 55a07045..4be41cdd 100644 --- a/config/locales/en_proposals.yml +++ b/config/locales/en_proposals.yml @@ -27,6 +27,10 @@ en:
  • external_author/name: Proposal author name outside of the Decidim platform
  • meeting_url: Meeting url on the Decidim platform
  • + models: + proposal: + fields: + attachments: Attachments proposals: show: proposal_in_evaluation_reason: 'This proposal is under evaluation because:' diff --git a/config/locales/es_proposals.yml b/config/locales/es_proposals.yml index ec41477e..f65d7326 100644 --- a/config/locales/es_proposals.yml +++ b/config/locales/es_proposals.yml @@ -27,6 +27,10 @@ es:
  • external_author/name: Nombre del autor de la propuesta externo a la plataforma Decidim
  • meeting_url: Url del encuentro en la plataforma Decidim
  • + models: + proposal: + fields: + attachments: Adjuntos proposals: show: proposal_in_evaluation_reason: 'Esta propuesta está en evaluación porque:' diff --git a/config/locales/oc_proposals.yml b/config/locales/oc_proposals.yml index 508de74e..ae5b4533 100644 --- a/config/locales/oc_proposals.yml +++ b/config/locales/oc_proposals.yml @@ -379,6 +379,8 @@ oc: models: proposal: + fields: + attachments: Adjunts name: Proposta participatory_texts: bulk-actions: diff --git a/docs/HOW_TO_UPGRADE.md b/docs/HOW_TO_UPGRADE.md index e8ec6feb..572c5e07 100644 --- a/docs/HOW_TO_UPGRADE.md +++ b/docs/HOW_TO_UPGRADE.md @@ -190,6 +190,10 @@ These are custom modules and this is what you have to keep in mind when updating * Override to export proposal emails and names from authors * probably removable from Decidim v0.28 (remember remove test too) + * `app/decorators/decidim/proposals/proposal_decorator.rb` + * Adds `sort_by_attachments_count_asc` and `sort_by_attachments_count_desc` scopes to `Decidim::Proposals::Proposal` + * Ransack picks up these scopes automatically via the `sort_by_X_asc/desc` naming convention when the field is not a DB column + * `lib/decidim/has_private_users.rb` * Override to allow private space users to acces public view * Could not use a decorator so the whole class has been copied @@ -203,6 +207,10 @@ These are custom modules and this is what you have to keep in mind when updating * `config/locales/` * Overrides some translations keys * Fixes some Decidim translations in `*_fix.yml` files + * `app/overrides/decidim/admin/proposals_index_attachments_th.rb (decidim-proposals)` + * Inserts an "Attachments" column header in the proposals admin index, with ascending/descending sort support via `sort_link` + * `app/overrides/decidim/admin/proposals_tr_attachments_td.rb (decidim-proposals)` + * Inserts the attachments count cell in each proposal row of the admin index ##### Custom files: diff --git a/spec/decorators/decidim/proposals/proposal_decorator_spec.rb b/spec/decorators/decidim/proposals/proposal_decorator_spec.rb new file mode 100644 index 00000000..d500c2d5 --- /dev/null +++ b/spec/decorators/decidim/proposals/proposal_decorator_spec.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe Decidim::Proposals::Proposal do + let(:organization) { create(:organization) } + let(:participatory_process) { create(:participatory_process, organization:) } + let(:component) { create(:component, manifest_name: :proposals, participatory_space: participatory_process) } + + let!(:proposal_no_attachments) { create(:proposal, component:) } + let!(:proposal_one_attachment) { create(:proposal, component:) } + let!(:proposal_two_attachments) { create(:proposal, component:) } + + before do + create(:attachment, attached_to: proposal_one_attachment) + create(:attachment, attached_to: proposal_two_attachments) + create(:attachment, attached_to: proposal_two_attachments) + end + + let(:scoped_proposals) { described_class.where(component:) } + + describe ".sort_by_attachments_count_asc" do + subject { scoped_proposals.sort_by_attachments_count_asc.to_a } + + it "orders proposals by ascending attachment count" do + expect(subject.index(proposal_no_attachments)).to be < subject.index(proposal_one_attachment) + expect(subject.index(proposal_one_attachment)).to be < subject.index(proposal_two_attachments) + end + end + + describe ".sort_by_attachments_count_desc" do + subject { scoped_proposals.sort_by_attachments_count_desc.to_a } + + it "orders proposals by descending attachment count" do + expect(subject.index(proposal_two_attachments)).to be < subject.index(proposal_one_attachment) + expect(subject.index(proposal_one_attachment)).to be < subject.index(proposal_no_attachments) + end + end +end diff --git a/spec/system/proposals_admin_attachments_column_spec.rb b/spec/system/proposals_admin_attachments_column_spec.rb new file mode 100644 index 00000000..ad72581c --- /dev/null +++ b/spec/system/proposals_admin_attachments_column_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require "rails_helper" + +describe "Admin proposals attachments column", type: :system do + let(:manifest_name) { "proposals" } + + include_context "when managing a component as an admin" + + let!(:proposal) { create(:proposal, component:, users: [user]) } + + context "when the component has attachments allowed" do + let!(:component) do + create(:component, + manifest_name: :proposals, + participatory_space: participatory_process, + settings: { "attachments_allowed" => true }) + end + + before { visit_component_admin } + + it "shows the attachments column header" do + expect(page).to have_css("thead th", text: I18n.t("decidim.proposals.models.proposal.fields.attachments")) + end + + it "shows the attachment count for each proposal row" do + within("tbody tr", match: :first) do + expect(page).to have_content(proposal.attachments.size.to_s) + end + end + end + + context "when the component does not have attachments allowed" do + before { visit_component_admin } + + it "does not show the attachments column header" do + expect(page).not_to have_css("thead th", text: I18n.t("decidim.proposals.models.proposal.fields.attachments")) + end + end +end