decidim-budgets 0.25.2 → 0.26.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/decidim/budgets/budget/_budget-vote-button.scss +22 -0
  3. data/app/cells/decidim/budgets/budget_information_modal/show.erb +3 -3
  4. data/app/cells/decidim/budgets/budget_list_item/show.erb +31 -15
  5. data/app/cells/decidim/budgets/budget_list_item_cell.rb +30 -1
  6. data/app/cells/decidim/budgets/budgets_header/show.erb +1 -1
  7. data/app/cells/decidim/budgets/budgets_list/card_list.erb +17 -6
  8. data/app/cells/decidim/budgets/budgets_list/show.erb +2 -3
  9. data/app/cells/decidim/budgets/budgets_list/voted.erb +6 -0
  10. data/app/cells/decidim/budgets/project_list_item/project_data.erb +1 -1
  11. data/app/cells/decidim/budgets/{project_list_item/project_data_vote_button.erb → project_vote_button/show.erb} +1 -0
  12. data/app/cells/decidim/budgets/project_vote_button_cell.rb +10 -0
  13. data/app/commands/decidim/budgets/add_line_item.rb +5 -1
  14. data/app/controllers/concerns/decidim/budgets/admin/filterable.rb +51 -0
  15. data/app/controllers/decidim/budgets/admin/projects_controller.rb +6 -1
  16. data/app/controllers/decidim/budgets/line_items_controller.rb +2 -2
  17. data/app/helpers/decidim/budgets/admin/filterable_helper.rb +10 -0
  18. data/app/models/decidim/budgets/project.rb +28 -1
  19. data/app/packs/entrypoints/decidim_budgets.js +1 -0
  20. data/app/packs/src/decidim/budgets/exit_handler.js +131 -0
  21. data/app/packs/src/decidim/budgets/projects.js +9 -39
  22. data/app/packs/stylesheets/decidim/budgets/budget/_budget-list.scss +35 -1
  23. data/app/views/decidim/budgets/admin/projects/index.html.erb +16 -5
  24. data/app/views/decidim/budgets/projects/_exit_modal.html.erb +22 -0
  25. data/app/views/decidim/budgets/projects/_filters_small_view.html.erb +3 -3
  26. data/app/views/decidim/budgets/projects/_project_budget_button.html.erb +17 -38
  27. data/app/views/decidim/budgets/projects/index.html.erb +25 -22
  28. data/app/views/decidim/budgets/projects/show.html.erb +41 -38
  29. data/config/locales/ca.yml +3 -0
  30. data/config/locales/cs.yml +23 -0
  31. data/config/locales/de.yml +9 -0
  32. data/config/locales/en.yml +23 -0
  33. data/config/locales/es.yml +23 -0
  34. data/config/locales/eu.yml +3 -0
  35. data/config/locales/fi-plain.yml +23 -0
  36. data/config/locales/fi.yml +23 -0
  37. data/config/locales/fr-CA.yml +11 -0
  38. data/config/locales/fr.yml +13 -2
  39. data/config/locales/gl.yml +9 -0
  40. data/config/locales/it.yml +9 -0
  41. data/config/locales/ja.yml +28 -5
  42. data/config/locales/lb-LU.yml +294 -0
  43. data/config/locales/nl.yml +18 -0
  44. data/config/locales/pl.yml +2 -0
  45. data/config/locales/pt-BR.yml +1 -1
  46. data/config/locales/pt.yml +3 -0
  47. data/config/locales/ro-RO.yml +15 -0
  48. data/config/locales/sv.yml +3 -0
  49. data/config/locales/val-ES.yml +1 -0
  50. data/lib/decidim/budgets/version.rb +1 -1
  51. metadata +23 -17
  52. data/app/cells/decidim/budgets/budgets_list/highlighted.erb +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 962bc802aefb2f454ad4737af4062ded5b34c49a55f89cad22cdd97bc7fb60be
4
- data.tar.gz: 81321d312849f1f2223a47a8eca5bdb1a6a5f846cce4e23ea01784fef415cb3e
3
+ metadata.gz: 5faa45ba2c53c8c620e02d864b28790d7a776a82aa4650fdf6e5bfa36353adb3
4
+ data.tar.gz: d4698f56fef1acacb46f2f37f0eea903c543814d063b6473e7ebfa5677113fac
5
5
  SHA512:
