Allow users to remove their support on investments
Note we don't cast negative votes when users remove their support. That way we provide compatibility for institutions who have implemented real negative votes (in case there are / will be any), and we also keep the database meaningful: it's not that users downvoted something; they simply removed their upvote. Co-Authored-By: Javi Martín <javim@elretirao.net> Co-Authored-By: Julian Nicolas Herrero <microweb10@gmail.com>
This commit is contained in:
@@ -5,19 +5,28 @@
|
||||
</span>
|
||||
|
||||
<div class="in-favor">
|
||||
<% if user_voted_for? %>
|
||||
<div class="supported callout success">
|
||||
<%= t("budgets.investments.votes.already_supported") %>
|
||||
</div>
|
||||
<% elsif investment.should_show_votes? %>
|
||||
<%= button_to t("budgets.investments.votes.support"), vote_path,
|
||||
class: "button button-support small expanded",
|
||||
title: t("budgets.investments.investment.support_title"),
|
||||
method: "post",
|
||||
remote: !display_support_alert?,
|
||||
data: ({ confirm: confirm_vote_message } if display_support_alert?),
|
||||
disabled: !current_user,
|
||||
"aria-label": support_aria_label %>
|
||||
<% if investment.should_show_votes? %>
|
||||
<% if user_voted_for? %>
|
||||
<div class="supported">
|
||||
<div class="callout success">
|
||||
<%= t("budgets.investments.votes.already_supported") %>
|
||||
</div>
|
||||
<%= button_to t("budgets.investments.votes.remove_support"), remove_support_path,
|
||||
class: "button button-remove-support expanded",
|
||||
method: "delete",
|
||||
remote: true,
|
||||
"aria-label": remove_support_aria_label %>
|
||||
</div>
|
||||
<% else %>
|
||||
<%= button_to t("budgets.investments.votes.support"), support_path,
|
||||
class: "button button-support expanded",
|
||||
title: t("budgets.investments.investment.support_title"),
|
||||
method: "post",
|
||||
remote: !display_support_alert?,
|
||||
data: ({ confirm: confirm_vote_message } if display_support_alert?),
|
||||
disabled: !current_user,
|
||||
"aria-label": support_aria_label %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ class Budgets::Investments::VotesComponent < ApplicationComponent
|
||||
@investment = investment
|
||||
end
|
||||
|
||||
def vote_path
|
||||
def support_path
|
||||
case namespace
|
||||
when "management"
|
||||
management_budget_investment_votes_path(investment.budget, investment)
|
||||
@@ -16,6 +16,17 @@ class Budgets::Investments::VotesComponent < ApplicationComponent
|
||||
end
|
||||
end
|
||||
|
||||
def remove_support_path
|
||||
vote = investment.votes_for.find_by!(voter: current_user)
|
||||
|
||||
case namespace
|
||||
when "management"
|
||||
management_budget_investment_vote_path(investment.budget, investment, vote)
|
||||
else
|
||||
budget_investment_vote_path(investment.budget, investment, vote)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def reason
|
||||
@@ -44,4 +55,8 @@ class Budgets::Investments::VotesComponent < ApplicationComponent
|
||||
def support_aria_label
|
||||
t("budgets.investments.votes.support_label", investment: investment.title)
|
||||
end
|
||||
|
||||
def remove_support_aria_label
|
||||
t("budgets.investments.votes.remove_support_label", investment: investment.title)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
</header>
|
||||
|
||||
<p><%= t("budgets.supports_info.next") %></p>
|
||||
<p><%= sanitize(t("budgets.supports_info.remember")) %></p>
|
||||
<p><%= t("budgets.supports_info.different") %></p>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ module Budgets
|
||||
class VotesController < ApplicationController
|
||||
load_and_authorize_resource :budget
|
||||
load_and_authorize_resource :investment, through: :budget, class: "Budget::Investment"
|
||||
load_and_authorize_resource through: :investment, through_association: :votes_for, only: :destroy
|
||||
|
||||
def create
|
||||
@investment.register_selection(current_user)
|
||||
@@ -13,7 +14,15 @@ module Budgets
|
||||
notice: t("flash.actions.create.support")
|
||||
end
|
||||
|
||||
format.js
|
||||
format.js { render :show }
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@investment.unliked_by(current_user)
|
||||
|
||||
respond_to do |format|
|
||||
format.js { render :show }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
class Management::Budgets::Investments::VotesController < Management::BaseController
|
||||
load_resource :budget, find_by: :slug_or_id
|
||||
load_resource :investment, through: :budget, class: "Budget::Investment"
|
||||
load_and_authorize_resource through: :investment, through_association: :votes_for, only: :destroy
|
||||
|
||||
def create
|
||||
@investment.register_selection(managed_user)
|
||||
@@ -11,7 +12,15 @@ class Management::Budgets::Investments::VotesController < Management::BaseContro
|
||||
notice: t("flash.actions.create.support")
|
||||
end
|
||||
|
||||
format.js
|
||||
format.js { render :show }
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@investment.unliked_by(managed_user)
|
||||
|
||||
respond_to do |format|
|
||||
format.js { render :show }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -99,7 +99,7 @@ module Abilities
|
||||
can :update, Budget::Investment, budget: { phase: "accepting" }, author_id: user.id
|
||||
can :suggest, Budget::Investment, budget: { phase: "accepting" }
|
||||
can :destroy, Budget::Investment, budget: { phase: ["accepting", "reviewing"] }, author_id: user.id
|
||||
can :create, ActsAsVotable::Vote,
|
||||
can [:create, :destroy], ActsAsVotable::Vote,
|
||||
voter_id: user.id,
|
||||
votable_type: "Budget::Investment",
|
||||
votable: { budget: { phase: "selecting" }}
|
||||
|
||||
@@ -172,6 +172,8 @@ en:
|
||||
confirm_group:
|
||||
one: "You can only support investments in %{count} district. If you continue you cannot change the election of your district. Are you sure?"
|
||||
other: "You can only support investments in %{count} districts. If you continue you cannot change the election of your district. Are you sure?"
|
||||
remove_support: "Remove your support"
|
||||
remove_support_label: "Remove your support to %{investment}"
|
||||
support: "Support"
|
||||
support_label: "Support %{investment}"
|
||||
investments_list:
|
||||
@@ -186,7 +188,6 @@ en:
|
||||
supports_info:
|
||||
different: "You may support on as many different projects as you would like."
|
||||
next: "Support the projects you would like to see move on to the next phase."
|
||||
remember: "<strong>Remember!</strong> You can only cast your support <strong>once</strong> for each project and each support is <strong>irreversible</strong>."
|
||||
scrolling: "Keep scrolling to see all ideas"
|
||||
share: "You can share the projects you have supported on through social media and attract more attention and support to them!"
|
||||
supported:
|
||||
|
||||
@@ -172,6 +172,8 @@ es:
|
||||
confirm_group:
|
||||
one: "Sólo puedes apoyar proyectos en %{count} distrito. Si sigues adelante no podrás cambiar la elección de este distrito. ¿Estás seguro/a?"
|
||||
other: "Sólo puedes apoyar proyectos en %{count} distritos. Si sigues adelante no podrás cambiar la elección de este distrito. ¿Estás seguro/a?"
|
||||
remove_support: "Eliminar apoyo"
|
||||
remove_support_label: "Eliminar tu apoyo a %{investment}"
|
||||
support: "Apoyar"
|
||||
support_label: "Apoyar %{investment}"
|
||||
investments_list:
|
||||
@@ -186,7 +188,6 @@ es:
|
||||
supports_info:
|
||||
different: "Puedes apoyar tantos proyectos diferentes como quieras."
|
||||
next: "Apoya proyectos que te gustaría ver en la siguiente fase."
|
||||
remember: "<strong>¡Recuerda!</strong> Solo puedes emitir tu apoyo <strong>una vez</strong> por cada proyecto y cada apoyo es <strong>irreversible</strong>."
|
||||
scrolling: "Sigue desplazándote para ver todas las ideas"
|
||||
share: "Puedes compartir los proyectos que has apoyado en las redes sociales y ¡atraer más atención y apoyos para ellos!"
|
||||
supported:
|
||||
|
||||
@@ -8,7 +8,7 @@ resources :budgets, only: [:show, :index] do
|
||||
|
||||
collection { get :suggest }
|
||||
|
||||
resources :votes, controller: "budgets/investments/votes", only: :create
|
||||
resources :votes, controller: "budgets/investments/votes", only: [:create, :destroy]
|
||||
end
|
||||
|
||||
resource :ballot, only: :show, controller: "budgets/ballots" do
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace :management do
|
||||
resources :investments, only: [:index, :new, :create, :show, :destroy], controller: "budgets/investments" do
|
||||
get :print, on: :collection
|
||||
|
||||
resources :votes, controller: "budgets/investments/votes", only: :create
|
||||
resources :votes, controller: "budgets/investments/votes", only: [:create, :destroy]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -23,8 +23,21 @@ describe Budgets::Investments::VotesComponent, type: :component do
|
||||
|
||||
render_inline component
|
||||
|
||||
expect(page).to have_button count: 1, disabled: :all
|
||||
expect(page).to have_button "Support", disabled: true
|
||||
end
|
||||
|
||||
it "shows the button to remove support when users have supported the investment" do
|
||||
user = create(:user)
|
||||
user.up_votes(investment)
|
||||
allow(controller).to receive(:current_user).and_return(user)
|
||||
|
||||
render_inline component
|
||||
|
||||
expect(page).to have_button count: 1, disabled: :all
|
||||
expect(page).to have_button "Remove your support"
|
||||
expect(page).to have_button "Remove your support to Renovate sidewalks in Main Street"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -251,6 +251,9 @@ describe Abilities::Common do
|
||||
it { should be_able_to(:create, user.votes.build(votable: investment_in_selecting_budget)) }
|
||||
it { should_not be_able_to(:create, user.votes.build(votable: investment_in_accepting_budget)) }
|
||||
it { should_not be_able_to(:create, user.votes.build(votable: investment_in_balloting_budget)) }
|
||||
it { should be_able_to(:destroy, create(:vote, voter: user, votable: investment_in_selecting_budget)) }
|
||||
it { should_not be_able_to(:destroy, create(:vote, voter: user, votable: investment_in_accepting_budget)) }
|
||||
it { should_not be_able_to(:destroy, create(:vote, voter: user, votable: investment_in_balloting_budget)) }
|
||||
|
||||
it { should_not be_able_to(:destroy, investment_in_accepting_budget) }
|
||||
it { should_not be_able_to(:destroy, investment_in_reviewing_budget) }
|
||||
|
||||
@@ -432,6 +432,42 @@ describe "Budgets" do
|
||||
expect(page).to have_content "It's time to support projects!"
|
||||
expect(page).to have_content "So far you've supported 3 projects."
|
||||
end
|
||||
|
||||
scenario "Show supports only if the support has not been removed" do
|
||||
voter = create(:user, :level_two)
|
||||
budget = create(:budget, phase: "selecting")
|
||||
investment = create(:budget_investment, :selected, budget: budget)
|
||||
|
||||
login_as(voter)
|
||||
|
||||
visit budget_path(budget)
|
||||
|
||||
expect(page).to have_content "So far you've supported 0 projects."
|
||||
|
||||
visit budget_investment_path(budget, investment)
|
||||
|
||||
within("#budget_investment_#{investment.id}_votes") do
|
||||
click_button "Support"
|
||||
|
||||
expect(page).to have_content "You have already supported this investment project."
|
||||
end
|
||||
|
||||
visit budget_path(budget)
|
||||
|
||||
expect(page).to have_content "So far you've supported 1 project."
|
||||
|
||||
visit budget_investment_path(budget, investment)
|
||||
|
||||
within("#budget_investment_#{investment.id}_votes") do
|
||||
click_button "Remove your support"
|
||||
|
||||
expect(page).to have_content "No supports"
|
||||
end
|
||||
|
||||
visit budget_path(budget)
|
||||
|
||||
expect(page).to have_content "So far you've supported 0 projects."
|
||||
end
|
||||
end
|
||||
|
||||
context "In Drafting phase" do
|
||||
|
||||
@@ -1177,6 +1177,48 @@ describe "Budget Investments" do
|
||||
expect(page).to have_content "SUPPORTS"
|
||||
end
|
||||
end
|
||||
|
||||
scenario "Remove a support from show view" do
|
||||
investment = create(:budget_investment, budget: budget)
|
||||
|
||||
login_as(author)
|
||||
visit budget_investment_path(budget, investment)
|
||||
|
||||
within("aside") do
|
||||
expect(page).to have_content "No supports"
|
||||
|
||||
click_button "Support"
|
||||
|
||||
expect(page).to have_content "1 support"
|
||||
expect(page).to have_content "You have already supported this investment project."
|
||||
|
||||
click_button "Remove your support"
|
||||
|
||||
expect(page).to have_content "No supports"
|
||||
expect(page).to have_button "Support"
|
||||
end
|
||||
end
|
||||
|
||||
scenario "Remove a support from index view" do
|
||||
investment = create(:budget_investment, budget: budget)
|
||||
|
||||
login_as(author)
|
||||
visit budget_investments_path(budget)
|
||||
|
||||
within("#budget_investment_#{investment.id}") do
|
||||
expect(page).to have_content "No supports"
|
||||
|
||||
click_button "Support"
|
||||
|
||||
expect(page).to have_content "1 support"
|
||||
expect(page).to have_content "You have already supported this investment project."
|
||||
|
||||
click_button "Remove your support"
|
||||
|
||||
expect(page).to have_content "No supports"
|
||||
expect(page).to have_button "Support"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "Evaluating Phase" do
|
||||
|
||||
@@ -333,6 +333,56 @@ describe "Budget Investments" do
|
||||
expect(page).to have_content "CONSUL\nMANAGEMENT"
|
||||
end
|
||||
|
||||
scenario "Remove support on behalf of someone else in index view" do
|
||||
create(:budget_investment, heading: heading)
|
||||
|
||||
login_managed_user(user)
|
||||
login_as_manager(manager)
|
||||
|
||||
visit management_budget_investments_path(budget)
|
||||
click_button "Support"
|
||||
|
||||
expect(page).to have_content "1 support"
|
||||
expect(page).to have_content "You have already supported this investment project. Share it!"
|
||||
expect(page).not_to have_button "Support"
|
||||
|
||||
click_button "Remove your support"
|
||||
|
||||
expect(page).to have_content "No supports"
|
||||
expect(page).to have_button "Support"
|
||||
expect(page).not_to have_button "Remove your support"
|
||||
end
|
||||
|
||||
scenario "Remove support on behalf of someone else in show view" do
|
||||
create(:budget_investment, heading: heading, title: "Don't support me!")
|
||||
|
||||
login_managed_user(user)
|
||||
login_as_manager(manager)
|
||||
|
||||
visit management_budget_investments_path(budget)
|
||||
click_link "Don't support me!"
|
||||
|
||||
expect(page).to have_css "h1", exact_text: "Don't support me!"
|
||||
|
||||
click_button "Support"
|
||||
|
||||
expect(page).to have_content "1 support"
|
||||
expect(page).to have_content "You have already supported this investment project. Share it!"
|
||||
expect(page).not_to have_button "Support"
|
||||
|
||||
click_button "Remove your support"
|
||||
|
||||
expect(page).to have_content "No supports"
|
||||
expect(page).to have_button "Support"
|
||||
expect(page).not_to have_button "Remove your support"
|
||||
|
||||
refresh
|
||||
|
||||
expect(page).to have_content "No supports"
|
||||
expect(page).to have_button "Support"
|
||||
expect(page).not_to have_button "Remove your support"
|
||||
end
|
||||
|
||||
scenario "Should not allow unverified users to vote proposals" do
|
||||
login_managed_user(create(:user))
|
||||
create(:budget_investment, budget: budget)
|
||||
|
||||
Reference in New Issue
Block a user