diff --git a/app/assets/stylesheets/participation.scss b/app/assets/stylesheets/participation.scss index 1cc6e87c9..cafbb52be 100644 --- a/app/assets/stylesheets/participation.scss +++ b/app/assets/stylesheets/participation.scss @@ -887,6 +887,13 @@ } } +.legislation-proposals { + + .votes { + min-height: $line-height * 8; + } +} + .proposal-show .votes, .debate-show .votes { border: 0; diff --git a/app/controllers/admin/legislation/proposals_controller.rb b/app/controllers/admin/legislation/proposals_controller.rb index 7f4441bce..44a1dd192 100644 --- a/app/controllers/admin/legislation/proposals_controller.rb +++ b/app/controllers/admin/legislation/proposals_controller.rb @@ -1,7 +1,16 @@ class Admin::Legislation::ProposalsController < Admin::Legislation::BaseController + + has_orders %w[id title supports], only: :index + load_and_authorize_resource :process, class: "Legislation::Process" load_and_authorize_resource :proposal, class: "Legislation::Proposal", through: :process def index + @proposals = @proposals.send("sort_by_#{@current_order}").page(params[:page]) + end + + def toggle_selection + @proposal.toggle :selected + @proposal.save! end end diff --git a/app/controllers/legislation/processes_controller.rb b/app/controllers/legislation/processes_controller.rb index f30b9d894..f05d2fdda 100644 --- a/app/controllers/legislation/processes_controller.rb +++ b/app/controllers/legislation/processes_controller.rb @@ -1,5 +1,7 @@ class Legislation::ProcessesController < Legislation::BaseController - has_filters %w{open next past}, only: :index + has_filters %w[open next past], only: :index + has_filters %w[random winners], only: :proposals + load_and_authorize_resource before_action :set_random_seed, only: :proposals @@ -91,7 +93,9 @@ class Legislation::ProcessesController < Legislation::BaseController @proposals = ::Legislation::Proposal.where(process: @process) @proposals = @proposals.search(params[:search]) if params[:search].present? - @proposals = @proposals.order('random()').page(params[:page]) + + @current_filter = "winners" if params[:filter].blank? && @proposals.winners.any? + @proposals = @proposals.send(@current_filter).page(params[:page]) if @process.proposals_phase.started? || (current_user && current_user.administrator?) legislation_proposal_votes(@proposals) diff --git a/app/helpers/legislation_helper.rb b/app/helpers/legislation_helper.rb index d8dc77f87..a737824ab 100644 --- a/app/helpers/legislation_helper.rb +++ b/app/helpers/legislation_helper.rb @@ -10,4 +10,20 @@ module LegislationHelper def new_legislation_proposal_link_text(process) t("proposals.index.start_proposal") end + + def link_to_toggle_legislation_proposal_selection(proposal) + if proposal.selected? + button_text = t("admin.legislation.proposals.index.selected") + html_class = 'button expanded' + else + button_text = t("admin.legislation.proposals.index.select") + html_class = 'button hollow expanded' + end + + link_to button_text, + toggle_selection_admin_legislation_process_proposal_path(proposal.process, proposal), + remote: true, + method: :patch, + class: html_class + end end diff --git a/app/models/legislation/proposal.rb b/app/models/legislation/proposal.rb index a345a5904..43a5cf1e8 100644 --- a/app/models/legislation/proposal.rb +++ b/app/models/legislation/proposal.rb @@ -45,9 +45,15 @@ class Legislation::Proposal < ActiveRecord::Base scope :sort_by_confidence_score, -> { reorder(confidence_score: :desc) } scope :sort_by_created_at, -> { reorder(created_at: :desc) } scope :sort_by_most_commented, -> { reorder(comments_count: :desc) } + scope :sort_by_title, -> { reorder(title: :asc) } + scope :sort_by_id, -> { reorder(id: :asc) } + scope :sort_by_supports, -> { reorder(cached_votes_up: :desc) } scope :sort_by_random, -> { reorder("RANDOM()") } scope :sort_by_flags, -> { order(flags_count: :desc, updated_at: :desc) } scope :last_week, -> { where("proposals.created_at >= ?", 7.days.ago)} + scope :selected, -> { where(selected: true) } + scope :random, -> { sort_by_random } + scope :winners, -> { selected.sort_by_confidence_score } def to_param "#{id}-#{title}".parameterize diff --git a/app/views/admin/legislation/proposals/_proposals.html.erb b/app/views/admin/legislation/proposals/_proposals.html.erb new file mode 100644 index 000000000..2f75b4223 --- /dev/null +++ b/app/views/admin/legislation/proposals/_proposals.html.erb @@ -0,0 +1,29 @@ +<% if proposals.any? %> +

<%= page_entries_info proposals %>

+ + <%= render 'shared/wide_order_selector', i18n_namespace: "admin.legislation.processes.proposals" %> + + + + + + + + + + + + + <% proposals.each do |proposal| %> + + + + + + + <% end %> + +
<%= t("admin.legislation.proposals.index.id") %><%= t("admin.legislation.proposals.index.title") %><%= t("admin.legislation.proposals.index.supports") %><%= t("admin.legislation.proposals.index.selected") %>
<%= proposal.id %><%= proposal.title %><%= proposal.cached_votes_up %><%= render "select_proposal", proposal: proposal %>
+ + <%= paginate proposals %> +<% end %> diff --git a/app/views/admin/legislation/proposals/_select_proposal.html.erb b/app/views/admin/legislation/proposals/_select_proposal.html.erb new file mode 100644 index 000000000..0d5eb845d --- /dev/null +++ b/app/views/admin/legislation/proposals/_select_proposal.html.erb @@ -0,0 +1 @@ +<%= link_to_toggle_legislation_proposal_selection(proposal) %> diff --git a/app/views/admin/legislation/proposals/index.html.erb b/app/views/admin/legislation/proposals/index.html.erb index aae66e008..d8dd407a5 100644 --- a/app/views/admin/legislation/proposals/index.html.erb +++ b/app/views/admin/legislation/proposals/index.html.erb @@ -11,4 +11,5 @@ <%= render 'admin/legislation/processes/subnav', process: @process, active: 'proposals' %> <%= render 'form' %> + <%= render 'proposals', proposals: @proposals %> diff --git a/app/views/admin/legislation/proposals/toggle_selection.js.erb b/app/views/admin/legislation/proposals/toggle_selection.js.erb new file mode 100644 index 000000000..093afa904 --- /dev/null +++ b/app/views/admin/legislation/proposals/toggle_selection.js.erb @@ -0,0 +1 @@ +$("#<%= dom_id(@proposal) %> .select").html('<%= j render("select_proposal", proposal: @proposal) %>'); diff --git a/app/views/legislation/processes/_proposals_content.html.erb b/app/views/legislation/processes/_proposals_content.html.erb index 63a7ac926..714ed4305 100644 --- a/app/views/legislation/processes/_proposals_content.html.erb +++ b/app/views/legislation/processes/_proposals_content.html.erb @@ -3,6 +3,7 @@
+ <%= render 'shared/filter_subnav', i18n_namespace: "legislation.processes.proposals" %> <% if proposals.empty? %>

<%= t("legislation.processes.proposals.empty_proposals") %>

diff --git a/app/views/legislation/proposals/_votes.html.erb b/app/views/legislation/proposals/_votes.html.erb index 8452bed9f..d88cf96d2 100644 --- a/app/views/legislation/proposals/_votes.html.erb +++ b/app/views/legislation/proposals/_votes.html.erb @@ -39,8 +39,6 @@
<% end %>
- <% else %> -

<%= t("legislation.proposals.closed") %>

<% end %> diff --git a/app/views/shared/_wide_order_selector.html.erb b/app/views/shared/_wide_order_selector.html.erb index d5b1405eb..0594f4301 100644 --- a/app/views/shared/_wide_order_selector.html.erb +++ b/app/views/shared/_wide_order_selector.html.erb @@ -2,6 +2,7 @@ # # i18n_namespace: for example "moderation.debates.index" %> + <% if @valid_orders.present? && @valid_orders.count > 1 %>
diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index b673c2110..ba9a522b8 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -139,6 +139,8 @@ ignore_unused: - 'admin.activity.show.filter*' - 'admin.legislation.processes.index.filter*' - 'admin.legislation.processes.*.submit_button' + - 'admin.legislation.processes.proposals.orders.*' + - 'admin.legislation.processes.proposals.select_order' - 'admin.legislation.draft_versions.*.submit_button' - 'admin.legislation.questions.*.submit_button' - 'admin.comments.index.hidden_*' @@ -176,6 +178,7 @@ ignore_unused: - 'notifications.notification.action.*' - 'legislation.processes.index.filter*' - 'legislation.processes.index.section_header.*' + - 'legislation.processes.proposals.filters.*' - 'helpers.page_entries_info.*' # kaminari - 'views.pagination.*' # kaminari - 'shared.suggest.*' diff --git a/config/locales/en/activerecord.yml b/config/locales/en/activerecord.yml index d3d19f1ed..84dee7a77 100644 --- a/config/locales/en/activerecord.yml +++ b/config/locales/en/activerecord.yml @@ -76,6 +76,9 @@ en: legislation/process: one: "Process" other: "Processes" + legislation/proposal: + one: "Proposal" + other: "Proposals" legislation/draft_versions: one: "Draft version" other: "Draft versions" diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index 261aca613..c119b2cff 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -394,6 +394,12 @@ en: back: Back title: Create new collaborative legislation process submit_button: Create process + proposals: + select_order: Sort by + orders: + id: Id + title: Title + supports: Supports process: title: Process comments: Comments @@ -411,6 +417,11 @@ en: index: title: Proposals back: Back + id: Id + title: Title + supports: Supports + select: Select + selected: Selected form: custom_categories: Categories custom_categories_description: Categories that users can select creating the proposal. diff --git a/config/locales/en/legislation.yml b/config/locales/en/legislation.yml index 479517a9d..bd3ed87c1 100644 --- a/config/locales/en/legislation.yml +++ b/config/locales/en/legislation.yml @@ -52,6 +52,9 @@ en: more_info: More information and context proposals: empty_proposals: There are no proposals + filters: + random: Random + winners: Selected debate: empty_questions: There aren't any questions participate: Participate in the debate @@ -120,4 +123,3 @@ en: form: tags_label: "Categories" not_verified: "For vote proposals %{verify_account}." - closed: "This process has been closed and can not receive votes." \ No newline at end of file diff --git a/config/locales/es/activerecord.yml b/config/locales/es/activerecord.yml index 52bb51e9f..2af5c034f 100644 --- a/config/locales/es/activerecord.yml +++ b/config/locales/es/activerecord.yml @@ -76,6 +76,9 @@ es: legislation/process: one: "Proceso" other: "Procesos" + legislation/proposal: + one: "Propuesta" + other: "Propuestas" legislation/draft_versions: one: "Versión borrador" other: "Versiones borrador" diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 2085443ef..3f0bade4f 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -395,6 +395,12 @@ es: back: Volver title: Crear nuevo proceso de legislación colaborativa submit_button: Crear proceso + proposals: + select_order: Ordenar por + orders: + id: Id + title: Título + supports: Apoyos process: title: Proceso comments: Comentarios @@ -412,6 +418,11 @@ es: index: title: Propuestas back: Volver + id: Id + title: Título + supports: Apoyos + select: Seleccionar + selected: Seleccionado form: custom_categories: Categorías custom_categories_description: Categorías que el usuario puede seleccionar al crear la propuesta. diff --git a/config/locales/es/legislation.yml b/config/locales/es/legislation.yml index cb7d9785d..03f4724fd 100644 --- a/config/locales/es/legislation.yml +++ b/config/locales/es/legislation.yml @@ -52,6 +52,9 @@ es: more_info: Más información y contexto proposals: empty_proposals: No hay propuestas + filters: + random: Aleatorias + winners: Seleccionadas debate: empty_questions: No hay preguntas participate: Realiza tus aportaciones al debate previo participando en los siguientes temas. @@ -120,4 +123,3 @@ es: form: tags_label: "Categorías" not_verified: "Para votar propuestas %{verify_account}." - closed: "Este proceso se ha cerrado y ya no puede recoger votos." diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 335d389d0..bd296658d 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -194,7 +194,9 @@ namespace :admin do namespace :legislation do resources :processes do resources :questions - resources :proposals + resources :proposals do + member { patch :toggle_selection } + end resources :draft_versions end end diff --git a/db/dev_seeds/legislation_proposals.rb b/db/dev_seeds/legislation_proposals.rb index ec6427395..e309dd28c 100644 --- a/db/dev_seeds/legislation_proposals.rb +++ b/db/dev_seeds/legislation_proposals.rb @@ -6,6 +6,7 @@ section "Creating legislation proposals" do summary: Faker::Lorem.paragraph, author: User.all.sample, process: Legislation::Process.all.sample, - terms_of_service: '1') + terms_of_service: '1', + selected: rand <= 1.0 / 3) end end diff --git a/db/migrate/20180807104331_add_selected_to_legislation_proposals.rb b/db/migrate/20180807104331_add_selected_to_legislation_proposals.rb new file mode 100644 index 000000000..6696c2796 --- /dev/null +++ b/db/migrate/20180807104331_add_selected_to_legislation_proposals.rb @@ -0,0 +1,5 @@ +class AddSelectedToLegislationProposals < ActiveRecord::Migration + def change + add_column :legislation_proposals, :selected, :boolean + end +end diff --git a/db/schema.rb b/db/schema.rb index b4e58d0e7..ea0b1bbbd 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -687,6 +687,7 @@ ActiveRecord::Schema.define(version: 20180924071722) do t.datetime "updated_at", null: false t.integer "cached_votes_total", default: 0 t.integer "cached_votes_down", default: 0 + t.boolean "selected" end add_index "legislation_proposals", ["legislation_process_id"], name: "index_legislation_proposals_on_legislation_process_id", using: :btree diff --git a/spec/features/admin/legislation/proposals_spec.rb b/spec/features/admin/legislation/proposals_spec.rb new file mode 100644 index 000000000..bd8caf0a3 --- /dev/null +++ b/spec/features/admin/legislation/proposals_spec.rb @@ -0,0 +1,85 @@ +require 'rails_helper' + +feature 'Admin legislation processes' do + + background do + admin = create(:administrator) + login_as(admin.user) + end + + context "Index" do + + scenario 'Displaying legislation proposals' do + proposal = create(:legislation_proposal, cached_votes_up: 10) + + visit admin_legislation_process_proposals_path(proposal.legislation_process_id) + + within "#legislation_proposal_#{proposal.id}" do + expect(page).to have_content(proposal.title) + expect(page).to have_content(proposal.id) + expect(page).to have_content(proposal.cached_votes_up) + expect(page).to have_content('Select') + end + end + + scenario 'Selecting legislation proposals', :js do + proposal = create(:legislation_proposal, cached_votes_up: 10) + + visit admin_legislation_process_proposals_path(proposal.legislation_process_id) + click_link 'Select' + + within "#legislation_proposal_#{proposal.id}" do + expect(page).to have_content('Selected') + end + end + + scenario 'Sorting legislation proposals by title', js: true do + process = create(:legislation_process) + create(:legislation_proposal, title: 'bbbb', legislation_process_id: process.id) + create(:legislation_proposal, title: 'aaaa', legislation_process_id: process.id) + create(:legislation_proposal, title: 'cccc', legislation_process_id: process.id) + + visit admin_legislation_process_proposals_path(process.id) + select "Title", from: "order-selector-participation" + + within('#legislation_proposals_list') do + within all('.legislation_proposal')[0] { expect(page).to have_content('aaaa') } + within all('.legislation_proposal')[1] { expect(page).to have_content('bbbb') } + within all('.legislation_proposal')[2] { expect(page).to have_content('cccc') } + end + end + + scenario 'Sorting legislation proposals by supports', js: true do + process = create(:legislation_process) + create(:legislation_proposal, cached_votes_up: 10, legislation_process_id: process.id) + create(:legislation_proposal, cached_votes_up: 30, legislation_process_id: process.id) + create(:legislation_proposal, cached_votes_up: 20, legislation_process_id: process.id) + + visit admin_legislation_process_proposals_path(process.id) + select "Supports", from: "order-selector-participation" + + within('#legislation_proposals_list') do + within all('.legislation_proposal')[0] { expect(page).to have_content('30') } + within all('.legislation_proposal')[1] { expect(page).to have_content('20') } + within all('.legislation_proposal')[2] { expect(page).to have_content('10') } + end + end + + scenario 'Sorting legislation proposals by Id', js: true do + process = create(:legislation_process) + proposal1 = create(:legislation_proposal, title: 'bbbb', legislation_process_id: process.id) + proposal2 = create(:legislation_proposal, title: 'aaaa', legislation_process_id: process.id) + proposal3 = create(:legislation_proposal, title: 'cccc', legislation_process_id: process.id) + + visit admin_legislation_process_proposals_path(process.id, order: :title) + select "Id", from: "order-selector-participation" + + within('#legislation_proposals_list') do + within all('.legislation_proposal')[0] { expect(page).to have_content(proposal1.id) } + within all('.legislation_proposal')[1] { expect(page).to have_content(proposal2.id) } + within all('.legislation_proposal')[2] { expect(page).to have_content(proposal3.id) } + end + end + end + +end diff --git a/spec/features/legislation/proposals_spec.rb b/spec/features/legislation/proposals_spec.rb index 0d6784a2f..17a7c607e 100644 --- a/spec/features/legislation/proposals_spec.rb +++ b/spec/features/legislation/proposals_spec.rb @@ -64,6 +64,49 @@ feature 'Legislation Proposals' do expect(legislation_proposals_order).to eq(first_page_proposals_order) end + context 'Selected filter' do + scenario 'apperars even if there are not any selected poposals' do + create(:legislation_proposal, legislation_process_id: process.id) + + visit legislation_process_proposals_path(process) + + expect(page).to have_content('Selected') + end + + scenario 'defaults to winners if there are selected proposals' do + create(:legislation_proposal, legislation_process_id: process.id) + create(:legislation_proposal, legislation_process_id: process.id, selected: true) + + visit legislation_process_proposals_path(process) + + expect(page).to have_link('Random') + expect(page).not_to have_link('Selected') + expect(page).to have_content('Selected') + end + + scenario 'defaults to random if the current process does not have selected proposals' do + create(:legislation_proposal, legislation_process_id: process.id) + create(:legislation_proposal, selected: true) + + visit legislation_process_proposals_path(process) + + expect(page).to have_link('Selected') + expect(page).not_to have_link('Random') + expect(page).to have_content('Random') + end + + scenario 'filters correctly' do + proposal1 = create(:legislation_proposal, legislation_process_id: process.id) + proposal2 = create(:legislation_proposal, legislation_process_id: process.id, selected: true) + + visit legislation_process_proposals_path(process, filter: "random") + click_link 'Selected' + + expect(page).to have_css("div#legislation_proposal_#{proposal2.id}") + expect(page).not_to have_css("div#legislation_proposal_#{proposal1.id}") + end + end + def legislation_proposals_order all("[id^='legislation_proposal_']").collect { |e| e[:id] } end