diff --git a/app/assets/javascripts/columns_selector.js b/app/assets/javascripts/columns_selector.js
index 888dd2c76..40a562510 100644
--- a/app/assets/javascripts/columns_selector.js
+++ b/app/assets/javascripts/columns_selector.js
@@ -80,9 +80,6 @@
App.ColumnsSelector.toggleColumn(event);
}
});
- $(".column-selectable").on("inserted", function() {
- App.ColumnsSelector.initColumns();
- });
},
destroy: function() {
$("#js-columns-selector-wrapper").children(":not(#column_selector_item_template)").remove();
diff --git a/app/assets/stylesheets/admin/budget_investments/investments.scss b/app/assets/stylesheets/admin/budget_investments/investments.scss
new file mode 100644
index 000000000..5d931e6ff
--- /dev/null
+++ b/app/assets/stylesheets/admin/budget_investments/investments.scss
@@ -0,0 +1,19 @@
+.admin .admin-budget-investments {
+ td {
+ &[data-field=supports],
+ &[data-field=valuation_finished],
+ &[data-field=visible_to_valuators],
+ &[data-field=selected],
+ &[data-field=incompatible] {
+ text-align: center;
+ }
+
+ &:not([data-field=id], [data-field=title], [data-field=supports]) {
+ font-size: $small-font-size;
+ }
+ }
+
+ .toggle-switch [aria-pressed] {
+ font-size: inherit;
+ }
+}
diff --git a/app/assets/stylesheets/mixins/buttons.scss b/app/assets/stylesheets/mixins/buttons.scss
index ee09362ac..72691b9f1 100644
--- a/app/assets/stylesheets/mixins/buttons.scss
+++ b/app/assets/stylesheets/mixins/buttons.scss
@@ -85,7 +85,7 @@
@include regular-button;
border-radius: $line-height;
font-weight: bold;
- min-width: rem-calc(100);
+ min-width: 6em;
position: relative;
&::after {
diff --git a/app/components/admin/budget_investments/investments_component.html.erb b/app/components/admin/budget_investments/investments_component.html.erb
new file mode 100644
index 000000000..f5e0f4a59
--- /dev/null
+++ b/app/components/admin/budget_investments/investments_component.html.erb
@@ -0,0 +1,64 @@
+
+ <%= link_to t("admin.budget_investments.index.download_current_selection"),
+ admin_budget_budget_investments_path(csv_params),
+ class: "float-right small clear" %>
+
+ <% if params[:advanced_filters].include?("winners") %>
+ <%= render Admin::Budgets::CalculateWinnersButtonComponent.new(budget, from_investments: true) %>
+ <% end %>
+
+ <% if investments.any? %>
+
<%= page_entries_info investments %>
+ <%= render "admin/shared/columns_selector",
+ cookie: "investments-columns",
+ default: %w[id title supports admin valuator geozone feasibility price valuation_finished visible_to_valuators selected incompatible] %>
+
+
+ <%= render "filters_description", i18n_namespace: "admin.budget_investments.index" %>
+
+
+
+
+ | <%= link_to_investments_sorted_by :id %> |
+ <%= link_to_investments_sorted_by :title %> |
+ <%= link_to_investments_sorted_by :supports %> |
+ <%= t("admin.budget_investments.index.list.admin") %> |
+
+ <%= t("admin.budget_investments.index.list.author") %>
+ |
+
+ <%= t("admin.budget_investments.index.list.valuation_group") %> /
+ <%= t("admin.budget_investments.index.list.valuator") %>
+ |
+ <%= t("admin.budget_investments.index.list.geozone") %> |
+ <%= t("admin.budget_investments.index.list.feasibility") %> |
+ <% if budget.show_money? %>
+ <%= t("admin.budget_investments.index.list.price") %> |
+ <% end %>
+
+ <%= t("admin.budget_investments.index.list.valuation_finished") %>
+ |
+
+ <%= t("admin.budget_investments.index.list.visible_to_valuators") %>
+ |
+ <%= t("admin.budget_investments.index.list.selected") %> |
+ <% if params[:advanced_filters]&.include?("selected") %>
+ <%= t("admin.budget_investments.index.list.incompatible") %> |
+ <% end %>
+
+
+
+
+ <% investments.each do |investment| %>
+ <%= render Admin::BudgetInvestments::RowComponent.new(investment) %>
+ <% end %>
+
+
+
+ <%= paginate investments %>
+ <% else %>
+
+ <%= t("admin.budget_investments.index.no_budget_investments") %>
+
+ <% end %>
+
diff --git a/app/components/admin/budget_investments/investments_component.rb b/app/components/admin/budget_investments/investments_component.rb
new file mode 100644
index 000000000..74f7f439b
--- /dev/null
+++ b/app/components/admin/budget_investments/investments_component.rb
@@ -0,0 +1,30 @@
+class Admin::BudgetInvestments::InvestmentsComponent < ApplicationComponent
+ attr_reader :budget, :investments
+ use_helpers :set_direction, :set_sorting_icon
+
+ def initialize(budget, investments)
+ @budget = budget
+ @investments = investments
+ end
+
+ private
+
+ def csv_params
+ csv_params = params.clone.merge(format: :csv)
+ csv_params = csv_params.to_unsafe_h.transform_keys(&:to_sym)
+ csv_params.delete(:page)
+ csv_params
+ end
+
+ def link_to_investments_sorted_by(column)
+ direction = set_direction(params[:direction])
+ icon = set_sorting_icon(direction, column)
+
+ translation = t("admin.budget_investments.index.list.#{column}")
+
+ link_to(
+ safe_join([translation, tag.span(class: "icon-sortable #{icon}")]),
+ admin_budget_budget_investments_path(sort_by: column, direction: direction)
+ )
+ end
+end
diff --git a/app/components/admin/budget_investments/row_component.html.erb b/app/components/admin/budget_investments/row_component.html.erb
new file mode 100644
index 000000000..3c191a726
--- /dev/null
+++ b/app/components/admin/budget_investments/row_component.html.erb
@@ -0,0 +1,57 @@
+
+ |
+ <%= investment.id %>
+ |
+
+
+ <%= link_to investment.title, investment_path, target: "_blank" %>
+ |
+
+
+ <%= investment.total_votes %>
+ |
+
+
+ <%= administrator_info %>
+ |
+
+
+ <%= investment.author.name %>
+ |
+
+
+ <%= valuators_info %>
+ |
+
+
+ <%= investment.heading.name %>
+ |
+
+
+ <%= t("admin.budget_investments.index.feasibility.#{investment.feasibility}") %>
+ |
+
+ <% if budget.show_money? %>
+
+ <%= investment.formatted_price %>
+ |
+ <% end %>
+
+
+ <%= investment.valuation_finished? ? t("shared.yes") : t("shared.no") %>
+ |
+
+
+ <%= render Admin::BudgetInvestments::ToggleVisibleToValuatorsComponent.new(investment) %>
+ |
+
+
+ <%= render Admin::BudgetInvestments::ToggleSelectionComponent.new(investment) %>
+ |
+
+ <% if params[:advanced_filters]&.include?("selected") %>
+
+ <%= investment.incompatible? ? t("shared.yes") : t("shared.no") %>
+ |
+ <% end %>
+
diff --git a/app/components/admin/budget_investments/row_component.rb b/app/components/admin/budget_investments/row_component.rb
new file mode 100644
index 000000000..c48156735
--- /dev/null
+++ b/app/components/admin/budget_investments/row_component.rb
@@ -0,0 +1,38 @@
+class Admin::BudgetInvestments::RowComponent < ApplicationComponent
+ attr_reader :investment
+
+ def initialize(investment)
+ @investment = investment
+ end
+
+ private
+
+ def budget
+ investment.budget
+ end
+
+ def investment_path
+ admin_budget_budget_investment_path(budget_id: budget.id,
+ id: investment.id,
+ params: Budget::Investment.filter_params(params).to_h)
+ end
+
+ def administrator_info
+ if investment.administrator.present?
+ tag.span(investment.administrator.description_or_name,
+ title: t("admin.budget_investments.index.assigned_admin"))
+ else
+ t("admin.budget_investments.index.no_admin_assigned")
+ end
+ end
+
+ def valuators_info
+ valuators = [investment.assigned_valuation_groups, investment.assigned_valuators].compact
+
+ if valuators.present?
+ valuators.join(", ")
+ else
+ t("admin.budget_investments.index.no_valuators_assigned")
+ end
+ end
+end
diff --git a/app/components/admin/budget_investments/toggle_selection_component.html.erb b/app/components/admin/budget_investments/toggle_selection_component.html.erb
new file mode 100644
index 000000000..a417c6043
--- /dev/null
+++ b/app/components/admin/budget_investments/toggle_selection_component.html.erb
@@ -0,0 +1,5 @@
+<% if can?(action, investment) %>
+ <%= render Admin::ToggleSwitchComponent.new(action, investment, pressed: selected?, **options) %>
+<% elsif selected? %>
+ <%= selected_text %>
+<% end %>
diff --git a/app/components/admin/budget_investments/toggle_selection_component.rb b/app/components/admin/budget_investments/toggle_selection_component.rb
new file mode 100644
index 000000000..4e35f425d
--- /dev/null
+++ b/app/components/admin/budget_investments/toggle_selection_component.rb
@@ -0,0 +1,50 @@
+class Admin::BudgetInvestments::ToggleSelectionComponent < ApplicationComponent
+ attr_reader :investment
+ use_helpers :can?
+ delegate :selected?, to: :investment
+
+ def initialize(investment)
+ @investment = investment
+ end
+
+ private
+
+ def selected_text
+ t("admin.budget_investments.index.selected")
+ end
+
+ def action
+ if selected?
+ :deselect
+ else
+ :select
+ end
+ end
+
+ def path
+ url_for({
+ controller: "admin/budget_investments",
+ action: action,
+ budget_id: investment.budget,
+ id: investment,
+ filter: params[:filter],
+ sort_by: params[:sort_by],
+ min_total_supports: params[:min_total_supports],
+ max_total_supports: params[:max_total_supports],
+ advanced_filters: params[:advanced_filters],
+ page: params[:page]
+ })
+ end
+
+ def options
+ {
+ "aria-label": label,
+ form_class: "toggle-selection",
+ path: path
+ }
+ end
+
+ def label
+ t("admin.actions.label", action: t("admin.actions.select"), name: investment.title)
+ end
+end
diff --git a/app/components/admin/budget_investments/toggle_visible_to_valuators_component.html.erb b/app/components/admin/budget_investments/toggle_visible_to_valuators_component.html.erb
new file mode 100644
index 000000000..6233b0355
--- /dev/null
+++ b/app/components/admin/budget_investments/toggle_visible_to_valuators_component.html.erb
@@ -0,0 +1,5 @@
+<% if can?(:admin_update, investment) %>
+ <%= render Admin::ToggleSwitchComponent.new(action, investment, pressed: visible_to_valuators?, **options) %>
+<% else %>
+ <%= text %>
+<% end %>
diff --git a/app/components/admin/budget_investments/toggle_visible_to_valuators_component.rb b/app/components/admin/budget_investments/toggle_visible_to_valuators_component.rb
new file mode 100644
index 000000000..2ecdfd8ef
--- /dev/null
+++ b/app/components/admin/budget_investments/toggle_visible_to_valuators_component.rb
@@ -0,0 +1,38 @@
+class Admin::BudgetInvestments::ToggleVisibleToValuatorsComponent < ApplicationComponent
+ attr_reader :investment
+ use_helpers :can?
+ delegate :visible_to_valuators?, to: :investment
+
+ def initialize(investment)
+ @investment = investment
+ end
+
+ private
+
+ def action
+ if visible_to_valuators?
+ :hide_from_valuators
+ else
+ :show_to_valuators
+ end
+ end
+
+ def text
+ if visible_to_valuators?
+ t("shared.yes")
+ else
+ t("shared.no")
+ end
+ end
+
+ def options
+ {
+ "aria-label": label,
+ form_class: "visible-to-valuators"
+ }
+ end
+
+ def label
+ t("admin.actions.show_to_valuators", name: investment.title)
+ end
+end
diff --git a/app/components/admin/proposals/toggle_selection_component.html.erb b/app/components/admin/proposals/toggle_selection_component.html.erb
new file mode 100644
index 000000000..bdb2aaa99
--- /dev/null
+++ b/app/components/admin/proposals/toggle_selection_component.html.erb
@@ -0,0 +1 @@
+<%= render Admin::ToggleSwitchComponent.new(action, proposal, pressed: selected?, **options) %>
diff --git a/app/components/admin/proposals/toggle_selection_component.rb b/app/components/admin/proposals/toggle_selection_component.rb
new file mode 100644
index 000000000..dfcb9ea8e
--- /dev/null
+++ b/app/components/admin/proposals/toggle_selection_component.rb
@@ -0,0 +1,31 @@
+class Admin::Proposals::ToggleSelectionComponent < ApplicationComponent
+ attr_reader :proposal
+
+ def initialize(proposal)
+ @proposal = proposal
+ end
+
+ private
+
+ def action
+ if selected?
+ :deselect
+ else
+ :select
+ end
+ end
+
+ def selected?
+ proposal.selected?
+ end
+
+ def options
+ {
+ "aria-label": label
+ }
+ end
+
+ def label
+ t("admin.actions.label", action: t("admin.actions.select"), name: proposal.title)
+ end
+end
diff --git a/app/components/admin/toggle_switch_component.html.erb b/app/components/admin/toggle_switch_component.html.erb
index 35b566c48..20639357f 100644
--- a/app/components/admin/toggle_switch_component.html.erb
+++ b/app/components/admin/toggle_switch_component.html.erb
@@ -1 +1 @@
-<%= render Admin::ActionComponent.new(action, record, **default_options.merge(options)) %>
+<%= render Admin::ActionComponent.new(action, record, **html_options) %>
diff --git a/app/components/admin/toggle_switch_component.rb b/app/components/admin/toggle_switch_component.rb
index ceee74d5b..c61c2dfe0 100644
--- a/app/components/admin/toggle_switch_component.rb
+++ b/app/components/admin/toggle_switch_component.rb
@@ -25,7 +25,11 @@ class Admin::ToggleSwitchComponent < ApplicationComponent
method: :patch,
remote: true,
"aria-pressed": pressed?,
- form_class: "toggle-switch"
+ form_class: "toggle-switch #{options[:form_class]}".strip
}
end
+
+ def html_options
+ default_options.merge(options.except(:form_class))
+ end
end
diff --git a/app/controllers/admin/budget_investments_controller.rb b/app/controllers/admin/budget_investments_controller.rb
index c4d68ae43..6b031dd3b 100644
--- a/app/controllers/admin/budget_investments_controller.rb
+++ b/app/controllers/admin/budget_investments_controller.rb
@@ -6,19 +6,18 @@ class Admin::BudgetInvestmentsController < Admin::BaseController
feature_flag :budgets
has_orders %w[oldest], only: [:show, :edit]
- has_filters %w[all], only: [:index, :toggle_selection]
+ has_filters %w[all], only: :index
before_action :load_budget
- before_action :load_investment, only: [:show, :edit, :update, :toggle_selection]
+ before_action :load_investment, except: [:index]
before_action :load_ballot, only: [:show, :index]
before_action :parse_valuation_filters
- before_action :load_investments, only: [:index, :toggle_selection]
+ before_action :load_investments, only: :index
def index
load_tags
respond_to do |format|
format.html
- format.js
format.csv do
send_data Budget::Investment::Exporter.new(@investments).to_csv,
filename: "budget_investments.csv"
@@ -40,32 +39,57 @@ class Admin::BudgetInvestmentsController < Admin::BaseController
def update
authorize! :admin_update, @investment
- respond_to do |format|
- format.html do
- if @investment.update(budget_investment_params)
- redirect_to admin_budget_budget_investment_path(@budget,
- @investment,
- Budget::Investment.filter_params(params).to_h),
- notice: t("flash.actions.update.budget_investment")
- else
- load_staff
- load_valuator_groups
- load_tags
- render :edit
- end
- end
-
- format.json do
- @investment.update!(budget_investment_params)
- end
+ if @investment.update(budget_investment_params)
+ redirect_to admin_budget_budget_investment_path(@budget,
+ @investment,
+ Budget::Investment.filter_params(params).to_h),
+ notice: t("flash.actions.update.budget_investment")
+ else
+ load_staff
+ load_valuator_groups
+ load_tags
+ render :edit
end
end
- def toggle_selection
- authorize! :toggle_selection, @investment
- @investment.toggle :selected
- @investment.save!
- load_investments
+ def show_to_valuators
+ authorize! :admin_update, @investment
+ @investment.update!(visible_to_valuators: true)
+
+ respond_to do |format|
+ format.html { redirect_to request.referer, notice: t("flash.actions.update.budget_investment") }
+ format.js { render :toggle_visible_to_valuators }
+ end
+ end
+
+ def hide_from_valuators
+ authorize! :admin_update, @investment
+ @investment.update!(visible_to_valuators: false)
+
+ respond_to do |format|
+ format.html { redirect_to request.referer, notice: t("flash.actions.update.budget_investment") }
+ format.js { render :toggle_visible_to_valuators }
+ end
+ end
+
+ def select
+ authorize! :select, @investment
+ @investment.update!(selected: true)
+
+ respond_to do |format|
+ format.html { redirect_to request.referer, notice: t("flash.actions.update.budget_investment") }
+ format.js { render :toggle_selection }
+ end
+ end
+
+ def deselect
+ authorize! :deselect, @investment
+ @investment.update!(selected: false)
+
+ respond_to do |format|
+ format.html { redirect_to request.referer, notice: t("flash.actions.update.budget_investment") }
+ format.js { render :toggle_selection }
+ end
end
private
@@ -96,7 +120,7 @@ class Admin::BudgetInvestmentsController < Admin::BaseController
def allowed_params
attributes = [:external_url, :heading_id, :administrator_id, :tag_list,
- :valuation_tag_list, :incompatible, :visible_to_valuators, :selected,
+ :valuation_tag_list, :incompatible, :selected,
:milestone_tag_list, valuator_ids: [], valuator_group_ids: []]
[*attributes, translation_params(Budget::Investment)]
end
diff --git a/app/controllers/admin/legislation/proposals_controller.rb b/app/controllers/admin/legislation/proposals_controller.rb
index c2711aa99..8022e8628 100644
--- a/app/controllers/admin/legislation/proposals_controller.rb
+++ b/app/controllers/admin/legislation/proposals_controller.rb
@@ -8,8 +8,21 @@ class Admin::Legislation::ProposalsController < Admin::Legislation::BaseControll
@proposals = @proposals.send("sort_by_#{@current_order}").page(params[:page])
end
- def toggle_selection
- @proposal.toggle :selected
- @proposal.save!
+ def select
+ @proposal.update!(selected: true)
+
+ respond_to do |format|
+ format.html { redirect_to request.referer, notice: t("flash.actions.update.proposal") }
+ format.js { render :toggle_selection }
+ end
+ end
+
+ def deselect
+ @proposal.update!(selected: false)
+
+ respond_to do |format|
+ format.html { redirect_to request.referer, notice: t("flash.actions.update.proposal") }
+ format.js { render :toggle_selection }
+ end
end
end
diff --git a/app/controllers/admin/proposals_controller.rb b/app/controllers/admin/proposals_controller.rb
index 18b2866ec..5e9e52b49 100644
--- a/app/controllers/admin/proposals_controller.rb
+++ b/app/controllers/admin/proposals_controller.rb
@@ -19,9 +19,22 @@ class Admin::ProposalsController < Admin::BaseController
end
end
- def toggle_selection
- @proposal.toggle :selected
- @proposal.save!
+ def select
+ @proposal.update!(selected: true)
+
+ respond_to do |format|
+ format.html { redirect_to request.referer, notice: t("flash.actions.update.proposal") }
+ format.js { render :toggle_selection }
+ end
+ end
+
+ def deselect
+ @proposal.update!(selected: false)
+
+ respond_to do |format|
+ format.html { redirect_to request.referer, notice: t("flash.actions.update.proposal") }
+ format.js { render :toggle_selection }
+ end
end
private
diff --git a/app/helpers/budget_investments_helper.rb b/app/helpers/budget_investments_helper.rb
index dc0920630..f3a49cde6 100644
--- a/app/helpers/budget_investments_helper.rb
+++ b/app/helpers/budget_investments_helper.rb
@@ -3,18 +3,6 @@ module BudgetInvestmentsHelper
params.map { |af| t("admin.budget_investments.index.filters.#{af}") }.join(", ")
end
- def link_to_investments_sorted_by(column)
- direction = set_direction(params[:direction])
- icon = set_sorting_icon(direction, column)
-
- translation = t("admin.budget_investments.index.list.#{column}")
-
- link_to(
- safe_join([translation, tag.span(class: "icon-sortable #{icon}")]),
- admin_budget_budget_investments_path(sort_by: column, direction: direction)
- )
- end
-
def set_sorting_icon(direction, sort_by)
if sort_by.to_s == params[:sort_by]
if direction == "desc"
diff --git a/app/helpers/budgets_helper.rb b/app/helpers/budgets_helper.rb
index ffcfc548c..7ad8d9a52 100644
--- a/app/helpers/budgets_helper.rb
+++ b/app/helpers/budgets_helper.rb
@@ -1,11 +1,4 @@
module BudgetsHelper
- def csv_params
- csv_params = params.clone.merge(format: :csv)
- csv_params = csv_params.to_unsafe_h.transform_keys(&:to_sym)
- csv_params.delete(:page)
- csv_params
- end
-
def namespaced_budget_investment_path(investment, options = {})
case namespace
when "management"
diff --git a/app/helpers/proposals_helper.rb b/app/helpers/proposals_helper.rb
index f2b82e8dc..a497c7037 100644
--- a/app/helpers/proposals_helper.rb
+++ b/app/helpers/proposals_helper.rb
@@ -64,25 +64,6 @@ module ProposalsHelper
proposals_current_view == "default" ? "minimal" : "default"
end
- def link_to_toggle_proposal_selection(proposal)
- if proposal.selected?
- button_text = t("admin.proposals.index.selected")
- html_class = "button expanded"
- else
- button_text = t("admin.proposals.index.select")
- html_class = "button hollow expanded"
- end
-
- case proposal.class.to_s
- when "Proposal"
- path = toggle_selection_admin_proposal_path(proposal)
- when "Legislation::Proposal"
- path = toggle_selection_admin_legislation_process_proposal_path(proposal.process, proposal)
- end
-
- link_to button_text, path, remote: true, method: :patch, class: html_class
- end
-
def css_for_proposal_info_row(proposal)
if proposal.image.present?
if params[:selected].present?
diff --git a/app/models/abilities/administrator.rb b/app/models/abilities/administrator.rb
index 9422a4cf5..5c9fb6dee 100644
--- a/app/models/abilities/administrator.rb
+++ b/app/models/abilities/administrator.rb
@@ -71,10 +71,13 @@ module Abilities
can [:read, :create, :update, :destroy], Budget::Group
can [:read, :create, :update, :destroy], Budget::Heading
- can [:hide, :admin_update, :toggle_selection], Budget::Investment
+ can [:hide, :admin_update], Budget::Investment
can [:valuate, :comment_valuation], Budget::Investment
- cannot [:admin_update, :toggle_selection, :valuate, :comment_valuation],
+ cannot [:admin_update, :valuate, :comment_valuation],
Budget::Investment, budget: { phase: "finished" }
+ can [:select, :deselect], Budget::Investment do |investment|
+ investment.feasible? && investment.valuation_finished? && !investment.budget.finished?
+ end
can :create, Budget::ValuatorAssignment
diff --git a/app/views/admin/budget_investments/_investments.html.erb b/app/views/admin/budget_investments/_investments.html.erb
deleted file mode 100644
index f378fd63a..000000000
--- a/app/views/admin/budget_investments/_investments.html.erb
+++ /dev/null
@@ -1,64 +0,0 @@
-<%= link_to t("admin.budget_investments.index.download_current_selection"),
- admin_budget_budget_investments_path(csv_params),
- class: "float-right small clear" %>
-
-<% if params[:advanced_filters].include?("winners") %>
- <%= render Admin::Budgets::CalculateWinnersButtonComponent.new(@budget, from_investments: true) %>
-<% end %>
-
-<% if @investments.any? %>
- <%= page_entries_info @investments %>
- <%= render "admin/shared/columns_selector",
- cookie: "investments-columns",
- default: %w[id title supports admin valuator geozone feasibility price valuation_finished visible_to_valuators selected incompatible] %>
-
-
- <%= render "filters_description", i18n_namespace: "admin.budget_investments.index" %>
-
-
-
-
- | <%= link_to_investments_sorted_by :id %> |
- <%= link_to_investments_sorted_by :title %> |
- <%= link_to_investments_sorted_by :supports %> |
- <%= t("admin.budget_investments.index.list.admin") %> |
-
- <%= t("admin.budget_investments.index.list.author") %>
- |
-
- <%= t("admin.budget_investments.index.list.valuation_group") %> /
- <%= t("admin.budget_investments.index.list.valuator") %>
- |
- <%= t("admin.budget_investments.index.list.geozone") %> |
- <%= t("admin.budget_investments.index.list.feasibility") %> |
- <% if @budget.show_money? %>
- <%= t("admin.budget_investments.index.list.price") %> |
- <% end %>
-
- <%= t("admin.budget_investments.index.list.valuation_finished") %>
- |
-
- <%= t("admin.budget_investments.index.list.visible_to_valuators") %>
- |
- <%= t("admin.budget_investments.index.list.selected") %> |
- <% if params[:advanced_filters]&.include?("selected") %>
- <%= t("admin.budget_investments.index.list.incompatible") %> |
- <% end %>
-
-
-
-
- <% @investments.each do |investment| %>
-
- <%= render "/admin/budget_investments/select_investment", investment: investment %>
-
- <% end %>
-
-
-
- <%= paginate @investments %>
-<% else %>
-
- <%= t("admin.budget_investments.index.no_budget_investments") %>
-
-<% end %>
diff --git a/app/views/admin/budget_investments/_select_investment.html.erb b/app/views/admin/budget_investments/_select_investment.html.erb
deleted file mode 100644
index 9753b35f9..000000000
--- a/app/views/admin/budget_investments/_select_investment.html.erb
+++ /dev/null
@@ -1,109 +0,0 @@
-
- <%= investment.id %>
- |
-
-
- <%= link_to investment.title,
- admin_budget_budget_investment_path(budget_id: @budget.id,
- id: investment.id,
- params: Budget::Investment.filter_params(params).to_h),
- target: "_blank" %>
- |
-
-
- <%= investment.total_votes %>
- |
-
-
- <% if investment.administrator.present? %>
- ">
- <%= investment.administrator.description_or_name %>
-
- <% else %>
- <%= t("admin.budget_investments.index.no_admin_assigned") %>
- <% end %>
- |
-
-
- <%= investment.author.name %>
- |
-
-
- <% valuators = [investment.assigned_valuation_groups, investment.assigned_valuators].compact %>
- <% no_valuators_assigned = t("admin.budget_investments.index.no_valuators_assigned") %>
- <%= valuators.present? ? valuators.join(", ") : no_valuators_assigned %>
- |
-
-
- <%= investment.heading.name %>
- |
-
-
- <%= t("admin.budget_investments.index.feasibility.#{investment.feasibility}") %>
- |
-
-<% if @budget.show_money? %>
-
- <%= investment.formatted_price %>
- |
-<% end %>
-
-
- <%= investment.valuation_finished? ? t("shared.yes") : t("shared.no") %>
- |
-
-
- <% if can?(:admin_update, investment) %>
- <%= form_for [:admin, investment.budget, investment], remote: true, format: :json do |f| %>
- <%= f.check_box :visible_to_valuators,
- label: false,
- class: "js-submit-on-change",
- id: "budget_investment_visible_to_valuators" %>
- <% end %>
- <% else %>
- <%= investment.visible_to_valuators? ? t("shared.yes") : t("shared.no") %>
- <% end %>
- |
-
-
- <% if investment.selected? %>
- <%= link_to_if can?(:toggle_selection, investment),
- t("admin.budget_investments.index.selected"),
- toggle_selection_admin_budget_budget_investment_path(
- @budget,
- investment,
- filter: params[:filter],
- sort_by: params[:sort_by],
- min_total_supports: params[:min_total_supports],
- max_total_supports: params[:max_total_supports],
- advanced_filters: params[:advanced_filters],
- page: params[:page]
- ),
- method: :patch,
- remote: true,
- class: "button small expanded" %>
- <% elsif investment.feasible? && investment.valuation_finished? %>
- <% if can?(:toggle_selection, investment) %>
- <%= link_to t("admin.budget_investments.index.select"),
- toggle_selection_admin_budget_budget_investment_path(
- @budget,
- investment,
- filter: params[:filter],
- sort_by: params[:sort_by],
- min_total_supports: params[:min_total_supports],
- max_total_supports: params[:max_total_supports],
- advanced_filters: params[:advanced_filters],
- page: params[:page]
- ),
- method: :patch,
- remote: true,
- class: "button small hollow expanded" %>
- <% end %>
- <% end %>
- |
-
-<% if params[:advanced_filters]&.include?("selected") %>
-
- <%= investment.incompatible? ? t("shared.yes") : t("shared.no") %>
- |
-<% end %>
diff --git a/app/views/admin/budget_investments/index.html.erb b/app/views/admin/budget_investments/index.html.erb
index 4878421cd..7a8e46741 100644
--- a/app/views/admin/budget_investments/index.html.erb
+++ b/app/views/admin/budget_investments/index.html.erb
@@ -12,6 +12,4 @@
<%= render "/shared/filter_subnav", i18n_namespace: "admin.budget_investments.index" %>
-
- <%= render "investments" %>
-
+<%= render Admin::BudgetInvestments::InvestmentsComponent.new(@budget, @investments) %>
diff --git a/app/views/admin/budget_investments/index.js.erb b/app/views/admin/budget_investments/index.js.erb
deleted file mode 100644
index c1569cc59..000000000
--- a/app/views/admin/budget_investments/index.js.erb
+++ /dev/null
@@ -1 +0,0 @@
-$("#investments").html("<%= j render("admin/budget_investments/investments") %>");
diff --git a/app/views/admin/budget_investments/toggle_selection.js.erb b/app/views/admin/budget_investments/toggle_selection.js.erb
index ecf457e8c..bac042d52 100644
--- a/app/views/admin/budget_investments/toggle_selection.js.erb
+++ b/app/views/admin/budget_investments/toggle_selection.js.erb
@@ -1 +1,4 @@
-$("#<%= dom_id(@investment) %>").html("<%= j render("select_investment", investment: @investment) %>").trigger("inserted");
+<%= render "admin/shared/toggle_switch",
+ record: @investment,
+ form_class: "toggle-selection",
+ new_content: render(Admin::BudgetInvestments::ToggleSelectionComponent.new(@investment)) %>
diff --git a/app/views/admin/budget_investments/toggle_visible_to_valuators.js.erb b/app/views/admin/budget_investments/toggle_visible_to_valuators.js.erb
new file mode 100644
index 000000000..207826f07
--- /dev/null
+++ b/app/views/admin/budget_investments/toggle_visible_to_valuators.js.erb
@@ -0,0 +1,4 @@
+<%= render "admin/shared/toggle_switch",
+ record: @investment,
+ form_class: "visible-to-valuators",
+ new_content: render(Admin::BudgetInvestments::ToggleVisibleToValuatorsComponent.new(@investment)) %>
diff --git a/app/views/admin/budgets_wizard/phases/toggle_enabled.js.erb b/app/views/admin/budgets_wizard/phases/toggle_enabled.js.erb
index d0688ab25..764515939 100644
--- a/app/views/admin/budgets_wizard/phases/toggle_enabled.js.erb
+++ b/app/views/admin/budgets_wizard/phases/toggle_enabled.js.erb
@@ -1,5 +1,3 @@
-var replacement = $("<%= j render Admin::BudgetPhases::ToggleEnabledComponent.new(@phase) %>");
-var form = $("#<%= dom_id(@phase) %> .toggle-switch");
-
-form.replaceWith(replacement);
-replacement.find("[type='submit']").focus();
+<%= render "admin/shared/toggle_switch",
+ record: @phase,
+ new_content: render(Admin::BudgetPhases::ToggleEnabledComponent.new(@phase)) %>
diff --git a/app/views/admin/legislation/proposals/_proposals.html.erb b/app/views/admin/legislation/proposals/_proposals.html.erb
index 264f92cda..4694b1732 100644
--- a/app/views/admin/legislation/proposals/_proposals.html.erb
+++ b/app/views/admin/legislation/proposals/_proposals.html.erb
@@ -19,7 +19,7 @@
<%= proposal.id %> |
<%= proposal.title %> |
<%= proposal.votes_score %> |
- <%= render "select_proposal", proposal: proposal %> |
+ <%= render Admin::Proposals::ToggleSelectionComponent.new(proposal) %> |
<% end %>
diff --git a/app/views/admin/legislation/proposals/_select_proposal.html.erb b/app/views/admin/legislation/proposals/_select_proposal.html.erb
deleted file mode 100644
index d5d2069ab..000000000
--- a/app/views/admin/legislation/proposals/_select_proposal.html.erb
+++ /dev/null
@@ -1 +0,0 @@
-<%= link_to_toggle_proposal_selection(proposal) %>
diff --git a/app/views/admin/legislation/proposals/toggle_selection.js.erb b/app/views/admin/legislation/proposals/toggle_selection.js.erb
index d38292e9d..258c2901c 100644
--- a/app/views/admin/legislation/proposals/toggle_selection.js.erb
+++ b/app/views/admin/legislation/proposals/toggle_selection.js.erb
@@ -1 +1,3 @@
-$("#<%= dom_id(@proposal) %> .select").html("<%= j render("select_proposal", proposal: @proposal) %>");
+<%= render "admin/shared/toggle_switch",
+ record: @proposal,
+ new_content: render(Admin::Proposals::ToggleSelectionComponent.new(@proposal)) %>
diff --git a/app/views/admin/proposals/_select_proposal.html.erb b/app/views/admin/proposals/_select_proposal.html.erb
deleted file mode 100644
index d5d2069ab..000000000
--- a/app/views/admin/proposals/_select_proposal.html.erb
+++ /dev/null
@@ -1 +0,0 @@
-<%= link_to_toggle_proposal_selection(proposal) %>
diff --git a/app/views/admin/proposals/index.html.erb b/app/views/admin/proposals/index.html.erb
index 5cad75632..27964da5b 100644
--- a/app/views/admin/proposals/index.html.erb
+++ b/app/views/admin/proposals/index.html.erb
@@ -27,7 +27,7 @@
<%= link_to proposal.title, admin_proposal_path(proposal) %> |
<%= proposal.author.username %> |
<%= proposal.milestones.count %> |
- <%= render "select_proposal", proposal: proposal %> |
+ <%= render Admin::Proposals::ToggleSelectionComponent.new(proposal) %> |
<% end %>
diff --git a/app/views/admin/proposals/toggle_selection.js.erb b/app/views/admin/proposals/toggle_selection.js.erb
index 0f7f171ba..258c2901c 100644
--- a/app/views/admin/proposals/toggle_selection.js.erb
+++ b/app/views/admin/proposals/toggle_selection.js.erb
@@ -1 +1,3 @@
-$("#<%= dom_id(@proposal) %> .js-select").html("<%= j render("select_proposal", proposal: @proposal) %>");
+<%= render "admin/shared/toggle_switch",
+ record: @proposal,
+ new_content: render(Admin::Proposals::ToggleSelectionComponent.new(@proposal)) %>
diff --git a/app/views/admin/shared/_toggle_switch.js.erb b/app/views/admin/shared/_toggle_switch.js.erb
new file mode 100644
index 000000000..3be8b17dc
--- /dev/null
+++ b/app/views/admin/shared/_toggle_switch.js.erb
@@ -0,0 +1,5 @@
+var new_toggle_switch = $("<%= j new_content %>");
+var current_toggle_switch = $("#<%= dom_id(record) %> .<%= local_assigns[:form_class] || "toggle-switch" %>");
+
+current_toggle_switch.replaceWith(new_toggle_switch);
+new_toggle_switch.find("[type='submit']").focus();
diff --git a/config/initializers/routes_hierarchy.rb b/config/initializers/routes_hierarchy.rb
index fc9eac7f0..9f15c4587 100644
--- a/config/initializers/routes_hierarchy.rb
+++ b/config/initializers/routes_hierarchy.rb
@@ -24,7 +24,7 @@ module ActionDispatch::Routing::UrlFor
end
def namespaced_polymorphic_path(namespace, resource, options = {})
- if %w[Budget::Group Budget::Heading Legislation::DraftVersion Legislation::Question
+ if %w[Budget::Group Budget::Heading Legislation::DraftVersion Legislation::Proposal Legislation::Question
Poll::Booth Poll::BoothAssignment Poll::Officer Poll::Question Poll::Question::Option
Poll::Question::Option::Video Poll::Shift SDG::LocalTarget].include?(resource.class.name)
resolve = resolve_for(resource)
diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml
index 3b70a864d..d07775899 100644
--- a/config/locales/en/admin.yml
+++ b/config/locales/en/admin.yml
@@ -16,6 +16,8 @@ en:
unmark_featured: Unmark featured
edit: Edit
configure: Configure
+ select: Select
+ show_to_valuators: "Show %{name} to valuators"
officing_booth:
title: "You are officing the booth located at %{booth}. If this is not correct, do not continue and call the help phone number. Thank you."
banners:
@@ -268,7 +270,6 @@ en:
unfeasible: "Unfeasible"
undecided: "Undecided"
selected: "Selected"
- select: "Select"
list:
id: ID
title: Title
@@ -1294,7 +1295,6 @@ en:
title: Proposals
id: ID
author: Author
- select: Select
selected: Selected
milestones: Milestones
no_proposals: There are no proposals.
diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml
index 513ae0306..d2b103454 100644
--- a/config/locales/es/admin.yml
+++ b/config/locales/es/admin.yml
@@ -16,6 +16,8 @@ es:
unmark_featured: Quitar destacado
edit: Editar
configure: Configurar
+ select: Seleccionar
+ show_to_valuators: "Mostrar %{name} a evaluadores"
officing_booth:
title: "Estás ahora mismo en la mesa ubicada en %{booth}. Si esto no es correcto no sigas adelante y llama al teléfono de incidencias. Gracias."
banners:
@@ -268,7 +270,6 @@ es:
unfeasible: "Inviable"
undecided: "Sin decidir"
selected: "Seleccionado"
- select: "Seleccionar"
list:
id: ID
title: Título
@@ -1295,7 +1296,6 @@ es:
id: ID
author: Autor
milestones: Hitos
- select: Seleccionar
selected: Seleccionada
no_proposals: No hay propuestas.
show:
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index 6f3f3d561..9709300d3 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -32,7 +32,11 @@ namespace :admin do
resources :debates, only: [:index, :show]
resources :proposals, only: [:index, :show, :update] do
- member { patch :toggle_selection }
+ member do
+ patch :select
+ patch :deselect
+ end
+
resources :milestones, controller: "proposal_milestones"
resources :progress_bars, except: :show, controller: "proposal_progress_bars"
end
@@ -62,7 +66,12 @@ namespace :admin do
end
resources :budget_investments, only: [:index, :show, :edit, :update] do
- member { patch :toggle_selection }
+ member do
+ patch :select
+ patch :deselect
+ patch :show_to_valuators
+ patch :hide_from_valuators
+ end
resources :audits, only: :show, controller: "budget_investment_audits"
resources :milestones, controller: "budget_investment_milestones"
@@ -232,7 +241,10 @@ namespace :admin do
resources :processes do
resources :questions
resources :proposals do
- member { patch :toggle_selection }
+ member do
+ patch :select
+ patch :deselect
+ end
end
resources :draft_versions
resources :milestones
diff --git a/spec/components/admin/budget_investments/toggle_selection_component_spec.rb b/spec/components/admin/budget_investments/toggle_selection_component_spec.rb
new file mode 100644
index 000000000..aff7da09f
--- /dev/null
+++ b/spec/components/admin/budget_investments/toggle_selection_component_spec.rb
@@ -0,0 +1,66 @@
+require "rails_helper"
+
+describe Admin::BudgetInvestments::ToggleSelectionComponent, :admin do
+ context "open budget" do
+ let(:budget) { create(:budget) }
+
+ it "is not rendered for not-yet-evaluated investments" do
+ unfeasible_investment = create(:budget_investment, :unfeasible, budget: budget)
+ feasible_investment = create(:budget_investment, :feasible, budget: budget)
+
+ render_inline Admin::BudgetInvestments::ToggleSelectionComponent.new(unfeasible_investment)
+ expect(page).not_to be_rendered
+
+ render_inline Admin::BudgetInvestments::ToggleSelectionComponent.new(feasible_investment)
+ expect(page).not_to be_rendered
+ end
+
+ it "renders a button to select unselected evaluated investments" do
+ valuation_finished_investment = create(:budget_investment, :feasible, :finished, budget: budget)
+
+ render_inline Admin::BudgetInvestments::ToggleSelectionComponent.new(valuation_finished_investment)
+
+ expect(page).to have_button count: 1
+ expect(page).to have_button exact_text: "No"
+ expect(page).to have_css "button[aria-pressed='false']"
+ end
+
+ it "renders a button to deselect selected investments" do
+ selected_investment = create(:budget_investment, :selected, budget: budget)
+
+ render_inline Admin::BudgetInvestments::ToggleSelectionComponent.new(selected_investment)
+
+ expect(page).to have_button count: 1
+ expect(page).to have_button exact_text: "Yes"
+ expect(page).to have_css "button[aria-pressed='true']"
+ end
+ end
+
+ context "finished budget" do
+ let(:budget) { create(:budget, :finished) }
+
+ it "is not rendered for unselected investments" do
+ unfeasible_investment = create(:budget_investment, :unfeasible, budget: budget)
+ feasible_investment = create(:budget_investment, :feasible, budget: budget)
+ valuation_finished_investment = create(:budget_investment, :feasible, :finished, budget: budget)
+
+ render_inline Admin::BudgetInvestments::ToggleSelectionComponent.new(unfeasible_investment)
+ expect(page).not_to be_rendered
+
+ render_inline Admin::BudgetInvestments::ToggleSelectionComponent.new(feasible_investment)
+ expect(page).not_to be_rendered
+
+ render_inline Admin::BudgetInvestments::ToggleSelectionComponent.new(valuation_finished_investment)
+ expect(page).not_to be_rendered
+ end
+
+ it "renders plain text for selected investments" do
+ selected_investment = create(:budget_investment, :selected, budget: budget)
+
+ render_inline Admin::BudgetInvestments::ToggleSelectionComponent.new(selected_investment)
+
+ expect(page).to have_content "Selected"
+ expect(page).not_to have_button
+ end
+ end
+end
diff --git a/spec/components/admin/budget_investments/toggle_visible_to_valuators_component_spec.rb b/spec/components/admin/budget_investments/toggle_visible_to_valuators_component_spec.rb
new file mode 100644
index 000000000..cf73b05f2
--- /dev/null
+++ b/spec/components/admin/budget_investments/toggle_visible_to_valuators_component_spec.rb
@@ -0,0 +1,21 @@
+require "rails_helper"
+
+describe Admin::BudgetInvestments::ToggleVisibleToValuatorsComponent, :admin do
+ describe "aria-pressed attribute" do
+ it "is true for investments visible to valuators" do
+ investment = create(:budget_investment, :visible_to_valuators)
+
+ render_inline Admin::BudgetInvestments::ToggleVisibleToValuatorsComponent.new(investment)
+
+ expect(page).to have_css "[aria-pressed=true]"
+ end
+
+ it "is true for investments invisible to valuators" do
+ investment = create(:budget_investment, :invisible_to_valuators)
+
+ render_inline Admin::BudgetInvestments::ToggleVisibleToValuatorsComponent.new(investment)
+
+ expect(page).to have_css "[aria-pressed=false]"
+ end
+ end
+end
diff --git a/spec/components/admin/proposals/toggle_selection_component_spec.rb b/spec/components/admin/proposals/toggle_selection_component_spec.rb
new file mode 100644
index 000000000..2350c92f8
--- /dev/null
+++ b/spec/components/admin/proposals/toggle_selection_component_spec.rb
@@ -0,0 +1,21 @@
+require "rails_helper"
+
+describe Admin::Proposals::ToggleSelectionComponent, :admin do
+ describe "aria-pressed attribute" do
+ it "is true for selected proposals" do
+ proposal = create(:proposal, :selected)
+
+ render_inline Admin::Proposals::ToggleSelectionComponent.new(proposal)
+
+ expect(page).to have_css "button[aria-pressed='true']"
+ end
+
+ it "is false for not selected proposals" do
+ proposal = create(:proposal)
+
+ render_inline Admin::Proposals::ToggleSelectionComponent.new(proposal)
+
+ expect(page).to have_css "button[aria-pressed='false']"
+ end
+ end
+end
diff --git a/spec/controllers/admin/budget_investments_controller_spec.rb b/spec/controllers/admin/budget_investments_controller_spec.rb
index 6fd1cc4e7..53122d970 100644
--- a/spec/controllers/admin/budget_investments_controller_spec.rb
+++ b/spec/controllers/admin/budget_investments_controller_spec.rb
@@ -23,18 +23,139 @@ describe Admin::BudgetInvestmentsController, :admin do
end
end
- describe "PATCH update" do
- it "does not redirect on AJAX requests" do
- investment = create(:budget_investment)
+ describe "PATCH show_to_valuators" do
+ let(:investment) { create(:budget_investment, :invisible_to_valuators) }
- patch :update, params: {
- id: investment,
- budget_id: investment.budget,
- format: :json,
- budget_investment: { visible_to_valuators: true }
- }
+ it "marks the investment as visible to valuators" do
+ expect do
+ patch :show_to_valuators, xhr: true, params: { id: investment, budget_id: investment.budget }
+ end.to change { investment.reload.visible_to_valuators? }.from(false).to(true)
- expect(response).not_to be_redirect
+ expect(response).to be_successful
+ end
+
+ it "does not modify investments visible to valuators" do
+ investment.update!(visible_to_valuators: true)
+
+ expect do
+ patch :show_to_valuators, xhr: true, params: { id: investment, budget_id: investment.budget }
+ end.not_to change { investment.reload.visible_to_valuators? }
+ end
+
+ it "redirects admins without JavaScript to the same page" do
+ request.env["HTTP_REFERER"] = admin_budget_budget_investments_path(investment.budget)
+
+ patch :show_to_valuators, params: { id: investment, budget_id: investment.budget }
+
+ expect(response).to redirect_to admin_budget_budget_investments_path(investment.budget)
+ expect(flash[:notice]).to eq "Investment project updated successfully."
+ end
+ end
+
+ describe "PATCH hide_from_valuators" do
+ let(:investment) { create(:budget_investment, :visible_to_valuators) }
+
+ it "marks the investment as visible to valuators" do
+ expect do
+ patch :hide_from_valuators, xhr: true, params: { id: investment, budget_id: investment.budget }
+ end.to change { investment.reload.visible_to_valuators? }.from(true).to(false)
+
+ expect(response).to be_successful
+ end
+
+ it "does not modify investments visible to valuators" do
+ investment.update!(visible_to_valuators: false)
+
+ expect do
+ patch :hide_from_valuators, xhr: true, params: { id: investment, budget_id: investment.budget }
+ end.not_to change { investment.reload.visible_to_valuators? }
+ end
+
+ it "redirects admins without JavaScript to the same page" do
+ request.env["HTTP_REFERER"] = admin_budget_budget_investments_path(investment.budget)
+
+ patch :hide_from_valuators, params: { id: investment, budget_id: investment.budget }
+
+ expect(response).to redirect_to admin_budget_budget_investments_path(investment.budget)
+ expect(flash[:notice]).to eq "Investment project updated successfully."
+ end
+ end
+
+ describe "PATCH select" do
+ let(:investment) { create(:budget_investment, :feasible, :finished) }
+
+ it "selects the investment" do
+ expect do
+ patch :select, xhr: true, params: { id: investment, budget_id: investment.budget }
+ end.to change { investment.reload.selected? }.from(false).to(true)
+
+ expect(response).to be_successful
+ end
+
+ it "does not modify already selected investments" do
+ investment.update!(selected: true)
+
+ expect do
+ patch :select, xhr: true, params: { id: investment, budget_id: investment.budget }
+ end.not_to change { investment.reload.selected? }
+ end
+
+ it "uses the select/deselect authorization rules" do
+ investment.update!(valuation_finished: false)
+
+ patch :select, xhr: true, params: { id: investment, budget_id: investment.budget }
+
+ expect(flash[:alert]).to eq "You do not have permission to carry out the action " \
+ "'select' on Investment."
+ expect(investment).not_to be_selected
+ end
+
+ it "redirects admins without JavaScript to the same page" do
+ request.env["HTTP_REFERER"] = admin_budget_budget_investments_path(investment.budget)
+
+ patch :select, params: { id: investment, budget_id: investment.budget }
+
+ expect(response).to redirect_to admin_budget_budget_investments_path(investment.budget)
+ expect(flash[:notice]).to eq "Investment project updated successfully."
+ end
+ end
+
+ describe "PATCH deselect" do
+ let(:investment) { create(:budget_investment, :feasible, :finished, :selected) }
+
+ it "deselects the investment" do
+ expect do
+ patch :deselect, xhr: true, params: { id: investment, budget_id: investment.budget }
+ end.to change { investment.reload.selected? }.from(true).to(false)
+
+ expect(response).to be_successful
+ end
+
+ it "does not modify non-selected investments" do
+ investment.update!(selected: false)
+
+ expect do
+ patch :deselect, xhr: true, params: { id: investment, budget_id: investment.budget }
+ end.not_to change { investment.reload.selected? }
+ end
+
+ it "uses the select/deselect authorization rules" do
+ investment.update!(valuation_finished: false)
+
+ patch :deselect, xhr: true, params: { id: investment, budget_id: investment.budget }
+
+ expect(flash[:alert]).to eq "You do not have permission to carry out the action " \
+ "'deselect' on Investment."
+ expect(investment).to be_selected
+ end
+
+ it "redirects admins without JavaScript to the same page" do
+ request.env["HTTP_REFERER"] = admin_budget_budget_investments_path(investment.budget)
+
+ patch :deselect, params: { id: investment, budget_id: investment.budget }
+
+ expect(response).to redirect_to admin_budget_budget_investments_path(investment.budget)
+ expect(flash[:notice]).to eq "Investment project updated successfully."
end
end
end
diff --git a/spec/controllers/admin/legislation/proposals_controller_spec.rb b/spec/controllers/admin/legislation/proposals_controller_spec.rb
new file mode 100644
index 000000000..6cd64562c
--- /dev/null
+++ b/spec/controllers/admin/legislation/proposals_controller_spec.rb
@@ -0,0 +1,61 @@
+require "rails_helper"
+
+describe Admin::Legislation::ProposalsController, :admin do
+ describe "PATCH select" do
+ let(:proposal) { create(:legislation_proposal) }
+
+ it "selects the proposal" do
+ expect do
+ patch :select, xhr: true, params: { id: proposal.id, process_id: proposal.process.id }
+ end.to change { proposal.reload.selected? }.from(false).to(true)
+
+ expect(response).to be_successful
+ end
+
+ it "does not modify already selected proposals" do
+ proposal.update!(selected: true)
+
+ expect do
+ patch :select, xhr: true, params: { id: proposal.id, process_id: proposal.process.id }
+ end.not_to change { proposal.reload.selected? }
+ end
+
+ it "redirects admins without JavaScript to the same page" do
+ request.env["HTTP_REFERER"] = admin_proposals_path
+
+ patch :select, params: { id: proposal.id, process_id: proposal.process.id }
+
+ expect(response).to redirect_to admin_proposals_path
+ expect(flash[:notice]).to eq "Proposal updated successfully."
+ end
+ end
+
+ describe "PATCH deselect" do
+ let(:proposal) { create(:legislation_proposal, :selected) }
+
+ it "deselects the proposal" do
+ expect do
+ patch :deselect, xhr: true, params: { id: proposal.id, process_id: proposal.process.id }
+ end.to change { proposal.reload.selected? }.from(true).to(false)
+
+ expect(response).to be_successful
+ end
+
+ it "does not modify non-selected proposals" do
+ proposal.update!(selected: false)
+
+ expect do
+ patch :deselect, xhr: true, params: { id: proposal.id, process_id: proposal.process.id }
+ end.not_to change { proposal.reload.selected? }
+ end
+
+ it "redirects admins without JavaScript to the same page" do
+ request.env["HTTP_REFERER"] = admin_proposals_path
+
+ patch :deselect, params: { id: proposal.id, process_id: proposal.process.id }
+
+ expect(response).to redirect_to admin_proposals_path
+ expect(flash[:notice]).to eq "Proposal updated successfully."
+ end
+ end
+end
diff --git a/spec/controllers/admin/proposals_controller_spec.rb b/spec/controllers/admin/proposals_controller_spec.rb
new file mode 100644
index 000000000..b23369adb
--- /dev/null
+++ b/spec/controllers/admin/proposals_controller_spec.rb
@@ -0,0 +1,61 @@
+require "rails_helper"
+
+describe Admin::ProposalsController, :admin do
+ describe "PATCH select" do
+ let(:proposal) { create(:proposal) }
+
+ it "selects the proposal" do
+ expect do
+ patch :select, xhr: true, params: { id: proposal.id }
+ end.to change { proposal.reload.selected? }.from(false).to(true)
+
+ expect(response).to be_successful
+ end
+
+ it "does not modify already selected proposals" do
+ proposal.update!(selected: true)
+
+ expect do
+ patch :select, xhr: true, params: { id: proposal.id }
+ end.not_to change { proposal.reload.selected? }
+ end
+
+ it "redirects admins without JavaScript to the same page" do
+ request.env["HTTP_REFERER"] = admin_proposals_path
+
+ patch :select, params: { id: proposal.id }
+
+ expect(response).to redirect_to admin_proposals_path
+ expect(flash[:notice]).to eq "Proposal updated successfully."
+ end
+ end
+
+ describe "PATCH deselect" do
+ let(:proposal) { create(:proposal, :selected) }
+
+ it "deselects the proposal" do
+ expect do
+ patch :deselect, xhr: true, params: { id: proposal.id }
+ end.to change { proposal.reload.selected? }.from(true).to(false)
+
+ expect(response).to be_successful
+ end
+
+ it "does not modify non-selected proposals" do
+ proposal.update!(selected: false)
+
+ expect do
+ patch :deselect, xhr: true, params: { id: proposal.id }
+ end.not_to change { proposal.reload.selected? }
+ end
+
+ it "redirects admins without JavaScript to the same page" do
+ request.env["HTTP_REFERER"] = admin_proposals_path
+
+ patch :deselect, params: { id: proposal.id }
+
+ expect(response).to redirect_to admin_proposals_path
+ expect(flash[:notice]).to eq "Proposal updated successfully."
+ end
+ end
+end
diff --git a/spec/factories/legislations.rb b/spec/factories/legislations.rb
index 4ae250317..5c27fc3f5 100644
--- a/spec/factories/legislations.rb
+++ b/spec/factories/legislations.rb
@@ -177,5 +177,9 @@ FactoryBot.define do
trait :hidden do
hidden_at { Time.current }
end
+
+ trait :selected do
+ selected { true }
+ end
end
end
diff --git a/spec/models/abilities/administrator_spec.rb b/spec/models/abilities/administrator_spec.rb
index f24a6f4b4..f75aac370 100644
--- a/spec/models/abilities/administrator_spec.rb
+++ b/spec/models/abilities/administrator_spec.rb
@@ -115,7 +115,11 @@ describe Abilities::Administrator do
it { should_not be_able_to(:admin_update, finished_investment) }
it { should_not be_able_to(:valuate, finished_investment) }
it { should_not be_able_to(:comment_valuation, finished_investment) }
- it { should_not be_able_to(:toggle_selection, finished_investment) }
+
+ it { should be_able_to([:select, :deselect], create(:budget_investment, :feasible, :finished)) }
+ it { should_not be_able_to([:select, :deselect], create(:budget_investment, :feasible, :open)) }
+ it { should_not be_able_to([:select, :deselect], create(:budget_investment, :unfeasible, :finished)) }
+ it { should_not be_able_to([:select, :deselect], finished_investment) }
it { should be_able_to(:destroy, proposal_image) }
it { should be_able_to(:destroy, proposal_document) }
diff --git a/spec/system/admin/budget_investments_spec.rb b/spec/system/admin/budget_investments_spec.rb
index 506562d17..bd695df79 100644
--- a/spec/system/admin/budget_investments_spec.rb
+++ b/spec/system/admin/budget_investments_spec.rb
@@ -43,21 +43,6 @@ describe "Admin budget investments", :admin do
expect(page).not_to have_content("€")
end
- scenario "If budget is finished do not show 'Selected' button" do
- finished_budget = create(:budget, :finished)
- budget_investment = create(:budget_investment, budget: finished_budget, cached_votes_up: 77)
-
- visit admin_budget_budget_investments_path(budget_id: finished_budget.id)
-
- within("#budget_investment_#{budget_investment.id}") do
- expect(page).to have_content(budget_investment.title)
- expect(page).to have_content(budget_investment.heading.name)
- expect(page).to have_content(budget_investment.id)
- expect(page).to have_content(budget_investment.total_votes)
- expect(page).not_to have_link("Selected")
- end
- end
-
scenario "Display admin and valuator assignments" do
olga = create(:user, username: "Olga")
miriam = create(:user, username: "Miriam")
@@ -1422,62 +1407,19 @@ describe "Admin budget investments", :admin do
expect(page).not_to have_content(feasible_vf_bi.title)
end
- scenario "Showing the selection buttons" do
- visit admin_budget_budget_investments_path(budget)
-
- within("#budget_investment_#{unfeasible_bi.id}") do
- expect(page).not_to have_link("Select")
- expect(page).not_to have_link("Selected")
- end
-
- within("#budget_investment_#{feasible_bi.id}") do
- expect(page).not_to have_link("Select")
- expect(page).not_to have_link("Selected")
- end
-
- within("#budget_investment_#{feasible_vf_bi.id}") do
- expect(page).to have_link("Select")
- expect(page).not_to have_link("Selected")
- end
-
- within("#budget_investment_#{selected_bi.id}") do
- expect(page).not_to have_link("Select")
- expect(page).to have_link("Selected")
- end
- end
-
- scenario "Show only selected text when budget is finished" do
- budget.update!(phase: "finished")
-
- visit admin_budget_budget_investments_path(budget)
-
- within("#budget_investment_#{unfeasible_bi.id} #selection") do
- expect(page).not_to have_content("Select")
- expect(page).not_to have_content("Selected")
- end
-
- within("#budget_investment_#{feasible_bi.id} #selection") do
- expect(page).not_to have_content("Select")
- expect(page).not_to have_content("Selected")
- end
-
- within("#budget_investment_#{feasible_vf_bi.id} #selection") do
- expect(page).not_to have_content("Select")
- expect(page).not_to have_content("Selected")
- end
-
- within("#budget_investment_#{selected_bi.id} #selection") do
- expect(page).not_to contain_exactly("Select")
- expect(page).to have_content("Selected")
- end
- end
-
scenario "Selecting an investment" do
visit admin_budget_budget_investments_path(budget)
- within("#budget_investment_#{feasible_vf_bi.id}") do
- click_link("Select")
- expect(page).to have_link("Selected")
+ expect(page).to have_content "Unfeasible project"
+
+ within("tr", text: "Feasible, VF project") do
+ within("[data-field=selected]") do
+ expect(page).to have_content "No"
+
+ click_button "Select Feasible, VF project"
+
+ expect(page).to have_content "Yes"
+ end
end
click_link "Advanced filters"
@@ -1485,9 +1427,12 @@ describe "Admin budget investments", :admin do
within("#advanced_filters") { check("Selected") }
click_button("Filter")
- within("#budget_investment_#{feasible_vf_bi.id}") do
- expect(page).not_to have_link("Select")
- expect(page).to have_link("Selected")
+ expect(page).not_to have_content "Unfeasible project"
+
+ within("tr", text: "Feasible, VF project") do
+ within("[data-field=selected]") do
+ expect(page).to have_content "Yes"
+ end
end
end
@@ -1500,21 +1445,26 @@ describe "Admin budget investments", :admin do
expect(page).to have_content("There are 2 investments")
- within("#budget_investment_#{selected_bi.id}") do
- click_link("Selected")
+ within("tr", text: "Selected project") do
+ within("[data-field=selected]") do
+ expect(page).to have_content "Yes"
- expect(page).to have_link("Select")
+ click_button "Select Selected project"
+
+ expect(page).to have_content "No"
+ end
end
- click_button("Filter")
- expect(page).not_to have_content(selected_bi.title)
- expect(page).to have_content("There is 1 investment")
+ click_button "Filter"
+ expect(page).not_to have_content "Selected project"
+ expect(page).to have_content "There is 1 investment"
visit admin_budget_budget_investments_path(budget)
- within("#budget_investment_#{selected_bi.id}") do
- expect(page).to have_link("Select")
- expect(page).not_to have_link("Selected")
+ within("tr", text: "Selected project") do
+ within("[data-field=selected]") do
+ expect(page).to have_content "No"
+ end
end
end
@@ -1526,10 +1476,12 @@ describe "Admin budget investments", :admin do
visit admin_budget_budget_investments_path(budget)
- within("#budget_investment_#{selected_bi.id}") do
- click_link("Selected")
+ within("tr", text: "Selected project") do
+ within("[data-field=selected]") do
+ click_button "Select Selected project"
- expect(page).to have_link "Select"
+ expect(page).to have_content "No"
+ end
end
click_link("Next")
@@ -1545,8 +1497,8 @@ describe "Admin budget investments", :admin do
let(:heading) { create(:budget_heading, budget: budget) }
- let(:investment1) { create(:budget_investment, heading: heading) }
- let(:investment2) { create(:budget_investment, heading: heading) }
+ let(:investment1) { create(:budget_investment, heading: heading, title: "Investment 1") }
+ let(:investment2) { create(:budget_investment, heading: heading, title: "Investment 2") }
scenario "Mark as visible to valuator" do
investment1.valuators << valuator
@@ -1559,17 +1511,22 @@ describe "Admin budget investments", :admin do
check "Under valuation"
click_button "Filter"
- within("#budget_investment_#{investment1.id}") do
- check "budget_investment_visible_to_valuators"
+ within("tr", text: "Investment 1") do
+ within("[data-field=visible_to_valuators]") do
+ expect(page).to have_content "No"
+
+ click_button "Show Investment 1 to valuators"
+
+ expect(page).to have_content "Yes"
+ end
end
- visit admin_budget_budget_investments_path(budget)
- click_link "Advanced filters"
- check "Under valuation"
- click_button "Filter"
+ refresh
- within("#budget_investment_#{investment1.id}") do
- expect(find("#budget_investment_visible_to_valuators")).to be_checked
+ within("tr", text: "Investment 1") do
+ within("[data-field=visible_to_valuators]") do
+ expect(page).to have_content "Yes"
+ end
end
end
@@ -1608,18 +1565,22 @@ describe "Admin budget investments", :admin do
check "Under valuation"
click_button "Filter"
- within("#budget_investment_#{investment1.id}") do
- uncheck "budget_investment_visible_to_valuators"
+ within("tr", text: "Investment 1") do
+ within("[data-field=visible_to_valuators]") do
+ expect(page).to have_content "Yes"
+
+ click_button "Show Investment 1 to valuators"
+
+ expect(page).to have_content "No"
+ end
end
- visit admin_budget_budget_investments_path(budget)
+ refresh
- click_link "Advanced filters"
- check "Under valuation"
- click_button "Filter"
-
- within("#budget_investment_#{investment1.id}") do
- expect(find("#budget_investment_visible_to_valuators")).not_to be_checked
+ within("tr", text: "Investment 1") do
+ within("[data-field=visible_to_valuators]") do
+ expect(page).to have_content "No"
+ end
end
end
@@ -1631,42 +1592,44 @@ describe "Admin budget investments", :admin do
visit admin_budget_budget_investments_path(budget)
within "tr", text: "Visible" do
- within "td[data-field=visible_to_valuators]" do
+ within "[data-field=visible_to_valuators]" do
expect(page).to have_text "Yes"
- expect(page).not_to have_field "budget_investment_visible_to_valuators"
+ expect(page).not_to have_button
end
end
within "tr", text: "Invisible" do
- within "td[data-field=visible_to_valuators]" do
+ within "[data-field=visible_to_valuators]" do
expect(page).to have_text "No"
- expect(page).not_to have_field "budget_investment_visible_to_valuators"
+ expect(page).not_to have_button
end
end
end
scenario "Showing the valuating checkbox" do
- investment1 = create(:budget_investment, :with_administrator, :with_valuator, :visible_to_valuators,
- budget: budget)
- investment2 = create(:budget_investment, :with_administrator, :with_valuator, :invisible_to_valuators,
- budget: budget)
+ investment1.valuators << valuator
+ investment2.valuators << valuator
+ investment1.update!(administrator: admin, visible_to_valuators: true)
+ investment2.update!(administrator: admin, visible_to_valuators: false)
visit admin_budget_budget_investments_path(budget)
- expect(page).to have_css("#budget_investment_visible_to_valuators")
-
click_link "Advanced filters"
check "Under valuation"
click_button "Filter"
- within("#budget_investment_#{investment1.id}") do
- valuating_checkbox = find("#budget_investment_visible_to_valuators")
- expect(valuating_checkbox).to be_checked
+ within "tr", text: "Investment 1" do
+ within "[data-field=visible_to_valuators]" do
+ expect(page).to have_content "Yes"
+ expect(page).to have_css "button[aria-pressed='true']"
+ end
end
- within("#budget_investment_#{investment2.id}") do
- valuating_checkbox = find("#budget_investment_visible_to_valuators")
- expect(valuating_checkbox).not_to be_checked
+ within "tr", text: "Investment 2" do
+ within "[data-field=visible_to_valuators]" do
+ expect(page).to have_content "No"
+ expect(page).to have_css "button[aria-pressed='false']"
+ end
end
end
@@ -1676,8 +1639,14 @@ describe "Admin budget investments", :admin do
visit admin_budget_budget_investments_path(budget)
- within("#budget_investment_#{investment1.id}") do
- check "budget_investment_visible_to_valuators"
+ within "tr", text: "Investment 1" do
+ within "[data-field=visible_to_valuators]" do
+ expect(page).to have_content "No"
+
+ click_button "Show Investment 1 to valuators"
+
+ expect(page).to have_content "Yes"
+ end
end
visit edit_admin_budget_budget_investment_path(budget, investment1)
@@ -1867,11 +1836,16 @@ describe "Admin budget investments", :admin do
within("#js-columns-selector-wrapper") { uncheck "Title" }
within("#budget_investment_#{investment.id}") do
- click_link "Selected"
+ within("[data-field=selected]") do
+ expect(page).to have_content "Yes"
- expect(page).to have_link "Select"
- expect(page).not_to have_content "Don't display me, please!"
+ click_button "Select Don't display me, please!"
+
+ expect(page).to have_content "No"
+ end
end
+
+ expect(page).not_to have_content "Don't display me, please!"
end
scenario "When restoring the page from browser history renders columns selectors only once" do
diff --git a/spec/system/admin/legislation/proposals_spec.rb b/spec/system/admin/legislation/proposals_spec.rb
index 8653594f2..0b2a1d6eb 100644
--- a/spec/system/admin/legislation/proposals_spec.rb
+++ b/spec/system/admin/legislation/proposals_spec.rb
@@ -11,18 +11,18 @@ describe "Admin collaborative legislation", :admin do
expect(page).to have_content(proposal.title)
expect(page).to have_content(proposal.id)
expect(page).to have_content(proposal.cached_votes_score)
- expect(page).to have_content("Select")
+ expect(page).to have_content("No")
end
end
scenario "Selecting legislation proposals" do
- proposal = create(:legislation_proposal, cached_votes_score: 10)
+ proposal = create(:legislation_proposal, title: "Add more accessibility tests")
visit admin_legislation_process_proposals_path(proposal.legislation_process_id)
- click_link "Select"
+ click_button "Select Add more accessibility tests"
within "#legislation_proposal_#{proposal.id}" do
- expect(page).to have_content("Selected")
+ expect(page).to have_content "Yes"
end
end
diff --git a/spec/system/admin/proposals_spec.rb b/spec/system/admin/proposals_spec.rb
index 473470c67..d0377a70b 100644
--- a/spec/system/admin/proposals_spec.rb
+++ b/spec/system/admin/proposals_spec.rb
@@ -22,31 +22,39 @@ describe "Admin proposals", :admin do
end
scenario "Select a proposal" do
- proposal = create(:proposal)
+ proposal = create(:proposal, title: "Forbid door-to-door sales")
visit admin_proposals_path
- within("#proposal_#{proposal.id}") { click_link "Select" }
+ within("#proposal_#{proposal.id}") do
+ expect(page).to have_content "No"
- within("#proposal_#{proposal.id}") { expect(page).to have_link "Selected" }
+ click_button "Select Forbid door-to-door sales"
+
+ expect(page).to have_content "Yes"
+ end
refresh
- within("#proposal_#{proposal.id}") { expect(page).to have_link "Selected" }
+ within("#proposal_#{proposal.id}") { expect(page).to have_content "Yes" }
end
scenario "Unselect a proposal" do
- proposal = create(:proposal, :selected)
+ proposal = create(:proposal, :selected, title: "Allow door-to-door sales")
visit admin_proposals_path
- within("#proposal_#{proposal.id}") { click_link "Selected" }
+ within("#proposal_#{proposal.id}") do
+ expect(page).to have_content "Yes"
- within("#proposal_#{proposal.id}") { expect(page).to have_link "Select" }
+ click_button "Select Allow door-to-door sales"
+
+ expect(page).to have_content "No"
+ end
refresh
- within("#proposal_#{proposal.id}") { expect(page).to have_link "Select" }
+ within("#proposal_#{proposal.id}") { expect(page).to have_content "No" }
end
end
diff --git a/spec/views/admin/budget_investments/select_investment_spec.rb b/spec/views/admin/budget_investments/select_investment_spec.rb
deleted file mode 100644
index e04c6dd8f..000000000
--- a/spec/views/admin/budget_investments/select_investment_spec.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require "rails_helper"
-
-describe "investment row" do
- it "uses a JSON request to update visible to valuators" do
- investment = create(:budget_investment)
- @budget = investment.budget
- sign_in(create(:administrator).user)
-
- render "admin/budget_investments/select_investment", investment: investment
-
- expect(rendered).to have_css "form[action$='json'] input[name$='[visible_to_valuators]']"
- end
-end