Move budget ballot actions to admin budget page
The buttons to create polls associated with a budget were too prominent, appearing on the table as if they were as used as the link to manage investments. Most CONSUL installations don't use physical booths, and would probably wonder what that button is about. We're moving it to a more discrete place, at the bottom of the budget page. This way we can also split the action in two: on budgets not having a poll, we display the button in a not-so-accessible position (at the bottom of the page), since this button will only be used once per budget at most. Once the poll has been created, it means this feature is going to be used, so we display a link to manage ballots more prominently at the top of the page. If the budget has finished the final voting stage without creating a poll, we don't show either the link or the button because this feature can no longer be used. We're also adding some texts indicating what this feature is about, since it's probably one of the least understood features in CONSUL (probably because the interface is very confusing... but that's a different story). Since now from the budget page we can access every feature related to the budget, we can remove the "preview" action from the budgets index table, since this feature isn't that useful for budgets once they're published. Now the budgets table doesn't take as much space as it used to, although it's still too wide to be handled properly on devices with a small screen.
This commit is contained in:
@@ -42,6 +42,10 @@
|
|||||||
@include hollow-button;
|
@include hollow-button;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ballots-link {
|
||||||
|
@include regular-button;
|
||||||
|
}
|
||||||
|
|
||||||
.destroy-link {
|
.destroy-link {
|
||||||
@include regular-button($alert-color);
|
@include regular-button($alert-color);
|
||||||
|
|
||||||
@@ -56,6 +60,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.calculate-winners-link,
|
.calculate-winners-link,
|
||||||
|
.ballots-link,
|
||||||
.destroy-link {
|
.destroy-link {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,11 @@
|
|||||||
@include icon-on-top;
|
@include icon-on-top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ballots-link {
|
||||||
|
@include has-fa-icon(archive, solid);
|
||||||
|
color: $link;
|
||||||
|
}
|
||||||
|
|
||||||
.results-link {
|
.results-link {
|
||||||
@include has-fa-icon(poll, solid);
|
@include has-fa-icon(poll, solid);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1618,8 +1618,7 @@ $font-awesome-icons: (
|
|||||||
color: $color-success;
|
color: $color-success;
|
||||||
}
|
}
|
||||||
|
|
||||||
.manage-link,
|
.manage-link {
|
||||||
.ballots-link {
|
|
||||||
@include has-fa-icon(archive, solid);
|
@include has-fa-icon(archive, solid);
|
||||||
color: $link;
|
color: $link;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ class Admin::Budgets::ActionsComponent < ApplicationComponent
|
|||||||
hint: winners_hint,
|
hint: winners_hint,
|
||||||
html: winners_action
|
html: winners_action
|
||||||
},
|
},
|
||||||
|
ballots: {
|
||||||
|
hint: t("admin.budgets.actions.descriptions.ballots"),
|
||||||
|
html: ballots_action
|
||||||
|
},
|
||||||
destroy: {
|
destroy: {
|
||||||
hint: destroy_hint,
|
hint: destroy_hint,
|
||||||
html: destroy_action
|
html: destroy_action
|
||||||
@@ -55,6 +59,27 @@ class Admin::Budgets::ActionsComponent < ApplicationComponent
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def ballots_action
|
||||||
|
if budget.published? && !budget.balloting_finished? && !budget.poll.present?
|
||||||
|
action(:ballots,
|
||||||
|
text: t("admin.budgets.actions.ballots"),
|
||||||
|
path: create_budget_poll_path,
|
||||||
|
method: :post,
|
||||||
|
confirm: t("admin.budgets.actions.confirm.ballots"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_budget_poll_path
|
||||||
|
balloting_phase = budget.phases.find_by(kind: "balloting")
|
||||||
|
|
||||||
|
admin_polls_path(poll: {
|
||||||
|
name: budget.name,
|
||||||
|
budget_id: budget.id,
|
||||||
|
starts_at: balloting_phase.starts_at,
|
||||||
|
ends_at: balloting_phase.ends_at
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
def descriptor_id(action_name)
|
def descriptor_id(action_name)
|
||||||
"#{dom_id(budget, action_name)}_descriptor"
|
"#{dom_id(budget, action_name)}_descriptor"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,6 +5,12 @@
|
|||||||
path: admin_budget_budget_investments_path(budget)) %>
|
path: admin_budget_budget_investments_path(budget)) %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
<% if budget.poll.present? %>
|
||||||
|
<%= action(:ballots,
|
||||||
|
text: t("admin.budgets.index.admin_ballots"),
|
||||||
|
path: admin_poll_booth_assignments_path(budget.poll)) %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<% if can?(:read_results, budget) %>
|
<% if can?(:read_results, budget) %>
|
||||||
<%= action(:results, text: results_text, path: budget_results_path(budget)) %>
|
<%= action(:results, text: results_text, path: budget_results_path(budget)) %>
|
||||||
<% else %>
|
<% else %>
|
||||||
|
|||||||
@@ -2,18 +2,4 @@
|
|||||||
<%= actions.action(:investments,
|
<%= actions.action(:investments,
|
||||||
text: t("admin.budgets.index.budget_investments"),
|
text: t("admin.budgets.index.budget_investments"),
|
||||||
path: admin_budget_budget_investments_path(budget)) %>
|
path: admin_budget_budget_investments_path(budget)) %>
|
||||||
<% if budget.poll.present? %>
|
|
||||||
<%= actions.action(:ballots,
|
|
||||||
text: t("admin.budgets.index.admin_ballots"),
|
|
||||||
path: admin_poll_booth_assignments_path(budget.poll)) %>
|
|
||||||
<% else %>
|
|
||||||
<%= actions.action(:ballots,
|
|
||||||
text: t("admin.budgets.index.admin_ballots"),
|
|
||||||
path: create_budget_poll_path,
|
|
||||||
method: :post) %>
|
|
||||||
<% end %>
|
|
||||||
<%= actions.action(:preview,
|
|
||||||
text: t("admin.budgets.actions.preview"),
|
|
||||||
path: budget_path(budget),
|
|
||||||
target: "_blank") %>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -7,17 +7,6 @@ class Admin::Budgets::TableActionsComponent < ApplicationComponent
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def create_budget_poll_path
|
|
||||||
balloting_phase = budget.phases.find_by(kind: "balloting")
|
|
||||||
|
|
||||||
admin_polls_path(poll: {
|
|
||||||
name: budget.name,
|
|
||||||
budget_id: budget.id,
|
|
||||||
starts_at: balloting_phase.starts_at,
|
|
||||||
ends_at: balloting_phase.ends_at
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
def actions_component
|
def actions_component
|
||||||
Admin::TableActionsComponent.new(
|
Admin::TableActionsComponent.new(
|
||||||
budget,
|
budget,
|
||||||
|
|||||||
@@ -69,9 +69,12 @@ en:
|
|||||||
no_activity: There are no moderators activity.
|
no_activity: There are no moderators activity.
|
||||||
budgets:
|
budgets:
|
||||||
actions:
|
actions:
|
||||||
|
ballots: "Create booths"
|
||||||
confirm:
|
confirm:
|
||||||
|
ballots: "Are you sure? This action is only necessary if you're going to use physical booths during the final voting phase. Citizens will still be able to vote via web with or without physical booths."
|
||||||
destroy: "Are you sure? This will delete the budget and all its associated groups and headings. This action cannot be undone."
|
destroy: "Are you sure? This will delete the budget and all its associated groups and headings. This action cannot be undone."
|
||||||
descriptions:
|
descriptions:
|
||||||
|
ballots: "This will let you manage physical ballots. Only use this option if you're going to use physical booths to collect ballots during the final voting phase."
|
||||||
calculate_winners: 'After calculating the results, only administrators will be able to access them. They will be public when the project reaches the "%{phase}" phase <strong>if the option "Show results" is enabled</strong> when editing the budget.'
|
calculate_winners: 'After calculating the results, only administrators will be able to access them. They will be public when the project reaches the "%{phase}" phase <strong>if the option "Show results" is enabled</strong> when editing the budget.'
|
||||||
destroy: "This will delete the budget and all its associated groups and headers. This action cannot be undone."
|
destroy: "This will delete the budget and all its associated groups and headers. This action cannot be undone."
|
||||||
edit: "Edit budget"
|
edit: "Edit budget"
|
||||||
|
|||||||
@@ -69,9 +69,12 @@ es:
|
|||||||
no_activity: No hay actividad de moderadores.
|
no_activity: No hay actividad de moderadores.
|
||||||
budgets:
|
budgets:
|
||||||
actions:
|
actions:
|
||||||
|
ballots: "Crear urnas"
|
||||||
confirm:
|
confirm:
|
||||||
|
ballots: "¿Seguro? Esta acción solo es necesaria si vas a utilizar urnas físicas durante la votación final. Los ciudadanos podrán votar mediante la web con o sin urnas físicas."
|
||||||
destroy: "¿Seguro? Esta acción borrará el presupuesto y todos sus grupos y partidas. Esta acción no se puede deshacer."
|
destroy: "¿Seguro? Esta acción borrará el presupuesto y todos sus grupos y partidas. Esta acción no se puede deshacer."
|
||||||
descriptions:
|
descriptions:
|
||||||
|
ballots: "Esta acción te permitirá gestionar las urnas de votación. Usa esta opción solamente si vas a utilizar urnas físicas para recoger votos durante la votación final."
|
||||||
calculate_winners: 'Tras calcular los resultados, solamente los administradores tendrán acceso a los mismos. Se harán públicos cuando el presupuesto llegue a la fase "%{phase}" <strong>si se habilita la opción "Mostrar resultados"</strong> al editar el presupuesto.'
|
calculate_winners: 'Tras calcular los resultados, solamente los administradores tendrán acceso a los mismos. Se harán públicos cuando el presupuesto llegue a la fase "%{phase}" <strong>si se habilita la opción "Mostrar resultados"</strong> al editar el presupuesto.'
|
||||||
destroy: "Esta acción borrará el presupuesto y todos sus grupos y partidas. Esta acción no se puede deshacer."
|
destroy: "Esta acción borrará el presupuesto y todos sus grupos y partidas. Esta acción no se puede deshacer."
|
||||||
edit: "Editar presupuesto"
|
edit: "Editar presupuesto"
|
||||||
|
|||||||
34
spec/components/admin/budgets/actions_component_spec.rb
Normal file
34
spec/components/admin/budgets/actions_component_spec.rb
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
require "rails_helper"
|
||||||
|
|
||||||
|
describe Admin::Budgets::ActionsComponent, controller: Admin::BaseController do
|
||||||
|
before { sign_in(create(:administrator).user) }
|
||||||
|
|
||||||
|
let(:budget) { create(:budget) }
|
||||||
|
let(:component) { Admin::Budgets::ActionsComponent.new(budget) }
|
||||||
|
|
||||||
|
describe "Create booths button" do
|
||||||
|
it "is rendered for budgets without polls" do
|
||||||
|
render_inline component
|
||||||
|
|
||||||
|
expect(page.find("form[action*='polls'][method='post']")).to have_button "Create booths"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not rendered for finished budgets without polls" do
|
||||||
|
budget.update!(phase: "finished")
|
||||||
|
|
||||||
|
render_inline component
|
||||||
|
|
||||||
|
expect(page).not_to have_css "form[action*='polls']"
|
||||||
|
expect(page).not_to have_button "Create booths"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not rendered for budgets with polls" do
|
||||||
|
budget.poll = create(:poll, budget: budget)
|
||||||
|
|
||||||
|
render_inline component
|
||||||
|
|
||||||
|
expect(page).not_to have_css "form[action*='polls']"
|
||||||
|
expect(page).not_to have_button "Create booths"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -97,4 +97,24 @@ describe Admin::Budgets::LinksComponent, controller: Admin::BaseController do
|
|||||||
expect(page).not_to have_link "Investment projects"
|
expect(page).not_to have_link "Investment projects"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "ballots link" do
|
||||||
|
let(:budget) { create(:budget) }
|
||||||
|
let(:component) { Admin::Budgets::LinksComponent.new(budget) }
|
||||||
|
|
||||||
|
it "is rendered for budgets with polls" do
|
||||||
|
budget.poll = create(:poll, budget: budget)
|
||||||
|
path = Rails.application.routes.url_helpers.admin_poll_booth_assignments_path(budget.poll)
|
||||||
|
|
||||||
|
render_inline component
|
||||||
|
|
||||||
|
expect(page).to have_link "Ballots", href: path
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not rendered for budgets without polls" do
|
||||||
|
render_inline component
|
||||||
|
|
||||||
|
expect(page).not_to have_link "Ballots"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -4,29 +4,13 @@ describe Admin::Budgets::TableActionsComponent, controller: Admin::BaseControlle
|
|||||||
let(:budget) { create(:budget) }
|
let(:budget) { create(:budget) }
|
||||||
let(:component) { Admin::Budgets::TableActionsComponent.new(budget) }
|
let(:component) { Admin::Budgets::TableActionsComponent.new(budget) }
|
||||||
|
|
||||||
it "renders actions to edit budget, manage investments and manage ballots" do
|
it "renders actions to edit budget and manage investments" do
|
||||||
render_inline component
|
render_inline component
|
||||||
|
|
||||||
expect(page).to have_link count: 3
|
expect(page).to have_link count: 2
|
||||||
expect(page).to have_link "Investment projects", href: /investments/
|
expect(page).to have_link "Investment projects", href: /investments/
|
||||||
expect(page).to have_link "Edit", href: /#{budget.id}\Z/
|
expect(page).to have_link "Edit", href: /#{budget.id}\Z/
|
||||||
expect(page).to have_link "Preview", href: /budgets/
|
|
||||||
|
|
||||||
expect(page).to have_button count: 1
|
expect(page).not_to have_button
|
||||||
expect(page).to have_button "Ballots"
|
|
||||||
end
|
|
||||||
|
|
||||||
it "renders button to create new poll for budgets without polls" do
|
|
||||||
render_inline component
|
|
||||||
|
|
||||||
expect(page).to have_css "form[action*='polls'][method='post']", exact_text: "Ballots"
|
|
||||||
end
|
|
||||||
|
|
||||||
it "renders link to manage ballots for budgets with polls" do
|
|
||||||
budget.poll = create(:poll, budget: budget)
|
|
||||||
|
|
||||||
render_inline component
|
|
||||||
|
|
||||||
expect(page).to have_link "Ballots", href: /booth_assignments/
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ describe "Admin Budgets", :admin do
|
|||||||
budget = create(:budget)
|
budget = create(:budget)
|
||||||
balloting_phase = budget.phases.balloting
|
balloting_phase = budget.phases.balloting
|
||||||
|
|
||||||
visit admin_budgets_path
|
visit admin_budget_path(budget)
|
||||||
|
|
||||||
click_button "Ballots"
|
accept_confirm { click_button "Create booths" }
|
||||||
|
|
||||||
expect(page).to have_current_path(/admin\/polls\/\d+/)
|
expect(page).to have_current_path(/admin\/polls\/\d+/)
|
||||||
expect(page).to have_content(budget.name)
|
expect(page).to have_content(budget.name)
|
||||||
@@ -17,28 +17,17 @@ describe "Admin Budgets", :admin do
|
|||||||
end
|
end
|
||||||
|
|
||||||
scenario "Create poll in current locale if the budget does not have a poll associated" do
|
scenario "Create poll in current locale if the budget does not have a poll associated" do
|
||||||
create(:budget,
|
budget = create(:budget,
|
||||||
name_en: "Budget for climate change",
|
name_en: "Budget for climate change",
|
||||||
name_fr: "Budget pour le changement climatique")
|
name_es: "Presupuesto por el cambio climático")
|
||||||
|
|
||||||
visit admin_budgets_path
|
visit admin_budget_path(budget)
|
||||||
select "Français", from: "Language:"
|
select "Español", from: "Language:"
|
||||||
|
|
||||||
click_button "Bulletins de l’admin"
|
accept_confirm { click_button "Crear urnas" }
|
||||||
|
|
||||||
expect(page).to have_current_path(/admin\/polls\/\d+/)
|
expect(page).to have_current_path(/admin\/polls\/\d+/)
|
||||||
expect(page).to have_content("Budget pour le changement climatique")
|
expect(page).to have_content "Presupuesto por el cambio climático"
|
||||||
end
|
|
||||||
|
|
||||||
scenario "Display link to poll if the budget has a poll associated" do
|
|
||||||
budget = create(:budget)
|
|
||||||
poll = create(:poll, budget: budget)
|
|
||||||
|
|
||||||
visit admin_budgets_path
|
|
||||||
|
|
||||||
within "#budget_#{budget.id}" do
|
|
||||||
expect(page).to have_link "Ballots", href: admin_poll_booth_assignments_path(poll)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user