Skip to content

Commit

Permalink
Add modal window with instructions (#20)
Browse files Browse the repository at this point in the history
* change basic copies

* namespace css

* introduce modal pre-voting

* handle localstorage

* refactor namespaces for copies

* fix copy

* copies

* add finger

* add svg icons

* add margin

* fix margins in modal

* abstain style

* fix tests

* prevent voting abstain if not allowed

* fix footer

* normalize i18n

* add additional specs

* add conf var for sorting

* fix cache proposal_m

* proposal conf fixes
  • Loading branch information
microstudi authored Nov 2, 2023
1 parent ef5770a commit 5768ddd
Show file tree
Hide file tree
Showing 32 changed files with 1,165 additions and 334 deletions.
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,33 @@ Results can be filtered by role and by time range and also exported as CSV or ot

![Admin accountability](examples/admin_accountability.png)

#### 16. Weighted voting
#### 16. Additional proposal sortings

This feature allows you to add additional sorting options to the proposals component. By default 4 additional sortings are included:

- `supported_first`: Sort proposals supported by me first.
- `supported_last`: Sort proposals supported by me last.
- `az`: Sort proposals alphabetically.
- `za`: Sort proposals alphabetically (reverse).

By enabling this feature the user choosed sorting method will be stored in the browser's session. This means that if the user changes the sorting method and then navigates to another page, the same sorting will be applied.

You can disable or configure this feature setting the variable `additional_proposal_sortings` configuration in an initializer:

```ruby
# config/initializers/awesome_defaults.rb
Decidim::DecidimAwesome.configure do |config|
config.additional_proposal_sortings = :disabled
end

# Or, to disable alphabetical sorting:

Decidim::DecidimAwesome.configure do |config|
config.additional_proposal_sortings = [:supported_first, :supported_last]
end
```

#### 17. Weighted voting

This feature allows you to configure a proposals component to use a weighted voting system. This means that each vote can have a different weight and the result of the vote is calculated as the sum of all the weights.

Expand Down Expand Up @@ -198,7 +224,7 @@ if Decidim::DecidimAwesome.enabled?(:weighted_proposal_voting)

# optionally, define a label generator block
# by default labels are extracted from a I18n key following this rule
# "decidim.decidim_awesome.votings.manifests.{MANIFEST_NAME}.weight_{WEIGHT}"
# "decidim.decidim_awesome.voting.{MANIFEST_NAME}.weights.weight_{WEIGHT}"
#
# voting.label_generator do |weight, context|
# "Weight #{weight.round}"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# frozen_string_literal: true

module Decidim
module DecidimAwesome
module ProposalMCellOverride
extend ActiveSupport::Concern

included do
# rubocop:disable Metrics/CyclomaticComplexity
def cache_hash
hash = []
hash << I18n.locale.to_s
hash << model.cache_key_with_version
hash << model.proposal_votes_count
hash << model.weight_cache&.totals
hash << model.endorsements_count
hash << model.comments_count
hash << Digest::MD5.hexdigest(model.component.cache_key_with_version)
hash << Digest::MD5.hexdigest(resource_image_path) if resource_image_path
hash << render_space? ? 1 : 0
if current_user
hash << current_user.cache_key_with_version
hash << current_user.follows?(model) ? 1 : 0
end
hash << model.follows_count
hash << Digest::MD5.hexdigest(model.authors.map(&:cache_key_with_version).to_s)
hash << (model.must_render_translation?(model.organization) ? 1 : 0) if model.respond_to?(:must_render_translation?)
hash << model.component.participatory_space.active_step.id if model.component.participatory_space.try(:active_step)
hash << has_footer?
hash << has_actions?

hash.join(Decidim.cache_key_separator)
end
# rubocop:enable Metrics/CyclomaticComplexity
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ module Voting
class ThreeFlagsBaseCell < Decidim::ViewModel
include Decidim::IconHelper
include Decidim::ComponentPathHelper
include Decidim::Proposals::ProposalVotesHelper
include Decidim::Proposals::Engine.routes.url_helpers

def proposal
model
end
Expand All @@ -15,6 +17,12 @@ def current_component
proposal.component
end

def component_settings
current_component.settings
end

delegate :current_settings, to: :current_component

def current_vote
@current_vote ||= Decidim::Proposals::ProposalVote.find_by(author: current_user, proposal: model)
end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
<span class="votes_counter">
<span id="green-votes-<%= model.id %>" data-weight="3" class="text-success">G: <%= model.weight_count(3) %></span> |
<span id="yellow-votes-<%= model.id %>" data-weight="2" class="text-warning">Y: <%= model.weight_count(2) %></span> |
<span id="red-votes-<%= model.id %>" data-weight="1" class="text-alert">R: <%= model.weight_count(1) %></span>
</span>
<div id="proposal-<%= model.id %>-vote-button">
<%= render "vote_button" %>
</div>
<% unless proposal.rejected? || proposal.withdrawn? %>
<% unless current_settings.votes_hidden? %>
<span class="votes_counter">
<span data-weight="3" title="<%= proposal.manifest.label_for(3) %>" class="text-success"><%= t("decidim.decidim_awesome.voting.three_flags.weights.g") %>: <%= model.weight_count(3) %></span> |
<span data-weight="2" title="<%= proposal.manifest.label_for(2) %>" class="text-warning"><%= t("decidim.decidim_awesome.voting.three_flags.weights.y") %>: <%= model.weight_count(2) %></span> |
<span data-weight="1" title="<%= proposal.manifest.label_for(1) %>" class="text-alert"><%= t("decidim.decidim_awesome.voting.three_flags.weights.r") %>: <%= model.weight_count(1) %></span>
<% if current_component.settings.three_flags_show_abstain %>
| <span title="<%= proposal.manifest.label_for(0) %>" data-weight="0"><%= t("decidim.decidim_awesome.voting.three_flags.weights.a") %>: <%= model.weight_count(0) %></span>
<% end %>
</span>
<% end %>
<div id="proposal-<%= model.id %>-vote-button">
<%= render "vote_button" %>
</div>
<% end %>
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
<%= link_to resource_path,
class: "button #{vote_btn_class} button--sc",
title: t("decidim.decidim_awesome.proposal_m.vote_button"),
style: "cursor: pointer; padding-top: 0.6rem; padding-bottom: 0.6rem; margin-bottom: 0" do %>
class: "button #{vote_btn_class} small button--sc#{" disabled" if current_settings.votes_blocked?}",
title: t("decidim.decidim_awesome.voting.three_flags.vote_button") do %>
<% if user_voted_weight %>
<%= icon "actions", class: "icon" %><span><%= t("decidim.decidim_awesome.proposal_m.voted") %></span>
<%= icon "actions", class: "icon" %> <%= t("decidim.decidim_awesome.voting.three_flags.voted") %>
<% elsif proposal.maximum_votes_reached? && !proposal.can_accumulate_supports_beyond_threshold && current_component.participatory_space.can_participate?(current_user) %>
<%= t("decidim.proposals.proposals.vote_button.maximum_votes_reached") %>
<% elsif vote_limit_enabled? && remaining_votes_count_for(current_user) <= 0 %>
<%= t("decidim.proposals.proposals.vote_button.no_votes_remaining") %>
<% elsif current_settings.votes_blocked? || !current_component.participatory_space.can_participate?(current_user) %>
<%= t("decidim.proposals.proposals.vote_button.votes_blocked") %>
<% else %>
<%= t("decidim.decidim_awesome.proposal_m.vote_button") %>
<%= t("decidim.decidim_awesome.voting.three_flags.vote_button") %>
<% end %>
<% end %>
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,6 @@ def resource_path
resource_locator(model).path
end

def vote_span(weight, color)
content_tag :span, "#{color[0].upcase}:#{model.weight_count(weight)}", class: "text-#{color}"
end

def green_votes
vote_span(3, "green")
end

def yellow_votes
vote_span(2, "yellow")
end

def red_votes
vote_span(1, "red")
end

def user_voted_weight
current_vote&.weight
end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,35 @@
<h4 class="heading4 vote-title"><%= t("decidim.decidim_awesome.proposal_m.modal.title") %></h4>
<% if voted_for_any? %>
<h4 class="heading4 vote-title"><%= title %></h4>

<div class="flex--sbc">
<%= vote_block_for(proposal, 3) %>
<%= vote_block_for(proposal, 2) %>
<%= vote_block_for(proposal, 1) %>
</div>

<% if component_settings.three_flags_show_abstain? %>
<%= action_authorized_link_to :vote,
voted_for?(0) ? t("decidim.decidim_awesome.voting.three_flags.abstained") : proposal.manifest.label_for(0),
proposal_vote_path(0),
link_options(0).merge({
title: t("decidim.decidim_awesome.voting.three_flags.voting_for", type: proposal.manifest.label_for(0)),
class: "button secondary hollow expanded vote-action abstain-button small #{classes_for(0)}"
}) %>
<% end %>

<% if voted_for_any? && !current_settings.votes_blocked? %>
<p class="text-center">
<%= action_authorized_link_to :unvote,
"👉 #{t('decidim.decidim_awesome.proposal_m.change_vote')}",
t("decidim.decidim_awesome.voting.three_flags.change_vote"),
proposal_vote_path(current_vote&.weight),
resource: proposal,
remote: true,
method: :delete,
id: "change-vote",
class: "change-vote-button" %>
<% end %>
</p>

<div class="flex--sbc">
<%= vote_block_for(proposal, 3, "green") %>
<%= vote_block_for(proposal, 2, "yellow") %>
<%= vote_block_for(proposal, 1, "red") %>
</div>
<% if current_component.settings.proposal_vote_abstain %>
<%= action_authorized_link_to :vote,
t("decidim.decidim_awesome.proposal_m.abstain"),
proposal_vote_path(0),
resource: proposal,
remote: true,
method: :post,
class: "button expanded abstain-button small #{opacity_class_for(0)}" %>
class: "change-vote-button vote-action" %>
</p>
<% elsif proposal.maximum_votes_reached? && !proposal.can_accumulate_supports_beyond_threshold && current_component.participatory_space.can_participate?(current_user) %>
<p class="text-center"><%= t("decidim.proposals.proposals.vote_button.maximum_votes_reached") %></p>
<% elsif vote_limit_enabled? && remaining_votes_count_for(current_user) <= 0 %>
<p class="text-center"><%= t("decidim.proposals.proposals.vote_button.no_votes_remaining") %></p>
<% elsif current_settings.votes_blocked? || !current_component.participatory_space.can_participate?(current_user) %>
<p class="text-center"><%= t("decidim.proposals.proposals.vote_button.votes_blocked") %></p>
<% end %>
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
<div id="vote-container" data-proposal-id="<%= proposal.id %>">
<div class="vote-block modal" data-modal-id="voteProposalModal<%= proposal.id %>" id="vote-proposal-<%= proposal.id %>-<%= weight %>">
<p class="vote-count" data-id="<%= proposal.id %>" data-weight="<%= weight %>"><%= proposal_votes(weight) %></p>
<p class="vote-label <%= color %>"><%= color.capitalize %></p>
<div class="vote-block">
<% unless current_settings.votes_hidden? %>
<p class="vote-count" data-weight="<%= weight %>"><%= proposal_votes(weight) %></p>
<% end %>
<%= action_authorized_link_to :vote,
proposal_vote_path(weight),
resource: proposal,
remote: true,
method: :post,
class: "rectangle voting-block #{color} #{opacity_class_for(weight)} #{'voted' if voted_for?(weight)}" do %>
<%= icon "thumb-up" %>
**link_options(weight) do %>
<span class="vote-label"><%= proposal.manifest.label_for(weight) %></span>
<%= content_tag :svg, role: "img" do
content_tag(:title, t("decidim.decidim_awesome.voting.three_flags.voting_for", type: proposal.manifest.label_for(weight))) +
content_tag(:use, "", "href" => svg_path(weight))
end %>
<% end %>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@ def show
render :show
end

def vote_block_for(proposal, weight, color)
def vote_block_for(proposal, weight)
render partial: "vote_block", locals: {
proposal: proposal,
weight: weight,
color: color
weight: weight
}
end

Expand All @@ -34,16 +33,54 @@ def proposal_vote_path(weight)
proposal_proposal_vote_path(proposal_id: proposal.id, from_proposals_list: from_proposals_list, weight: weight)
end

def opacity_class_for(weight)
opacity = !voted_for_any? || voted_for?(weight) ? "fully-opaque" : "semi-opaque"
clickable = voted_for_any? ? "non-clickable" : ""
def link_options(weight)
ops = {
class: "vote-action vote-card #{classes_for(weight)}"
}
if current_user
ops.merge!({
remote: true,
method: :post
})
end
ops
end

def svg_path(weight)
card = "handcard"
card = "handcheck" if voted_for?(weight)
"#{asset_pack_path("media/images/#{card}.svg")}#handcard"
end

def classes_for(weight)
ops = ["weight_#{weight}"]
ops << "voted" if voted_for?(weight)
ops << "dim" if voted_for_any? && !voted_for?(weight)
ops << "disabled" if disabled?

ops.join(" ")
end

def disabled?
return true if voted_for_any? || current_settings.votes_blocked?

if proposal.maximum_votes_reached? && !proposal.can_accumulate_supports_beyond_threshold && current_component.participatory_space.can_participate?(current_user)
return true
end

[opacity, clickable].reject(&:empty?).join(" ")
return true if vote_limit_enabled? && remaining_votes_count_for(current_user) <= 0
end

def voted_for_any?
VOTE_WEIGHTS.any? { |opt| voted_for?(opt) }
end

def title
txt ||= translated_attribute(current_component.settings.three_flags_box_title)
return "" if txt == "-"

txt.presence || t("decidim.decidim_awesome.voting.three_flags.default_box_title")
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
<!--the modal window will be used in another pr-->
<div class="reveal vote_proposal_modal voting-three-flag" id="<%= modal_id %>" data-reveal role="dialog" aria-modal="true" aria-labelledby="<%#= modal_id %>-label">
<div class="reveal vote_proposal_modal voting-three-flag" id="threeFlagsModalHelp" data-reveal role="dialog" aria-modal="true" aria-labelledby="threeFlagsModalHelp-label">
<div class="reveal__content">
<p><%= vote_instructions %></p>
<div>
<%= check_box_tag "#{modal_id}-no_more_messages", true, false, id: "#{modal_id}-no-more-messages" %>
<%= label_tag "#{modal_id}-no-more-messages", "Check here to not receive these messages in the future" %>
<div class="instructions"><%= vote_instructions %></div>
<div class="current-choice voting-three-flags"><div class="vote-card"></div></div>
<div class="future-dismiss">
<%= check_box_tag "three_flag-skip_help", current_component.id, false %>
<%= label_tag "three_flag-skip_help", t("decidim.decidim_awesome.voting.three_flags.modal.skip") %>
</div>

<div class="text-center">
<button class="button vote-action"><%= t("decidim.decidim_awesome.voting.three_flags.modal.proceed") %></button>
<button class="button cancel-action hollow secondary" data-close><%= t("decidim.decidim_awesome.voting.three_flags.modal.cancel") %></button>
</div>
</div>
<%= action_authorized_link_to :vote,
proposal_vote_path(weight),
resource: proposal,
remote: true,
method: :post,
class: "button" do %>
Proceed
<% end %>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,9 @@ def show
render :show
end

def modal_id
options[:modal_id] || "voteProposalModal"
end

def from_proposals_list
options[:from_proposals_list]
end

def vote_instructions
translated_attribute(current_component.settings.proposal_vote_instructions)
end

def proposal_vote_path(weight)
proposal_proposal_vote_path(proposal_id: proposal.id, from_proposals_list: from_proposals_list, weight: weight)
end

def weight
options[:weight].to_i
translated_attribute(current_component.settings.three_flags_instructions).presence || t("decidim.decidim_awesome.voting.three_flags.default_instructions_html",
organization: current_organization.name)
end
end
end
Expand Down
Loading

0 comments on commit 5768ddd

Please sign in to comment.