6
- metadata.gz: 56952ca141f9df95aa2cf01af89542a968277729eb2b8214bf4f714cf0c9957f7e414dd21cf809a20c158ee75c9b7a410cfea4b0e1a12dd497737a3ca2bc37b0
7
- data.tar.gz: 9df57bd7064cae9ae426208e79677475cb8a0fdcf28f30212027eb28cdb6e50031b88618e8d50ee9d32b61ee5a4435af15d21c92813a03f0322b41db11de6578
6
+ metadata.gz: a19091bcad973926f835062473591c953166beda0fce9f7529cc1902569c40aa0fb9526fa4f25d8810ca62a9e1d2fb9d93a3e32845f3e9f5f8cc2f0966e432e5
7
+ data.tar.gz: 9f10d173bdf303db02468bd2bf3fcb78d9f18a052d148e955d934d7bdb1df78150bd90136442d63eb7627e9cc03af1c584ddcbd417aba49188712ab07eb349c8
@@ -0,0 +1,22 @@
1
+ .budget-vote-button.expanded{
2
+ display: flex;
3
+ text-align: left;
4
+ justify-content: space-between;
5
+ align-items: center;
6
+
7
+ span{
8
+ font-size: 1rem;
9
+ }
10
+
11
+ &.added{
12
+ background-color: tint($success, 80%);
13
+ color: $body-font-color;
14
+ }
15
+
16
+ &:not(.added){
17
+ .budget-list__action{
18
+ border-color: white;
19
+ color: white;
20
+ }
21
+ }
22
+ }
@@ -1,10 +1,10 @@
1
- <button class="link" data-open="budget-modal-info" aria-controls="budget-modal-info" aria-haspopup="true" tabindex="0">
1
+ <button class="link" data-open="budget-modal-info" aria-controls="budget-modal-info" aria-haspopup="dialog" tabindex="0">
2
2
  <%= t(:more_information, scope: i18n_scope) %>
3
3
  </button>
4
4
 
5
- <div class="reveal" data-reveal id="budget-modal-info">
5
+ <div class="reveal" data-reveal id="budget-modal-info" aria-modal="true" aria-labelledby="budget-modal-info-label">
6
6
  <div class="reveal__header">
7
- <h3 class="reveal__title"><%= decidim_sanitize(component_name) %></h3>
7
+ <h3 id="budget-modal-info-label" class="reveal__title"><%= decidim_sanitize(component_name) %></h3>
8
8
  <button class="close-button" data-close aria-label="<%= t(:close_modal, scope: i18n_scope) %>" type="button">
9
9
  <span aria-hidden="true">×</span>
10
10
  </button>
@@ -1,22 +1,38 @@
1
- <div class="<%= card_class %>">
2
- <div class="card--list__text">
1
+ <div class="<%= card_class %> budget-list__item budget-list__item-cell">
2
+ <div class="budget-list__text flex-horizontal">
3
3
  <%= link_to budget_path(budget), class: link_class do %>
4
- <% if voted? && !voting_finished? %>
5
- <strong>
6
- <%= translated_attribute(title) %>
7
- </strong>
8
- <span class="button tiny success card--list__check card--list__check--disabled">
9
- <%= icon "check", class: "icon--small", role: "img", aria_label: t("decidim.budgets.budget_list_item.voting_finished") %>
10
- </span>
11
- <% else %>
4
+ <h5 class="card--list__heading">
12
5
  <%= translated_attribute(title) %>
6
+ </h5>
7
+ <% end %>
8
+
9
+ <h5>
10
+ <strong>
11
+ <%= budget_to_currency(total_budget) %>
12
+ </strong>
13
+ </h5>
14
+
15
+ <%= decidim_sanitize html_truncate(translated_attribute(description), length: 140) %>
16
+ </div>
13
17
 
