Skip to content

Commit

Permalink
Vote overwrite configuration (#68)
Browse files Browse the repository at this point in the history
* add votes_overwrite_max config

* Implement new function for updating vote overwrite limit

* fix lint

* add helper spec, fix condition in the helper

* fix votes_left_message

* delete file

* refactoring

* add condition to helper
  • Loading branch information
antopalidi authored Aug 1, 2023
1 parent 53acc6b commit 8ea2d6c
Show file tree
Hide file tree
Showing 13 changed files with 124 additions and 11 deletions.
6 changes: 6 additions & 0 deletions app/controllers/decidim/vocdoni/votes_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ def show
enforce_permission_to :view, :election, election: election
end

def votes_left
votes_left = params[:votesLeft]
message = helpers.votes_left_message(votes_left.to_i)
render json: { message: message }
end

private

def election_unique_id
Expand Down
18 changes: 18 additions & 0 deletions app/helpers/decidim/vocdoni/votes_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,24 @@ def ordered_answers(question)
def more_information?(answer)
translated_attribute(answer.description).present?
end

def votes_left_message(votes_left)
scope = "decidim.vocdoni.votes.new"
max_votes = Decidim::Vocdoni.votes_overwrite_max

return if votes_left > max_votes

message_key, css_class = case votes_left
when (2..max_votes)
%w(can_vote_again secondary)
when 1
%w(can_vote_one_more_time warning)
when 0
%w(no_more_votes_left alert)
end

content_tag :div, t(message_key, scope: scope, votes_left: votes_left), class: "callout #{css_class} js-already_voted"
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ const setupElectionStep = async () => {
defaultLocale: document.querySelector(".js-vocdoni-client").dataset.defaultLocale,
componentId: window.location.pathname.split("/")[5],
electionId: window.location.pathname.split("/")[8],
maxVoteOverwrites: document.querySelector(".js-vocdoni-client").dataset.maxVoteOverwrites,
containerClass: ".process-content"
}, onSuccess, onFailure);
election.run();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ import { Election, PlainCensus } from "@vocdoni/sdk";
import { initVocdoniClient } from "src/decidim/vocdoni/admin/utils/init_vocdoni_client";
import { getAvailableCredits } from "src/decidim/vocdoni/admin/available_credits";

// Do not allow the user to vote multiple times
const MAX_VOTE_OVERWRITES = 0;

/*
* Creates an Election in the Vocdoni API
* Instantiates the Vocdoni SDK client using the Wallet's private key given as parameter.
Expand All @@ -29,6 +26,7 @@ export default class CreateVocdoniElection {
this.defaultLocale = options.defaultLocale;
this.componentId = options.componentId;
this.electionId = options.electionId;
this.maxVoteOverwrites = options.maxVoteOverwrites;
this.containerClass = options.containerClass;
this.onSuccess = onSuccess;
this.onFailure = onFailure;
Expand All @@ -39,6 +37,7 @@ export default class CreateVocdoniElection {
console.log("DEFAULT LOCALE => ", options.defaultLocale);
console.log("VOCDONI COMPONENT ID => ", options.componentId);
console.log("ELECTION ID => ", options.electionId);
console.log("MAX VOTE OVERWRITES => ", options.maxVoteOverwrites);
console.groupEnd();
}

Expand Down Expand Up @@ -134,6 +133,9 @@ export default class CreateVocdoniElection {
let electionMetadata = await this._getElectionMetadata();
electionMetadata = electionMetadata.data.component.election;

// Allow the user to vote multiple times
const MAX_VOTE_OVERWRITES = parseInt(document.querySelector("#max-vote-overwrites").dataset.maxVoteOverwrites, 10);

// Save the electionMetadata in the DOM to show it in the markup if there's any error
const errorDetails = document.querySelector(this.containerClass).querySelector(".js-election-create-error-message-details");
errorDetails.innerHTML = JSON.stringify(electionMetadata, null, 4);
Expand Down
24 changes: 24 additions & 0 deletions app/packs/src/decidim/vocdoni/voter/new-vote.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,30 @@ $(() => {
return;
}

const votesLeft = await client.votesLeftCount();
const electionUrl = document.getElementById("vote-wrapper").dataset.url;
console.log("VOTES LEFT => ", votesLeft);

/**
* Function to update the votes left for a given election.
* @param {number} votesLeftParam - The remaining votes overwrite for the election.
* @returns {void} No return value.
*/
const updateVotesLeft = function(votesLeftParam) {
fetch(electionUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify({ votesLeft: votesLeftParam })
}).then((response) => response.json()).then((data) => {
document.getElementById("votes-left-message").innerHTML = data.message;
}).catch((error) => console.error("Error:", error));
}

