decidim-budgets 0.25.0.rc4 → 0.26.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) 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 +42 -39
  29. data/config/locales/ca.yml +4 -0
  30. data/config/locales/cs.yml +24 -0
  31. data/config/locales/de.yml +10 -0
  32. data/config/locales/el.yml +141 -1
  33. data/config/locales/en.yml +23 -0
  34. data/config/locales/es-MX.yml +1 -0
  35. data/config/locales/es-PY.yml +1 -0
  36. data/config/locales/es.yml +24 -0
  37. data/config/locales/eu.yml +166 -0
  38. data/config/locales/fi-plain.yml +24 -0
  39. data/config/locales/fi.yml +24 -0
  40. data/config/locales/fr-CA.yml +19 -0
  41. data/config/locales/fr.yml +19 -0
  42. data/config/locales/ga-IE.yml +91 -0
  43. data/config/locales/gl.yml +9 -0
  44. data/config/locales/it.yml +10 -0
  45. data/config/locales/ja.yml +29 -5
  46. data/config/locales/lb-LU.yml +294 -0
  47. data/config/locales/lb.yml +295 -0
  48. data/config/locales/nl.yml +18 -0
  49. data/config/locales/pl.yml +7 -0
  50. data/config/locales/pt-BR.yml +1 -1
  51. data/config/locales/pt.yml +143 -0
  52. data/config/locales/ro-RO.yml +127 -49
  53. data/config/locales/sv.yml +4 -0
  54. data/config/locales/val-ES.yml +1 -0
  55. data/lib/decidim/budgets/engine.rb +4 -0
  56. data/lib/decidim/budgets/version.rb +1 -1
  57. metadata +25 -18
  58. 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: af519441e57d15b03ff4417ee9df831767dc0f80a01feca4196afc7c983fb980
4
- data.tar.gz: 36f02bfd2ff21ba0775e41e2ff823fa3530c109efe161931fdab0a708189b337
3
+ metadata.gz: 5faa45ba2c53c8c620e02d864b28790d7a776a82aa4650fdf6e5bfa36353adb3
4
+ data.tar.gz: d4698f56fef1acacb46f2f37f0eea903c543814d063b6473e7ebfa5677113fac
5
5
  SHA512:
6
- metadata.gz: f148768aec3553952fbaad64046098274a59d3410b0c3c7089c7cb22d94d8f42e32f2fdabba09b0cacef36f8bc84e32830639a05946f4b6b1a57782b305942bc
7
- data.tar.gz: d3519140e34cffe360f4c6db63fdfb26ec4494603b8fbc8ce04300ae7a9ce9bb9b35132ff308972eb87773b5d6497bcf090c35167998ab04dd1d29f8e8cbb5f5
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
  });