14
- <% if progress? && !voting_finished? %>
15
- <span class="button tiny hollow secondary card--list__check card--list__check--disabled">
16
- <%= icon "bookmark", class: "icon--small", role: "img", aria_label: t("decidim.budgets.budget_list_item.voting_started") %>
17
- </span>
18
- <% end %>
18
+ <% if !voting_finished? %>
19
+ <div class="budget-list__icon mr-s">
20
+ <% if voted? %>
21
+ <span class="button tiny success card--list__check card--list__check--disabled card--list__check--inline">
22
+ <%= icon "check", class: "icon--small", role: "img", aria_label: t("decidim.budgets.budget_list_item.voting_finished") %>
23
+ </span>
24
+ <% elsif progress? %>
25
+ <span class="button tiny warning card--list__check card--list__check--disabled card--list__check--inline">
26
+ <%= icon "bookmark", class: "icon--small", role: "img", aria_label: t("decidim.budgets.budget_list_item.voting_started") %>
27
+ </span>
19
28
  <% end %>
29
+ </div>
30
+ <% end %>
31
+
32
+ <div class="budget-list__data">
33
+ <%= link_to budget_path(budget), class: "button button--sc expanded #{button_class} mb-none" do %>
34
+ <span><%= button_text %></span>
35
+ <%= icon "chevron-right", class: "icon--small", role: "img" %>
20
36
  <% end %>
21
37
  </div>
22
38
  </div>
@@ -4,9 +4,15 @@ module Decidim
4
4
  module Budgets
5
5
  # This cell renders the budget item list in the budgets list
6
6
  class BudgetListItemCell < BaseCell
7
+ include Decidim::SanitizeHelper
8
+ include Decidim::ApplicationHelper
9
+ include ActiveSupport::NumberHelper
10
+ include Decidim::Budgets::ProjectsHelper
11
+
7
12
  delegate :voting_finished?, to: :controller
13
+ delegate :highlighted, to: :current_workflow
8
14
 
9
- property :title
15
+ property :title, :description, :total_budget
10
16
  alias budget model
11
17
 
12
18
  private
@@ -17,6 +23,7 @@ module Decidim
17
23
  list << "card--list__data-added" if voted?
18
24
  list << "card--list__data-progress" if progress?
19
25
  end
26
+ list << "budget--highlighted" if highlighted?
20
27
  end.join(" ")
21
28
  end
22
29
 
@@ -32,9 +39,31 @@ module Decidim
32
39
  current_user && status == :progress
33
40
  end
34
41
 
42
+ def highlighted?
43
+ highlighted.include?(budget)
44
+ end
45
+
35
46
  def status
36
47
  @status ||= current_workflow.status(budget)
37
48
  end
49
+
50
+ def button_class
51
+ "hollow" if voted? || !highlighted?
52
+ end
53
+
54
+ def button_text
55
+ key = if current_workflow.vote_allowed?(budget) && !voted?
56
+ progress? ? :progress : :vote
57
+ else
58
+ :show
59
+ end
60
+
61
+ t(key, scope: i18n_scope)
62
+ end
63
+
64
+ def i18n_scope
65
+ "decidim.budgets.budgets_list"
66
+ end
38
67
  end
39
68
  end
40
69
  end
@@ -1,7 +1,7 @@
1
1
  <div class="row">
2
2
  <div class="columns medium-7 mediumlarge-8">
3
3
  <div class="section">
4
- <%= decidim_sanitize(landing_page_content) %>
4
+ <%= decidim_sanitize_editor(landing_page_content) %>
5
5
  </div>
6
6
  </div>
7
7
  </div>
@@ -1,7 +1,18 @@
1
- <div id="budgets" class="card card--list card--list--mini">
2
- <% budgets.each do |budget| %>
3
- <% next if highlighted.include?(budget) && !voting_finished? %>
1
+ <%# show highlighted budgets first %>
2
+ <% if highlighted.any? %>
3
+ <div id="highlighted-budgets" class="card card--list budget-list">
4
+ <% highlighted.each do |budget| %>
5
+ <%= cell("decidim/budgets/budget_list_item", budget) %>
6
+ <% end %>
7
+ </div>
8
+ <% end %>
4
9
 
