diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index f66510049..774ea5c78 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -70,6 +70,7 @@ //= require advanced_search //= require registration_form //= require suggest +//= require filter_selector //= require forms //= require valuation_budget_investment_form //= require embed_video @@ -133,6 +134,7 @@ var initialize_modules = function() { App.RegistrationForm.initialize(); App.Suggest.initialize(); App.Forms.initialize(); + App.FilterSelector.initialize(); App.ValuationBudgetInvestmentForm.initialize(); App.EmbedVideo.initialize(); App.FixedBar.initialize(); diff --git a/app/assets/javascripts/filter_selector.js b/app/assets/javascripts/filter_selector.js new file mode 100644 index 000000000..5f35db45c --- /dev/null +++ b/app/assets/javascripts/filter_selector.js @@ -0,0 +1,8 @@ +(function() { + "use strict"; + App.FilterSelector = { + initialize: function() { + App.Forms.submitOnChange(".filter-selector select"); + } + }; +}).call(this); diff --git a/app/assets/javascripts/forms.js b/app/assets/javascripts/forms.js index 822b2391b..0a2dfe107 100644 --- a/app/assets/javascripts/forms.js +++ b/app/assets/javascripts/forms.js @@ -8,8 +8,8 @@ } }); }, - submitOnChange: function() { - $("body").on("change", ".js-submit-on-change", function() { + submitOnChange: function(selector) { + $("body").on("change", selector, function() { $(this).closest("form").submit(); return false; }); @@ -58,7 +58,7 @@ }, initialize: function() { App.Forms.disableEnter(); - App.Forms.submitOnChange(); + App.Forms.submitOnChange(".js-submit-on-change"); App.Forms.toggleLink(); App.Forms.synchronizeInputs(); App.Forms.hideOrShowFieldsAfterSelection(); diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 6ce3e2ede..e71bfaf26 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -12,6 +12,7 @@ @import "milestones"; @import "pages"; @import "dashboard"; +@import "filter_selector"; @import "legislation"; @import "legislation_process"; @import "legislation_process_form"; diff --git a/app/assets/stylesheets/filter_selector.scss b/app/assets/stylesheets/filter_selector.scss new file mode 100644 index 000000000..10de18b25 --- /dev/null +++ b/app/assets/stylesheets/filter_selector.scss @@ -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; + } +} diff --git a/app/components/shared/filter_selector_component.html.erb b/app/components/shared/filter_selector_component.html.erb new file mode 100644 index 000000000..f8cdd6dee --- /dev/null +++ b/app/components/shared/filter_selector_component.html.erb @@ -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 %> diff --git a/app/components/shared/filter_selector_component.rb b/app/components/shared/filter_selector_component.rb new file mode 100644 index 000000000..6ea316a62 --- /dev/null +++ b/app/components/shared/filter_selector_component.rb @@ -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 diff --git a/app/helpers/budgets_helper.rb b/app/helpers/budgets_helper.rb index d27fec415..fcf937fa0 100644 --- a/app/helpers/budgets_helper.rb +++ b/app/helpers/budgets_helper.rb @@ -1,8 +1,4 @@ module BudgetsHelper - def show_links_to_budget_investments(budget) - ["balloting", "reviewing_ballots", "finished"].include? budget.phase - end - def budget_voting_styles_select_options Budget::VOTING_STYLES.map do |style| [Budget.human_attribute_name("voting_style_#{style}"), style] diff --git a/app/views/budgets/index.html.erb b/app/views/budgets/index.html.erb index 73f53bbd9..bdd3d3504 100644 --- a/app/views/budgets/index.html.erb +++ b/app/views/budgets/index.html.erb @@ -55,29 +55,6 @@

<%= t("budgets.index.map") %>

<%= render_map(nil, "budgets", false, nil, @budgets_coordinates) %> - - <% end %> diff --git a/app/views/budgets/investments/index.html.erb b/app/views/budgets/investments/index.html.erb index c1cca7353..b8013cf14 100644 --- a/app/views/budgets/investments/index.html.erb +++ b/app/views/budgets/investments/index.html.erb @@ -70,6 +70,8 @@ <%= render("shared/order_links", i18n_namespace: "budgets.investments.index") %> <% end %> + <%= render Shared::FilterSelectorComponent.new(i18n_namespace: "budgets.investments.index") %> + <% if investments_default_view? %> <% @investments.each do |investment| %> diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index dd76cacec..71933acf9 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -127,6 +127,7 @@ ignore_missing: ## Consider these keys used: ignore_unused: - "budgets.phase.*" + - "budgets.investments.index.filter*" - "budgets.investments.index.orders.*" - "budgets.index.section_header.*" - "budgets.ballots.show.amount_available.*" diff --git a/config/locales/en/budgets.yml b/config/locales/en/budgets.yml index 97468dd92..7931cc773 100644 --- a/config/locales/en/budgets.yml +++ b/config/locales/en/budgets.yml @@ -69,9 +69,6 @@ en: prev_phase: Previous phase current_phase: Current phase 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 see_results: See results section_footer: @@ -120,6 +117,13 @@ en: verified_only: "To create a new budget investment %{verify}." create: "Create a budget investment" 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: random: random confidence_score: highest rated diff --git a/config/locales/es/budgets.yml b/config/locales/es/budgets.yml index 76a71eb2a..98eda05f9 100644 --- a/config/locales/es/budgets.yml +++ b/config/locales/es/budgets.yml @@ -69,9 +69,6 @@ es: prev_phase: Fase anterior current_phase: Fase actual 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 see_results: Ver resultados section_footer: @@ -120,6 +117,13 @@ es: verified_only: "Para crear un nuevo proyecto de gasto %{verify}." create: "Crear proyecto de gasto" 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: random: Aleatorios confidence_score: Mejor valorados diff --git a/spec/components/shared/filter_selector_component_spec.rb b/spec/components/shared/filter_selector_component_spec.rb new file mode 100644 index 000000000..6943a1a9e --- /dev/null +++ b/spec/components/shared/filter_selector_component_spec.rb @@ -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 diff --git a/spec/system/budgets/budgets_spec.rb b/spec/system/budgets/budgets_spec.rb index f3e0ba3a2..022ad0693 100644 --- a/spec/system/budgets/budgets_spec.rb +++ b/spec/system/budgets/budgets_spec.rb @@ -139,53 +139,10 @@ describe "Budgets" do 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_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") 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 Budget.destroy_all diff --git a/spec/system/budgets/investments_spec.rb b/spec/system/budgets/investments_spec.rb index 73555d56a..a93c5a276 100644 --- a/spec/system/budgets/investments_spec.rb +++ b/spec/system/budgets/investments_spec.rb @@ -151,6 +151,51 @@ describe "Budget Investments" do 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 scenario "Search by text" do investment1 = create(:budget_investment, heading: heading, title: "Get Schwifty")