Add filters on budget investments index page

Now it's easier to change the investments filter. Previously we had to
go back to the budget index page, change the filter there, and then
select one heading.

Now the links to change the current filter in the budget index page
aren't needed anymore.
This commit is contained in:
decabeza
2020-05-06 15:06:40 +02:00
committed by Javi Martín
parent f5c1cfbf8c
commit 04605d5d5b
16 changed files with 132 additions and 79 deletions

View File

@@ -70,6 +70,7 @@
//= require advanced_search //= require advanced_search
//= require registration_form //= require registration_form
//= require suggest //= require suggest
//= require filter_selector
//= require forms //= require forms
//= require valuation_budget_investment_form //= require valuation_budget_investment_form
//= require embed_video //= require embed_video
@@ -133,6 +134,7 @@ var initialize_modules = function() {
App.RegistrationForm.initialize(); App.RegistrationForm.initialize();
App.Suggest.initialize(); App.Suggest.initialize();
App.Forms.initialize(); App.Forms.initialize();
App.FilterSelector.initialize();
App.ValuationBudgetInvestmentForm.initialize(); App.ValuationBudgetInvestmentForm.initialize();
App.EmbedVideo.initialize(); App.EmbedVideo.initialize();
App.FixedBar.initialize(); App.FixedBar.initialize();

View File

@@ -0,0 +1,8 @@
(function() {
"use strict";
App.FilterSelector = {
initialize: function() {
App.Forms.submitOnChange(".filter-selector select");
}
};
}).call(this);

View File

@@ -8,8 +8,8 @@
} }
}); });
}, },
submitOnChange: function() { submitOnChange: function(selector) {
$("body").on("change", ".js-submit-on-change", function() { $("body").on("change", selector, function() {
$(this).closest("form").submit(); $(this).closest("form").submit();
return false; return false;
}); });
@@ -58,7 +58,7 @@
}, },
initialize: function() { initialize: function() {
App.Forms.disableEnter(); App.Forms.disableEnter();
App.Forms.submitOnChange(); App.Forms.submitOnChange(".js-submit-on-change");
App.Forms.toggleLink(); App.Forms.toggleLink();
App.Forms.synchronizeInputs(); App.Forms.synchronizeInputs();
App.Forms.hideOrShowFieldsAfterSelection(); App.Forms.hideOrShowFieldsAfterSelection();

View File

@@ -12,6 +12,7 @@
@import "milestones"; @import "milestones";
@import "pages"; @import "pages";
@import "dashboard"; @import "dashboard";
@import "filter_selector";
@import "legislation"; @import "legislation";
@import "legislation_process"; @import "legislation_process";
@import "legislation_process_form"; @import "legislation_process_form";

View File

@@ -0,0 +1,14 @@
.filter-selector {
text-align: right;
label {
display: inline-block;
font-size: $small-font-size;
margin-right: $line-height / 2;
padding-top: $line-height / 2;
}
select {
width: auto;
}
}

View File

@@ -0,0 +1,5 @@
<%= form_tag({}, method: :get, enforce_utf8: false, class: "filter-selector") do %>
<%= query_parameters_tags %>
<%= label_tag "filter_selector_filter", t("#{i18n_namespace}.filter") %>
<%= select_tag "filter", options_for_select(filter_options, current_filter), id: "filter_selector_filter" %>
<% end %>

View File

@@ -0,0 +1,22 @@
class Shared::FilterSelectorComponent < ApplicationComponent
delegate :valid_filters, :current_filter, to: :helpers
attr_reader :i18n_namespace
def initialize(i18n_namespace:)
@i18n_namespace = i18n_namespace
end
private
def query_parameters_tags
safe_join(request.query_parameters.reject do |name, _|
["page", "filter"].include?(name)
end.map do |name, value|
hidden_field_tag name, value, id: "filter_selector_#{name}"
end)
end
def filter_options
valid_filters.map { |filter| [t("#{i18n_namespace}.filters.#{filter}"), filter] }
end
end

View File

@@ -1,8 +1,4 @@
module BudgetsHelper module BudgetsHelper
def show_links_to_budget_investments(budget)
["balloting", "reviewing_ballots", "finished"].include? budget.phase
end
def budget_voting_styles_select_options def budget_voting_styles_select_options
Budget::VOTING_STYLES.map do |style| Budget::VOTING_STYLES.map do |style|
[Budget.human_attribute_name("voting_style_#{style}"), style] [Budget.human_attribute_name("voting_style_#{style}"), style]

View File

@@ -55,29 +55,6 @@
<h3><%= t("budgets.index.map") %></h3> <h3><%= t("budgets.index.map") %></h3>
<%= render_map(nil, "budgets", false, nil, @budgets_coordinates) %> <%= render_map(nil, "budgets", false, nil, @budgets_coordinates) %>
</div> </div>
<ul class="no-bullet margin-top">
<% show_links = show_links_to_budget_investments(current_budget) %>
<% if show_links %>
<li>
<%= link_to budget_path(current_budget) do %>
<small><%= t("budgets.index.investment_proyects") %></small>
<% end %>
</li>
<% end %>
<li>
<%= link_to budget_path(current_budget, filter: "unfeasible") do %>
<small><%= t("budgets.index.unfeasible_investment_proyects") %></small>
<% end %>
</li>
<% if show_links %>
<li>
<%= link_to budget_path(current_budget, filter: "unselected") do %>
<small><%= t("budgets.index.not_selected_investment_proyects") %></small>
<% end %>
</li>
<% end %>
</ul>
<% end %> <% end %>
</div> </div>
</div> </div>

View File

@@ -70,6 +70,8 @@
<%= render("shared/order_links", i18n_namespace: "budgets.investments.index") %> <%= render("shared/order_links", i18n_namespace: "budgets.investments.index") %>
<% end %> <% end %>
<%= render Shared::FilterSelectorComponent.new(i18n_namespace: "budgets.investments.index") %>
<% if investments_default_view? %> <% if investments_default_view? %>
<% @investments.each do |investment| %> <% @investments.each do |investment| %>

View File

@@ -127,6 +127,7 @@ ignore_missing:
## Consider these keys used: ## Consider these keys used:
ignore_unused: ignore_unused:
- "budgets.phase.*" - "budgets.phase.*"
- "budgets.investments.index.filter*"
- "budgets.investments.index.orders.*" - "budgets.investments.index.orders.*"
- "budgets.index.section_header.*" - "budgets.index.section_header.*"
- "budgets.ballots.show.amount_available.*" - "budgets.ballots.show.amount_available.*"

View File

@@ -69,9 +69,6 @@ en:
prev_phase: Previous phase prev_phase: Previous phase
current_phase: Current phase current_phase: Current phase
map: Budget investments' proposals located geographically map: Budget investments' proposals located geographically
investment_proyects: List of all investment projects
unfeasible_investment_proyects: List of all unfeasible investment projects
not_selected_investment_proyects: List of all investment projects not selected for balloting
finished_budgets: Finished participatory budgets finished_budgets: Finished participatory budgets
see_results: See results see_results: See results
section_footer: section_footer:
@@ -120,6 +117,13 @@ en:
verified_only: "To create a new budget investment %{verify}." verified_only: "To create a new budget investment %{verify}."
create: "Create a budget investment" create: "Create a budget investment"
not_logged_in: "To create a new budget investment you must %{sign_in} or %{sign_up}." not_logged_in: "To create a new budget investment you must %{sign_in} or %{sign_up}."
filter: "Filtering projects by"
filters:
not_unfeasible: "Not unfeasible"
selected: "Selected"
unfeasible: "Unfeasible"
unselected: "Unselected"
winners: "Winners"
orders: orders:
random: random random: random
confidence_score: highest rated confidence_score: highest rated

View File

@@ -69,9 +69,6 @@ es:
prev_phase: Fase anterior prev_phase: Fase anterior
current_phase: Fase actual current_phase: Fase actual
map: Proyectos localizables geográficamente map: Proyectos localizables geográficamente
investment_proyects: Ver lista completa de proyectos de gasto
unfeasible_investment_proyects: Ver lista de proyectos de gasto inviables
not_selected_investment_proyects: Ver lista de proyectos de gasto no seleccionados para la votación final
finished_budgets: Presupuestos participativos terminados finished_budgets: Presupuestos participativos terminados
see_results: Ver resultados see_results: Ver resultados
section_footer: section_footer:
@@ -120,6 +117,13 @@ es:
verified_only: "Para crear un nuevo proyecto de gasto %{verify}." verified_only: "Para crear un nuevo proyecto de gasto %{verify}."
create: "Crear proyecto de gasto" create: "Crear proyecto de gasto"
not_logged_in: "Para crear un nuevo proyecto de gasto debes %{sign_in} o %{sign_up}." not_logged_in: "Para crear un nuevo proyecto de gasto debes %{sign_in} o %{sign_up}."
filter: "Filtrando proyectos"
filters:
not_unfeasible: "No inviables"
selected: "Seleccionados"
unfeasible: "Inviables"
unselected: "No seleccionados"
winners: "Ganadores"
orders: orders:
random: Aleatorios random: Aleatorios
confidence_score: Mejor valorados confidence_score: Mejor valorados

View File

@@ -0,0 +1,15 @@
require "rails_helper"
describe Shared::FilterSelectorComponent, type: :component do
it "renders a form with a select" do
component = Shared::FilterSelectorComponent.new(i18n_namespace: "budgets.investments.index")
allow(component).to receive(:url_for).and_return("/")
allow(component).to receive(:valid_filters).and_return(["unfeasible", "winners"])
allow(component).to receive(:current_filter).and_return(nil)
render_inline component
expect(page).to have_select "Filtering projects by"
expect(page).to have_selector "form[method='get'].filter-selector select"
end
end

View File

@@ -139,53 +139,10 @@ describe "Budgets" do
expect(page).not_to have_link "#{heading.name} €1,000,000" expect(page).not_to have_link "#{heading.name} €1,000,000"
expect(page).to have_content "#{heading.name} €1,000,000" expect(page).to have_content "#{heading.name} €1,000,000"
expect(page).to have_link "List of all investment projects",
href: budget_path(budget)
expect(page).to have_link "List of all unfeasible investment projects",
href: budget_path(budget, filter: "unfeasible")
expect(page).to have_link "List of all investment projects not selected for balloting",
href: budget_path(budget, filter: "unselected")
expect(page).to have_css("div.map") expect(page).to have_css("div.map")
end end
end end
scenario "Show investment links only on balloting or later" do
budget = create(:budget)
create(:budget_heading, budget: budget)
allowed_phase_list.each do |phase|
budget.update!(phase: phase)
visit budgets_path
expect(page).to have_content(I18n.t("budgets.index.investment_proyects"))
expect(page).to have_content(I18n.t("budgets.index.unfeasible_investment_proyects"))
expect(page).to have_content(I18n.t("budgets.index.not_selected_investment_proyects"))
end
end
scenario "Not show investment links earlier of balloting " do
budget = create(:budget)
create(:budget_heading, budget: budget)
phases_without_links = ["informing"]
not_allowed_phase_list = Budget::Phase::PHASE_KINDS -
phases_without_links -
allowed_phase_list
not_allowed_phase_list.each do |phase|
budget.update!(phase: phase)
visit budgets_path
expect(page).not_to have_content(I18n.t("budgets.index.investment_proyects"))
expect(page).to have_content(I18n.t("budgets.index.unfeasible_investment_proyects"))
expect(page).not_to have_content(I18n.t("budgets.index.not_selected_investment_proyects"))
end
end
scenario "No budgets" do scenario "No budgets" do
Budget.destroy_all Budget.destroy_all

View File

@@ -151,6 +151,51 @@ describe "Budget Investments" do
end end
end end
scenario "Index filter by status", :js do
budget.update!(phase: "finished")
create(:budget_investment, :feasible, heading: heading, title: "Feasible investment")
create(:budget_investment, :unfeasible, heading: heading, title: "Unfeasible investment")
create(:budget_investment, :unselected, heading: heading, title: "Unselected investment")
create(:budget_investment, :selected, heading: heading, title: "Selected investment")
create(:budget_investment, :winner, heading: heading, title: "Winner investment")
visit budget_investments_path(budget, heading_id: heading.id)
expect(page).to have_select "Filtering projects by",
options: ["Not unfeasible", "Unfeasible", "Unselected", "Selected", "Winners"]
select "Unfeasible", from: "Filtering projects by"
expect(page).to have_css ".budget-investment", count: 1
expect(page).to have_content "Unfeasible investment"
select "Unselected", from: "Filtering projects by"
expect(page).to have_css ".budget-investment", count: 2
expect(page).to have_content "Unselected investment"
expect(page).to have_content "Feasible investment"
select "Selected", from: "Filtering projects by"
expect(page).to have_css ".budget-investment", count: 2
expect(page).to have_content "Selected investment"
expect(page).to have_content "Winner investment"
select "Winners", from: "Filtering projects by"
expect(page).to have_css ".budget-investment", count: 1
expect(page).to have_content "Winner investment"
select "Not unfeasible", from: "Filtering projects by"
expect(page).to have_css ".budget-investment", count: 4
expect(page).to have_content "Selected investment"
expect(page).to have_content "Unselected investment"
expect(page).to have_content "Feasible investment"
expect(page).to have_content "Winner investment"
end
context("Search") do context("Search") do
scenario "Search by text" do scenario "Search by text" do
investment1 = create(:budget_investment, heading: heading, title: "Get Schwifty") investment1 = create(:budget_investment, heading: heading, title: "Get Schwifty")