5
- <%= cell("decidim/budgets/budget_list_item", budget) %>
6
- <% end %>
7
- </div>
10
+ <% non_highlighted = (budgets - highlighted - voted) %>
11
+
12
+ <% if non_highlighted.any? %>
13
+ <div id="budgets" class="card card--list budget-list">
14
+ <% non_highlighted.each do |budget| %>
15
+ <%= cell("decidim/budgets/budget_list_item", budget) %>
16
+ <% end %>
17
+ </div>
18
+ <% end %>
@@ -1,18 +1,17 @@
1
1
  <div class="row">
2
2
  <div class="columns medium-7 mediumlarge-8">
3
- <% if !voting_finished? && (highlighted? || voted?) %>
3
+ <% if !voting_finished? && (voted?) %>
4
4
  <div class="section">
5
5
  <h3 class="section-heading">
6
6
  <%= t(:my_budgets, scope: i18n_scope) %>
7
7
  </h3>
8
8
 
9
- <%= render :highlighted %>
10
9
  <%= render :voted %>
11
10
  </div>
12
11
  <% end %>
13
12
 
14
13
  <div class="row">
15
- <div class="columns mediumlarge-6">
14
+ <div class="columns mediumlarge-12">
16
15
  <%= render :card_list %>
17
16
  </div>
18
17
  </div>
@@ -4,6 +4,12 @@
4
4
  <%= t(:voted_on, scope: i18n_scope, links: budgets_link_list(voted)) %>
5
5
  </p>
6
6
 
7
+ <div id="voted-budgets" class="card card--list budget-list">
8
+ <% voted.each do |budget| %>
9
+ <%= cell("decidim/budgets/budget_list_item", budget) %>
10
+ <% end %>
11
+ </div>
12
+
7
13
  <% if finished? %>
8
14
  <p class="lead">
9
15
  <%= t(:finished_message, scope: i18n_scope) %>
@@ -14,6 +14,6 @@
14
14
  <%= cell("decidim/budgets/project_voted_hint", model, class: "display-block margin-top-1") if current_order_checked_out? && resource_added? %>
15
15
  </span>
16
16
 
17
- <%= render :project_data_vote_button if !current_order_checked_out? && voting_open? %>
17
+ <%= cell("decidim/budgets/project_vote_button", model) if !current_order_checked_out? && voting_open? %>
18
18
  <% end %>
19
19
  </div>
@@ -3,6 +3,7 @@
3
3
  method: vote_button_method,
4
4
  remote: true,
5
5
  class: "button tiny budget-list__action #{vote_button_class}",