updateVotesLeft(votesLeft);

const hasAlreadyVoted = await client.hasAlreadyVoted();
if (hasAlreadyVoted) {
console.log("ALREADY VOTED");
Expand Down
2 changes: 2 additions & 0 deletions app/views/decidim/vocdoni/admin/steps/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<% add_decidim_page_title(t(".title")) %>

<div class="card js-vocdoni-client"
id="max-vote-overwrites"
data-max-vote-overwrites="<%= Decidim::Vocdoni.votes_overwrite_max %>"
data-default-locale="<%= current_organization.default_locale %>"
data-answers-values-election-path="<%= answers_values_election_path(election) %>"
data-vocdoni-wallet-private-key="<%= @current_vocdoni_wallet.private_key %>"
Expand Down
21 changes: 15 additions & 6 deletions app/views/decidim/vocdoni/votes/new.html.erb
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
<div class="vote-wrapper"
data-vocdoni-env="<%= vocdoni_api_endpoint_env %>"
data-election-unique-id="<%== election_unique_id %>"
data-scheme-name="vocdoni"
data-preview="<%= preview_mode? %>">
id="vote-wrapper"
data-vocdoni-env="<%= vocdoni_api_endpoint_env %>"
data-url="<%= votes_left_election_votes_path(election.id) %>"
data-election-unique-id="<%== election_unique_id %>"
data-scheme-name="vocdoni"
data-election-id="<%= election.id %>"
data-preview="<%= preview_mode? %>">

<div id="login" class="focus__step" data-toggler=".hide">
<%= render("login") %>
Expand All @@ -25,8 +28,14 @@
</div>

<% if step_index == 0 %>
<div class="callout warning hide js-already_voted"><%= t(".already_voted") %></div>
<% end %>
<div id="votes-left-message">
<!--
This block is updated by a JavaScript function with server response data.
Initially it's empty, but it's filled with data from JavaScript,
which makes a request to the server for fresh data and updates this block accordingly.
-->
</div>
<% end %>

<div class="focus__content evote">
<div class="row">
Expand Down
3 changes: 3 additions & 0 deletions config/i18n-tasks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ ignore_unused:
- "decidim.vocdoni.elections.show.action_button.{change_vote,vote,vote_again}"
- "decidim.vocdoni.elections.show.preview"
- "decidim.vocdoni.elections.show.verify.{already_voted,verify_here,will_verify}"
- "decidim.vocdoni.votes.new.can_vote_again"
- "decidim.vocdoni.votes.new.can_vote_one_more_time"
- "decidim.vocdoni.votes.new.no_more_votes_left"
- "decidim.vocdoni.models.answer.fields.proposals"
- "decidim.vocdoni.admin.elections.new.basic_info"
- "decidim.vocdoni.admin.elections.new.census"
Expand Down
8 changes: 6 additions & 2 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -444,9 +444,13 @@ en:
modal:
close: Close
new:
already_voted: You have already voted on this election. You can vote again.
The previous vote will be overwritten.
can_vote_again: You have already voted on this election. You can vote again
%{votes_left} times.
can_vote_one_more_time: You have already voted on this election. You can
vote again one more time.
more_information: More information
no_more_votes_left: You have already voted on this election. You can not
vote again.
preview_alert: This is a preview of the voting booth.
question_steps: Question %{current_step} of %{total_steps}
submitting:
Expand Down
6 changes: 6 additions & 0 deletions lib/decidim/vocdoni.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ module Vocdoni
"dev" => "https://api-dev.vocdoni.net/v2"
}.freeze

