Remove obsolete questions index in the admin area

We removed the link to this page in commit 83e8d6035 because poll
questions don't really make sense without a poll.

However, this page also contained information about successful
proposals, which might be interesting so administrators don't have to
navigate to the public area in order to find and create questions based
on successful proposals.

So we're keeping the part about successful proposals and linking it from
the proposals part of the admin area.

Note we're using translation keys like `successful_proposals_tab`, which
don't make sense anymore, for the successful proposals. We're doing so
because we've already got translations for these keys and, if we renamed
them, we'd lose the existing translations and our translators would have
to add them again.

Also note we're changing one poll question test a little bit so we
create the question from a successful proposal using the new page. There
are other tests checking how to create a question from the
admin/proposals#show action and other tests checking what happens when
accessing a successful proposal in the admin section, so we don't lose
any test coverage by changing an existing test instead of adding a new
one.

Finally, note that we've removing the `search` method in poll question
because we no longer use it. This currently makes the
`author_visible_name` database column useless; we aren't removing it
right now because we don't want to risk a possible data loss in a patch
release (we're about to release version 2.3.1), but we might remove it
in the future.
This commit is contained in:
Javi Martín
2025-02-23 14:51:48 +01:00
parent cf5863b29f
commit 2239b8fdca
23 changed files with 81 additions and 180 deletions

View File

@@ -1,11 +0,0 @@
.admin .poll-questions-filter {
$gap: 0.5em;
@include flex-with-gap($gap);
align-items: flex-end;
flex-wrap: wrap;
[type="submit"] {
@include regular-button;
margin-left: $gap;
}
}

View File

@@ -1,7 +0,0 @@
<%= form_tag "", method: :get, class: "poll-questions-filter" do %>
<div class="filter">
<%= label_tag :poll_id, t("admin.questions.index.filter_poll") %>
<%= select_tag "poll_id", poll_select_options, prompt: t("polls.all") %>
</div>
<%= submit_tag t("shared.filter") %>
<% end %>

View File

@@ -1,14 +0,0 @@
class Admin::Poll::Questions::FilterComponent < ApplicationComponent
attr_reader :polls
use_helpers :current_path_with_query_params
def initialize(polls)
@polls = polls
end
private
def poll_select_options
options_from_collection_for_select(polls, :id, :name, params[:poll_id])
end
end

View File

@@ -1,6 +1,8 @@
<% provide :main_class, "admin-proposals-index" %>
<%= header %>
<%= header do %>
<%= successful_proposals_link %>
<% end %>
<% if proposals.any? %>
<%= render Admin::SearchComponent.new(label: t("admin.shared.search.label.proposals")) %>

View File

@@ -9,4 +9,12 @@ class Admin::Proposals::IndexComponent < ApplicationComponent
def title
t("admin.proposals.index.title")
end
private
def successful_proposals_link
if Proposal.successful.any?
link_to t("admin.questions.index.successful_proposals_tab"), successful_admin_proposals_path
end
end
end

View File

@@ -1,3 +1,9 @@
<% provide :main_class, "admin-proposals-successful" %>
<%= back_link_to admin_proposals_path %>
<%= header %>
<table>
<thead>
<tr>
@@ -6,7 +12,7 @@
</tr>
</thead>
<tbody>
<% @proposals.each do |proposal| %>
<% proposals.each do |proposal| %>
<tr id="<%= dom_id(proposal) %>">
<td>
<%= proposal.title %>

View File

@@ -0,0 +1,12 @@
class Admin::Proposals::SuccessfulComponent < ApplicationComponent
include Header
attr_reader :proposals
def initialize(proposals)
@proposals = proposals
end
def title
t("admin.questions.index.successful_proposals_tab")
end
end

View File

@@ -4,14 +4,7 @@ class Admin::Poll::QuestionsController < Admin::Poll::BaseController
load_and_authorize_resource :poll
load_resource class: "Poll::Question"
authorize_resource except: [:new, :index]
def index
@polls = Poll.not_budget
@questions = @questions.search(search_params).page(params[:page]).order("created_at DESC")
@proposals = Proposal.successful.sort_by_confidence_score
end
authorize_resource except: :new
def new
proposal = Proposal.find(params[:proposal_id]) if params[:proposal_id].present?
@@ -61,8 +54,4 @@ class Admin::Poll::QuestionsController < Admin::Poll::BaseController
attributes = [:poll_id, :question, :proposal_id, votation_type_attributes: [:vote_type, :max_votes]]
[*attributes, translation_params(Poll::Question)]
end
def search_params
params.permit(:poll_id, :search)
end
end

View File

@@ -6,7 +6,11 @@ class Admin::ProposalsController < Admin::BaseController
has_orders %w[created_at]
before_action :load_proposal, except: :index
before_action :load_proposal, except: [:index, :successful]
def successful
@proposals = Proposal.successful.sort_by_confidence_score
end
def show
end

View File

@@ -1,6 +1,5 @@
class Poll::Question < ApplicationRecord
include Measurable
include Searchable
acts_as_paranoid column: :hidden_at
include ActsAsParanoidAliases
@@ -30,25 +29,9 @@ class Poll::Question < ApplicationRecord
delegate :multiple?, :vote_type, to: :votation_type, allow_nil: true
scope :by_poll_id, ->(poll_id) { where(poll_id: poll_id) }
scope :sort_for_list, -> { order(Arel.sql("poll_questions.proposal_id IS NULL"), :created_at) }
scope :for_render, -> { includes(:author, :proposal) }
def self.search(params)
results = all
results = results.by_poll_id(params[:poll_id]) if params[:poll_id].present?
results = results.pg_search(params[:search]) if params[:search].present?
results
end
def searchable_values
{ title => "A",
proposal&.title => "A",
author.username => "C",
author_visible_name => "C" }
end
def copy_attributes_from_proposal(proposal)
if proposal.present?
self.author = proposal.author

View File

@@ -1,12 +0,0 @@
<ul class="tabs" data-tabs id="questions-tabs">
<li class="tabs-title is-active">
<%= link_to "#tab-questions" do %>
<%= t("admin.questions.index.questions_tab") %>
<% end %>
</li>
<li class="tabs-title">
<%= link_to "#tab-successful-proposals" do %>
<%= t("admin.questions.index.successful_proposals_tab") %>
<% end %>
</li>
</ul>

View File

@@ -1,38 +0,0 @@
<%= render Admin::Poll::Questions::FilterComponent.new(@polls) %>
<% if @questions.count == 0 %>
<div class="callout primary margin-top">
<%= t("admin.questions.index.no_questions") %>
</div>
<% else %>
<table>
<thead>
<tr>
<th><%= t("admin.questions.index.table_question") %></th>
<th><%= t("admin.questions.index.table_poll") %></th>
<th><%= t("admin.actions.actions") %></th>
</tr>
</thead>
<tbody>
<% @questions.each do |question| %>
<tr id="<%= dom_id(question) %>">
<td><%= question.title %></td>
<td>
<% if question.poll.present? %>
<%= question.poll.name %>
<% else %>
<em><%= t("admin.questions.index.poll_not_assigned") %></em>
<% end %>
</td>
<td>
<%= render Admin::TableActionsComponent.new(question) do |actions| %>
<%= actions.action(:options, text: t("admin.polls.show.edit_answers")) %>
<% end %>
</td>
</tr>
<% end %>
</tbody>
</table>
<%= paginate @questions %>
<% end %>

View File

@@ -1,18 +0,0 @@
<h2 class="inline-block"><%= t("admin.questions.index.title") %></h2>
<%= link_to t("admin.questions.index.create"), new_admin_question_path,
class: "button float-right" %>
<%= render "search" %>
<div class="tabs-content" data-tabs-content="questions-tabs">
<%= render "filter_subnav" %>
<div class="tabs-panel is-active" id="tab-questions">
<%= render "questions" %>
</div>
<div class="tabs-panel" id="tab-successful-proposals">
<%= render "successful_proposals" %>
</div>
</div>

View File

@@ -0,0 +1 @@
<%= render Admin::Proposals::SuccessfulComponent.new(@proposals) %>

View File

@@ -1134,18 +1134,11 @@ en:
multiple_description: "Allows to choose multiple answers. It's possible to set the maximum number of answers."
questions:
index:
title: "Questions"
create: "Create question"
no_questions: "There are no questions."
filter_poll: Filter by Poll
select_poll: Select Poll
questions_tab: "Questions"
successful_proposals_tab: "Successful proposals"
create_question: "Create question"
table_proposal: "Proposal"
table_question: "Question"
table_poll: "Poll"
poll_not_assigned: "Poll not assigned"
edit:
title: "Edit Question"
form:

View File

@@ -569,7 +569,6 @@ en:
support: "You just have to click on the button that you will see below 'Support this proposal' and you can inform yourself about before promoting it. Only the proposals that achieve the maximum support will be carried out by the City Council, and I thought that you, I'm sure you help me achieve it!"
share: "And if you also do me the great favor of sharing my proposal with your friends, family and contacts, it would be perfect!"
polls:
all: "All"
dates: "From %{open_at} to %{closed_at}"
final_date: "Final recounts/Results"
index:

View File

@@ -1134,18 +1134,11 @@ es:
multiple_description: "Permite elegir más de una respuesta. Se puede elegir el número máximo de respuestas."
questions:
index:
title: "Preguntas de votaciones"
create: "Crear pregunta ciudadana"
no_questions: "No hay ninguna pregunta ciudadana."
filter_poll: Filtrar por votación
select_poll: Seleccionar votación
questions_tab: "Preguntas"
successful_proposals_tab: "Propuestas que han superado el umbral"
create_question: "Crear pregunta para votación"
table_proposal: "Propuesta"
table_question: "Pregunta"
table_poll: "Votación"
poll_not_assigned: "Votación no asignada"
edit:
title: "Editar pregunta ciudadana"
form:

View File

@@ -569,7 +569,6 @@ es:
support: "Tan sólo tienes que hacer clic en el botón que verás a continuación 'Apoyar esta propuesta' y directamente podrás informarte acerca de ella antes de impulsarla. Sólo las propuestas que consigan el máximo apoyo se llevarán a cabo por parte del Ayuntamiento, y he pensado que tú ¡seguro que me ayudas a lograrlo!"
share: "Y si además, me haces el gran favor de compartir mi propuesta con tus amigos, familiares y contactos ¡sería perfecto!"
polls:
all: "Todas"
dates: "Desde el %{open_at} hasta el %{closed_at}"
final_date: "Recuento final/Resultados"
index:

View File

@@ -45,6 +45,10 @@ namespace :admin do
resources :debates, only: [:index, :show]
resources :proposals, only: [:index, :show, :update] do
collection do
get :successful
end
member do
patch :select
patch :deselect
@@ -192,7 +196,7 @@ namespace :admin do
end
end
resources :questions, shallow: true do
resources :questions, except: :index, shallow: true do
resources :options, except: [:index, :show], controller: "questions/options", shallow: false
resources :options, only: [], controller: "questions/options" do
resources :images, controller: "questions/options/images"

View File

@@ -1,9 +0,0 @@
require "rails_helper"
describe Admin::Poll::Questions::FilterComponent do
it "renders a button to submit the form" do
render_inline Admin::Poll::Questions::FilterComponent.new([])
expect(page).to have_button "Filter"
end
end

View File

@@ -0,0 +1,35 @@
require "rails_helper"
describe Admin::Proposals::IndexComponent, controller: Admin::ProposalsController do
around do |example|
with_request_url(Rails.application.routes.url_helpers.admin_proposals_path) { example.run }
end
describe "#successful_proposals_link" do
it "is shown when there are successful proposals" do
create(:proposal, :successful)
render_inline Admin::Proposals::IndexComponent.new(Proposal.page(1))
expect(page).to have_link "Successful proposals"
end
it "is not shown when there aren't any successful proposals" do
create(:proposal)
render_inline Admin::Proposals::IndexComponent.new(Proposal.page(1))
expect(page).not_to have_link "Successful proposals"
end
it "is shown when there are successful proposals on a previous page" do
allow(Proposal).to receive(:default_per_page).and_return(1)
create(:proposal, :successful)
create(:proposal)
render_inline Admin::Proposals::IndexComponent.new(Proposal.order(:id).page(2))
expect(page).to have_link "Successful proposals"
end
end
end

View File

@@ -118,10 +118,9 @@ describe "Admin poll questions", :admin do
create(:poll, :future, name: "Proposals")
proposal = create(:proposal, :successful)
visit admin_proposal_path(proposal)
expect(page).to have_content("This proposal has reached the required supports")
click_link "Add this proposal to a poll to be voted"
visit admin_proposals_path
click_link "Successful proposals"
click_link "Create question"
expect(page).to have_current_path(new_admin_question_path, ignore_query: true)
expect(page).to have_field("Question", with: proposal.title)
@@ -130,11 +129,7 @@ describe "Admin poll questions", :admin do
click_button "Save"
expect(page).to have_content(proposal.title)
visit admin_questions_path
expect(page).to have_content(proposal.title)
expect(page).to have_content proposal.title
end
scenario "Update" do

View File

@@ -1,13 +0,0 @@
require "rails_helper"
describe "Poll Questions", :admin do
scenario "Do not display polls associated to a budget" do
create(:poll, name: "Citizen Proposal Poll")
create(:poll, :for_budget, name: "Participatory Budget Poll")
visit admin_questions_path
expect(page).to have_select("poll_id", text: "Citizen Proposal Poll")
expect(page).not_to have_select("poll_id", text: "Participatory Budget Poll")
end
end