6
+ id: "project-vote-button-#{model.id}",
6
7
  data: {
7
8
  add: !resource_added?,
8
9
  disable: true,
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Budgets
5
+ # This cell renders an authorized_action button
6
+ # to vote a given instance of a Project in a budget list
7
+ class ProjectVoteButtonCell < ProjectListItemCell
8
+ end
9
+ end
10
+ end
@@ -23,7 +23,7 @@ module Decidim
23
23
  # Returns nothing.
24
24
  def call
25
25
  transaction do
26
- return broadcast(:invalid) if voting_not_enabled? || order.checked_out?
26
+ return broadcast(:invalid) if voting_not_enabled? || order.checked_out? || exceeds_budget?
27
27
 
28
28
  add_line_item
29
29
  broadcast(:ok, order)
@@ -44,6 +44,10 @@ module Decidim
44
44
  end
45
45
  end
46
46
 
47
+ def exceeds_budget?
48
+ order.allocation_for(project) + order.total > order.available_allocation
49
+ end
50
+
47
51
  def voting_not_enabled?
48
52
  project.component.current_settings.votes != "enabled"
49
53
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Decidim
6
+ module Budgets
7
+ module Admin
8
+ module Filterable
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ include Decidim::Admin::Filterable
13
+
14
+ helper Decidim::Budgets::Admin::FilterableHelper
15
+
16
+ private
17
+
18
+ def base_query
19
+ collection
20
+ end
21
+
22
+ def search_field_predicate
23
+ :id_string_or_title_cont
24
+ end
25
+
26
+ def filters
27
+ [
28
+ :scope_id_eq,
29
+ :category_id_eq,
30
+ :selected_at_null
31
+ ]
32
+ end
33
+
34
+ def filters_with_values
35
+ {
36
+ scope_id_eq: scope_ids_hash(scopes.top_level),
37
+ category_id_eq: category_ids_hash(categories.first_class),
38
+ selected_at_null: [true, false]
39
+ }
40
+ end
41
+
42
+ # Can't user `super` here, because it does not belong to a superclass
43
+ # but to a concern.
44
+ def dynamically_translated_filters
45
+ [:scope_id_eq, :category_id_eq]
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -7,9 +7,14 @@ module Decidim
7
7
  class ProjectsController < Admin::ApplicationController
8
8
  include Decidim::ApplicationHelper
9
9
  include Decidim::Proposals::Admin::Picker if Decidim::Budgets.enable_proposal_linking
10
+ include Decidim::Budgets::Admin::Filterable
10
11
 
11
12
  helper_method :projects, :finished_orders, :pending_orders, :present
12
13
 
14
+ def collection
15
+ @collection ||= budget.projects.page(params[:page]).per(15)
16
+ end
17
+
13
18
  def new
14
19
  enforce_permission_to :create, :project
15
20
  @form = form(ProjectForm).from_params(
@@ -72,7 +77,7 @@ module Decidim
72
77
  private
73
78
 
74
79
  def projects
75
- @projects ||= budget.projects.page(params[:page]).per(15)
80
+ @projects ||= filtered_collection
76
81
  end
77
82
 
78
83
  def orders
@@ -20,7 +20,7 @@ module Decidim
20
20
  end
21
21
 
22
22
  on(:invalid) do
23
- render nothing: true, status: :unprocessable_entity
23
+ format.js { render "update_budget", status: :unprocessable_entity }
24
24
  end
25
25
  end
26
26
  end
@@ -35,7 +35,7 @@ module Decidim
35
35
  end
36
36
 
37
37
  on(:invalid) do
38
- render nothing: true, status: :unprocessable_entity
38
+ format.js { render "update_budget", status: :unprocessable_entity }
39
39
  end
40
40
  end
41
41
  end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Budgets
5
+ module Admin
6
+ module FilterableHelper
7
+ end
8
+ end
9
+ end
10
+ end
@@ -46,7 +46,8 @@ module Decidim
46
46
  # delimiter. Otherwise e.g. ID 2 would match ID "26" in the original
47
47
  # array. This is why we search for match ",2," instead to get the actual
48
48
  # position for ID 2.
49
- order(Arel.sql("position(concat(',', id::text, ',') in ',#{ids.join(",")},')"))
49
+ concat_ids = connection.quote(",#{ids.join(",")},")
50
+ order(Arel.sql("position(concat(',', id::text, ',') in #{concat_ids})"))
50
51
  end
51
52
 
52
53
  def self.log_presenter_class_for(_log)
@@ -92,6 +93,32 @@ module Decidim
92
93
  def attachment_context
93
94
  :admin
94
95
  end
96
+
97
+ ransacker :id_string do
98
+ Arel.sql(%{cast("decidim_budgets_projects"."id" as text)})
99
+ end
100
+
101
+ # Allow ransacker to search for a key in a hstore column (`title`.`en`)
102
+ ransacker :title do |parent|
103
+ Arel::Nodes::InfixOperation.new("->>", parent.table[:title], Arel::Nodes.build_quoted(I18n.locale.to_s))
104
+ end
105
+
106
+ ransacker :selected do
107
+ Arel.sql(%{("decidim_budgets_projects"."selected_at")::text})
108
+ end
109
+
110
+ ransacker :confirmed_orders_count do
111
+ query = <<-SQL.squish
112
+ (
113
+ SELECT COUNT(decidim_budgets_line_items.decidim_order_id)
114
+ FROM decidim_budgets_line_items
115
+ LEFT JOIN decidim_budgets_orders ON decidim_budgets_orders.id = decidim_budgets_line_items.decidim_order_id
116
+ WHERE decidim_budgets_orders.checked_out_at IS NOT NULL
117
+ AND decidim_budgets_projects.id = decidim_budgets_line_items.decidim_project_id
118
+ )
119
+ SQL
120
+ Arel.sql(query)
121
+ end
95
122
  end
96
123
  end
97
124
  end
@@ -1,5 +1,6 @@
1
1
  import "src/decidim/budgets/projects"
2
2
  import "src/decidim/budgets/progressFixed"
3
+ import "src/decidim/budgets/exit_handler"
3
4
 
4
5
  // Images
5
6
  require.context("../images", true)
@@ -0,0 +1,131 @@
1
+ const currentAllocationZero = () => {
2
+ const $budgetSummary = $(".budget-summary__progressbox");
3
+ return parseInt($budgetSummary.attr("data-current-allocation"), 10) === 0;
4
+ }
5
+
6
+ const isSafeUrl = (exitUrl) => {
7
+ if (!exitUrl) {
8
+ return false
9
+ }
10
+
11
+ const safeUrls = [
12
+ $(".budget-summary").attr("data-safe-url").split("?")[0],
13
+ `${location.pathname}#`,
14
+ `${location.href}#`,
15
+ "#"
16
+ ];
17
+
18
+ let safe = false;
19
+ safeUrls.forEach((url) => {
20
+ if (exitUrl.startsWith(url)) {
21
+ safe = true
22
+ }
23
+ });
24
+
25
+ return safe;
26
+ }
27
+
28
+ const allowExitFrom = ($el) => {
29
+ if (currentAllocationZero()) {
30
+ return true
31
+ } else if ($el.attr("target") === "_blank") {
32
+ return true;
33
+ } else if ($el.parents("#loginModal").length > 0) {
34
+ return true;
35
+ } else if ($el.parents("#authorizationModal").length > 0) {
36
+ return true;
37
+ } else if ($el.attr("id") === "exit-notification-link") {
38
+ return true;
39
+ } else if ($el.parents(".voting-wrapper").length > 0) {
40
+ return true;
41
+ } else if (isSafeUrl($el.attr("href"))) {
42
+ return true
43
+ }
44
+
45
+ return false;
46
+ }
47
+
48
+ // Don't show browser's default confirmation panel when visit current_path is called in tests.
49
+ const testReload = (initialLocation) => {
50
+ if (navigator && navigator.webdriver && initialLocation === location.href) {
51
+ return true;
52
+ }
53
+
54
+ return false;
55
+ }
56
+
57
+ $(() => {
58
+ const $exitNotification = $("#exit-notification");
59
+ const $exitLink = $("#exit-notification-link");
60
+ const defaultExitUrl = $exitLink.attr("href");
61
+ const defaultExitLinkText = $exitLink.text();
62
+ let exitLinkText = defaultExitLinkText;
63
+ const initialLocation = location.href;
64
+
65
+ if ($exitNotification.length < 1) {
66
+ // Do not apply when not inside the voting pipeline
67
+ return;
68
+ }
69
+
70
+ const openExitNotification = (url, method = null) => {
71
+ if (method && method !== "get") {
72
+ $exitLink.attr("data-method", method);
73
+ } else {
74
+ $exitLink.removeAttr("data-method");
75
+ }
76
+
77
+ $exitLink.attr("href", url);
78
+ $exitLink.html(exitLinkText);
79
+ $exitNotification.foundation("open");
80
+ };
81
+
82
+ // Handle "beforeunload"
83
+ window.allowExit = false;
84
+ $(document).on("click", "a", (event) => {
85
+ exitLinkText = defaultExitLinkText;
86
+ window.allowExit = false;
87
+
88
+ const $link = $(event.currentTarget);
89
+ if (allowExitFrom($link)) {
90
+ window.allowExit = true;
91
+ } else {
92
+ event.preventDefault();
93
+ openExitNotification($link.attr("href"), $link.data("method"));
94
+ }
95
+ });
96
+ // Custom handling for the header sign out so that it won't trigger the
97
+ // logout form submit and so that it changes the exit link text. This does
98
+ // not trigger the document link click listener because it has the
99
+ // data-method attribute to trigger a form submit event.
100
+ $(".header a.sign-out-link").on("click", (event) => {
101
+ event.preventDefault();
102
+ event.stopPropagation();
103
+
104
+ const $link = $(event.currentTarget);
105
+ exitLinkText = $link.text();
106
+ openExitNotification($link.attr("href"), $link.data("method"));
107
+ });
108
+ // Custom handling for the exit link which needs to change the exit link
109
+ // text to the default text as this is not handled by the document click
110
+ // listener.
111
+ $("a[data-open='exit-notification']").on("click", () => {
112
+ exitLinkText = defaultExitLinkText;
113
+ openExitNotification(defaultExitUrl);
114
+ });
115
+ // Allow all form submits on the page, including language change and sign
116
+ // out form (when triggered by the exit link click).
117
+ $(document).on("submit", "form", () => {
118
+ window.allowExit = true;
119
+ });
120
+
121
+ window.addEventListener("beforeunload", (event) => {
122
+ const allowExit = window.allowExit;
123
+ window.allowExit = false;
124
+
125
+ if (allowExit || testReload(initialLocation)) {
126
+ return;
127
+ }
128
+
129
+ event.returnValue = true;
130
+ });
131
+ });
@@ -3,28 +3,28 @@ $(() => {
3
3
  const $budgetSummaryTotal = $(".budget-summary__total");
4
4
  const $budgetExceedModal = $("#budget-excess");
5
5
  const $budgetSummary = $(".budget-summary__progressbox");
6
+ const $voteButton = $(".budget-vote-button");
6
7
  const totalAllocation = parseInt($budgetSummaryTotal.attr("data-total-allocation"), 10);
7
8
 
8
9
  const cancelEvent = (event) => {
10
+ $(event.currentTarget).removeClass("loading-spinner");
9
11
  event.stopPropagation();
10
12
  event.preventDefault();
11
13
  };
12
14
 
13
- const allowExitFrom = ($el) => {
14
- if ($el.parents("#loginModal").length > 0) {
15
- return true;
16
- } else if ($el.parents("#authorizationModal").length > 0) {
17
- return true;
18
- }
19
-
20
- return false;
21
- }
15
+ $voteButton.on("click", "span", () => {
16
+ $(".budget-list__action").click();
17
+ });
22
18
 
23
19
  $projects.on("click", ".budget-list__action", (event) => {
24
20
  const currentAllocation = parseInt($budgetSummary.attr("data-current-allocation"), 10);
25
21
  const $currentTarget = $(event.currentTarget);
26
22
  const projectAllocation = parseInt($currentTarget.attr("data-allocation"), 10);
27
23
 
24
+ if (!$currentTarget.attr("data-open")) {
25
+ $currentTarget.addClass("loading-spinner");
26
+ }
27
+
28
28
  if ($currentTarget.attr("disabled")) {
29
29
  cancelEvent(event);
30
30
  } else if (($currentTarget.attr("data-add") === "true") && ((currentAllocation + projectAllocation) > totalAllocation)) {
@@ -32,34 +32,4 @@ $(() => {
32
32
  cancelEvent(event);
33
33
  }
34
34
  });
35
-
36
- if ($("#order-progress [data-toggle=budget-confirm]").length > 0) {
37
- const safeUrl = $(".budget-summary").attr("data-safe-url").split("?")[0];
38
- $(document).on("click", "a", (event) => {
39
- if (allowExitFrom($(event.currentTarget))) {
40
- window.exitUrl = null;
41
- } else {
42
- window.exitUrl = event.currentTarget.href;
43
- }
44
- });
45
- $(document).on("submit", "form", (event) => {
46
- if (allowExitFrom($(event.currentTarget))) {
47
- window.exitUrl = null;
48
- } else {
49
- window.exitUrl = event.currentTarget.action;
50
- }
51
- });
52
-
53
- window.addEventListener("beforeunload", (event) => {
54
- const currentAllocation = parseInt($budgetSummary.attr("data-current-allocation"), 10);
55
- const exitUrl = window.exitUrl;
56
- window.exitUrl = null;
57
-
58
- if (currentAllocation === 0 || (exitUrl && exitUrl.startsWith(safeUrl))) {
59
- return;
60
- }
61
-
62
- event.returnValue = true;
63
- });
64
- }
65
35
  });