From 5768ddd3ec0c50f36b4e8828a63550132ab114b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20Verg=C3=A9s?= Date: Thu, 2 Nov 2023 10:21:52 +0100 Subject: [PATCH] Add modal window with instructions (#20) * 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 --- README.md | 30 +- .../proposal_m_cell_override.rb | 38 ++ .../voting/three_flags_base_cell.rb | 8 + .../voting/three_flags_counter/show.erb | 23 +- .../three_flags_counter/vote_button.erb | 15 +- .../voting/three_flags_counter_cell.rb | 16 - .../voting/three_flags_proposal/show.erb | 49 +- .../three_flags_proposal/vote_block_for.erb | 18 +- .../voting/three_flags_proposal_cell.rb | 51 +- .../three_flags_proposal_modal/show.erb | 24 +- .../voting/three_flags_proposal_modal_cell.rb | 19 +- .../proposals/orderable_override.rb | 27 +- .../decidim_awesome/has_vote_weight.rb | 7 + .../replace_counter.html.erb.deface | 2 +- .../decidim/decidim_awesome/handcard.svg | 14 + .../decidim/decidim_awesome/handcheck.svg | 17 + .../decidim_awesome/awesome_application.js | 1 + .../decidim_awesome/voting/three_flags.js | 63 ++ .../decidim_awesome/voting/three_flags.scss | 158 +++-- .../three_flags/_proposal_m_cell_footer.erb | 5 +- .../three_flags/_show_vote_button.html.erb | 3 + config/locales/en.yml | 59 +- lib/decidim/decidim_awesome/awesome.rb | 11 + lib/decidim/decidim_awesome/checksums.yml | 6 +- lib/decidim/decidim_awesome/engine.rb | 35 +- .../decidim_awesome/voting_manifest.rb | 2 +- .../voting/three_flags_counter_cell_spec.rb | 18 - .../voting/three_flags_proposal_cell_spec.rb | 23 - spec/controllers/proposals_controller_spec.rb | 139 +++- .../proposals_votes_controller_spec.rb | 20 +- spec/lib/system_checker_spec.rb | 4 +- spec/system/three_flags_spec.rb | 594 ++++++++++++++---- 32 files changed, 1165 insertions(+), 334 deletions(-) create mode 100644 app/cells/concerns/decidim/decidim_awesome/proposal_m_cell_override.rb create mode 100644 app/packs/images/decidim/decidim_awesome/handcard.svg create mode 100644 app/packs/images/decidim/decidim_awesome/handcheck.svg create mode 100644 app/packs/src/decidim/decidim_awesome/voting/three_flags.js diff --git a/README.md b/README.md index f27f0d254..567708c34 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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}" diff --git a/app/cells/concerns/decidim/decidim_awesome/proposal_m_cell_override.rb b/app/cells/concerns/decidim/decidim_awesome/proposal_m_cell_override.rb new file mode 100644 index 000000000..ffcf81cfb --- /dev/null +++ b/app/cells/concerns/decidim/decidim_awesome/proposal_m_cell_override.rb @@ -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 diff --git a/app/cells/decidim/decidim_awesome/voting/three_flags_base_cell.rb b/app/cells/decidim/decidim_awesome/voting/three_flags_base_cell.rb index ffe4827af..ffd2b5277 100644 --- a/app/cells/decidim/decidim_awesome/voting/three_flags_base_cell.rb +++ b/app/cells/decidim/decidim_awesome/voting/three_flags_base_cell.rb @@ -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 @@ -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 diff --git a/app/cells/decidim/decidim_awesome/voting/three_flags_counter/show.erb b/app/cells/decidim/decidim_awesome/voting/three_flags_counter/show.erb index b7786bfaf..69e51d2e0 100644 --- a/app/cells/decidim/decidim_awesome/voting/three_flags_counter/show.erb +++ b/app/cells/decidim/decidim_awesome/voting/three_flags_counter/show.erb @@ -1,8 +1,15 @@ - - G: <%= model.weight_count(3) %> | - Y: <%= model.weight_count(2) %> | - R: <%= model.weight_count(1) %> - -
- <%= render "vote_button" %> -
+<% unless proposal.rejected? || proposal.withdrawn? %> + <% unless current_settings.votes_hidden? %> + + <%= t("decidim.decidim_awesome.voting.three_flags.weights.g") %>: <%= model.weight_count(3) %> | + <%= t("decidim.decidim_awesome.voting.three_flags.weights.y") %>: <%= model.weight_count(2) %> | + <%= t("decidim.decidim_awesome.voting.three_flags.weights.r") %>: <%= model.weight_count(1) %> + <% if current_component.settings.three_flags_show_abstain %> + | <%= t("decidim.decidim_awesome.voting.three_flags.weights.a") %>: <%= model.weight_count(0) %> + <% end %> + + <% end %> +
+ <%= render "vote_button" %> +
+<% end %> diff --git a/app/cells/decidim/decidim_awesome/voting/three_flags_counter/vote_button.erb b/app/cells/decidim/decidim_awesome/voting/three_flags_counter/vote_button.erb index f3eb0c794..50b4fd164 100644 --- a/app/cells/decidim/decidim_awesome/voting/three_flags_counter/vote_button.erb +++ b/app/cells/decidim/decidim_awesome/voting/three_flags_counter/vote_button.erb @@ -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" %><%= t("decidim.decidim_awesome.proposal_m.voted") %> + <%= 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 %> diff --git a/app/cells/decidim/decidim_awesome/voting/three_flags_counter_cell.rb b/app/cells/decidim/decidim_awesome/voting/three_flags_counter_cell.rb index 504a878b9..8c922e116 100644 --- a/app/cells/decidim/decidim_awesome/voting/three_flags_counter_cell.rb +++ b/app/cells/decidim/decidim_awesome/voting/three_flags_counter_cell.rb @@ -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 diff --git a/app/cells/decidim/decidim_awesome/voting/three_flags_proposal/show.erb b/app/cells/decidim/decidim_awesome/voting/three_flags_proposal/show.erb index a48aa4462..e4f7a945a 100644 --- a/app/cells/decidim/decidim_awesome/voting/three_flags_proposal/show.erb +++ b/app/cells/decidim/decidim_awesome/voting/three_flags_proposal/show.erb @@ -1,28 +1,35 @@ -

<%= t("decidim.decidim_awesome.proposal_m.modal.title") %>

-<% if voted_for_any? %> +

<%= title %>

+ +
+ <%= vote_block_for(proposal, 3) %> + <%= vote_block_for(proposal, 2) %> + <%= vote_block_for(proposal, 1) %> +
+ +<% 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? %>

<%= 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 %> -

- -
- <%= vote_block_for(proposal, 3, "green") %> - <%= vote_block_for(proposal, 2, "yellow") %> - <%= vote_block_for(proposal, 1, "red") %> -
-<% 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" %> +

+<% 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") %>

<% end %> diff --git a/app/cells/decidim/decidim_awesome/voting/three_flags_proposal/vote_block_for.erb b/app/cells/decidim/decidim_awesome/voting/three_flags_proposal/vote_block_for.erb index b8ec01cb8..2da884d86 100644 --- a/app/cells/decidim/decidim_awesome/voting/three_flags_proposal/vote_block_for.erb +++ b/app/cells/decidim/decidim_awesome/voting/three_flags_proposal/vote_block_for.erb @@ -1,14 +1,16 @@
- diff --git a/app/cells/decidim/decidim_awesome/voting/three_flags_proposal_cell.rb b/app/cells/decidim/decidim_awesome/voting/three_flags_proposal_cell.rb index 98c0afde3..163e64651 100644 --- a/app/cells/decidim/decidim_awesome/voting/three_flags_proposal_cell.rb +++ b/app/cells/decidim/decidim_awesome/voting/three_flags_proposal_cell.rb @@ -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 @@ -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 diff --git a/app/cells/decidim/decidim_awesome/voting/three_flags_proposal_modal/show.erb b/app/cells/decidim/decidim_awesome/voting/three_flags_proposal_modal/show.erb index c546e0959..ad3e9fe96 100644 --- a/app/cells/decidim/decidim_awesome/voting/three_flags_proposal_modal/show.erb +++ b/app/cells/decidim/decidim_awesome/voting/three_flags_proposal_modal/show.erb @@ -1,18 +1,16 @@ -