# Public Setting that defines the maximum number of votes that can be
# overwritten by the voter
config_accessor :votes_overwrite_max do
ENV.fetch("DECIDIM_VOCDONI_VOTES_OVERWRITE_MAX", 10).to_i
end

# Public Setting that defines how many minutes should the setup be run before the election starts
config_accessor :minimum_minutes_before_start do
ENV.fetch("VOCDONI_MINUTES_BEFORE_START", 10).to_i
Expand Down
1 change: 1 addition & 0 deletions lib/decidim/vocdoni/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class Engine < ::Rails::Engine
resources :elections, only: [:index, :show] do
resources :votes, only: [:new, :create, :update, :show] do
match "new", action: :new, via: :post, as: :login, on: :collection
post "votes_left", on: :collection
end
end

Expand Down
34 changes: 34 additions & 0 deletions spec/helpers/decidim/vocdoni/votes_helper_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,40 @@ module Vocdoni
end
end
end

describe "votes_left_message" do
let(:votes_left) { nil }

let(:votes_left_message) { helper.votes_left_message(votes_left) }

before do
allow(Decidim::Vocdoni).to receive(:votes_overwrite_max).and_return(5)
end

context "when votes_left is greater than 1 and less than or equal to votes_overwrite_max" do
let(:votes_left) { 3 }

it "returns the 'can_vote_again' message" do
expect(votes_left_message).to include(I18n.t("can_vote_again", scope: "decidim.vocdoni.votes.new", votes_left: votes_left))
end
end

context "when votes_left is equal to 1" do
let(:votes_left) { 1 }

it "returns the 'can_vote_one_more_time' message" do
expect(votes_left_message).to include(I18n.t("can_vote_one_more_time", scope: "decidim.vocdoni.votes.new"))
end
end

context "when votes_left is 0 or less" do
let(:votes_left) { 0 }

it "returns the 'no_more_votes_left' message" do
expect(votes_left_message).to include(I18n.t("no_more_votes_left", scope: "decidim.vocdoni.votes.new"))
end
end
end
end
end
end
3 changes: 3 additions & 0 deletions spec/lib/decidim/vocdoni/module_config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ module Decidim
let(:env) do
{
"VOCDONI_MINUTES_BEFORE_START" => minutes,
"DECIDIM_VOCDONI_VOTES_OVERWRITE_MAX" => votes_overwrite_max,
"VOCDONI_API_ENDPOINT_ENV" => vocdoni_env,
"VOCDONI_MANUAL_START_DELAY" => start_delay
}
end
let(:vocdoni_env) { "STG" }
let(:minutes) { "11" }
let(:votes_overwrite_max) { "8" }
let(:start_delay) { "33" }
let(:config) { JSON.parse cmd_capture("bin/rails runner 'puts Decidim::Vocdoni.config.to_json'", env: env) }
let(:endpoint_env) { cmd_capture("bin/rails runner 'puts Decidim::Vocdoni.api_endpoint_env'", env: env) }
Expand All @@ -29,6 +31,7 @@ def cmd_capture(cmd, env: {})
it "has the correct configuration" do
expect(config).to eq({
"minimum_minutes_before_start" => 11,
"votes_overwrite_max" => 8,
"api_endpoint_env" => "STG",
"manual_start_time_delay" => 33,
"interruptible_elections" => true
Expand Down

0 comments on commit 8ea2d6c

Please sign in to comment.