decidim-budgets 0.26.8 → 0.27.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/budgets/budgets_header/show.erb +1 -1
  3. data/app/cells/decidim/budgets/order_activity_cell.rb +29 -0
  4. data/app/commands/decidim/budgets/add_line_item.rb +4 -2
  5. data/app/commands/decidim/budgets/admin/create_budget.rb +1 -1
  6. data/app/commands/decidim/budgets/admin/create_order_reminders.rb +66 -0
  7. data/app/commands/decidim/budgets/admin/create_project.rb +5 -2
  8. data/app/commands/decidim/budgets/admin/destroy_budget.rb +1 -1
  9. data/app/commands/decidim/budgets/admin/destroy_project.rb +1 -1
  10. data/app/commands/decidim/budgets/admin/import_proposals_to_budgets.rb +6 -6
  11. data/app/commands/decidim/budgets/admin/update_budget.rb +1 -1
  12. data/app/commands/decidim/budgets/admin/update_project.rb +5 -2
  13. data/app/commands/decidim/budgets/admin/update_project_category.rb +48 -0
  14. data/app/commands/decidim/budgets/admin/update_project_scope.rb +54 -0
  15. data/app/commands/decidim/budgets/admin/update_project_selection.rb +56 -0
  16. data/app/commands/decidim/budgets/cancel_order.rb +1 -1
  17. data/app/commands/decidim/budgets/checkout.rb +10 -3
  18. data/app/commands/decidim/budgets/remove_line_item.rb +1 -1
  19. data/app/controllers/decidim/budgets/admin/application_controller.rb +2 -0
  20. data/app/controllers/decidim/budgets/admin/attachment_collections_controller.rb +1 -3
  21. data/app/controllers/decidim/budgets/admin/attachments_controller.rb +1 -7
  22. data/app/controllers/decidim/budgets/admin/budgets_controller.rb +1 -1
  23. data/app/controllers/decidim/budgets/admin/projects_controller.rb +149 -1
  24. data/app/controllers/decidim/budgets/line_items_controller.rb +2 -2
  25. data/app/controllers/decidim/budgets/orders_controller.rb +1 -1
  26. data/app/controllers/decidim/budgets/projects_controller.rb +15 -19
  27. data/app/forms/decidim/budgets/admin/budget_form.rb +3 -4
  28. data/app/forms/decidim/budgets/admin/order_reminder_form.rb +75 -0
  29. data/app/forms/decidim/budgets/admin/project_form.rb +19 -3
  30. data/app/helpers/decidim/budgets/admin/project_bulk_actions_helper.rb +20 -0
  31. data/app/helpers/decidim/budgets/projects_helper.rb +29 -0
  32. data/app/jobs/decidim/budgets/send_vote_reminder_job.rb +16 -0
  33. data/app/mailers/decidim/budgets/vote_reminder_mailer.rb +39 -0
  34. data/app/models/decidim/budgets/order.rb +2 -4
  35. data/app/models/decidim/budgets/project.rb +12 -4
  36. data/app/packs/entrypoints/decidim_budgets_admin.js +1 -0
  37. data/app/packs/src/decidim/budgets/admin/projects.js +143 -0
  38. data/app/permissions/decidim/budgets/admin/permissions.rb +8 -3
  39. data/app/queries/decidim/budgets/filtered_projects.rb +1 -1
  40. data/app/serializers/decidim/budgets/{data_portability_budgets_order_serializer.rb → download_your_data_budgets_order_serializer.rb} +2 -2
  41. data/app/services/decidim/budgets/order_reminder_generator.rb +85 -0
  42. data/app/views/decidim/budgets/admin/budgets/_form.html.erb +1 -1
  43. data/app/views/decidim/budgets/admin/budgets/edit.html.erb +0 -1
  44. data/app/views/decidim/budgets/admin/budgets/index.html.erb +3 -1
  45. data/app/views/decidim/budgets/admin/budgets/new.html.erb +0 -1
  46. data/app/views/decidim/budgets/admin/projects/_bulk-actions.html.erb +13 -0
  47. data/app/views/decidim/budgets/admin/projects/_form.html.erb +6 -0
  48. data/app/views/decidim/budgets/admin/projects/_project-tr.html.erb +50 -0
  49. data/app/views/decidim/budgets/admin/projects/bulk_actions/_change-selected.html.erb +15 -0
  50. data/app/views/decidim/budgets/admin/projects/bulk_actions/_dropdown.html.erb +36 -0
  51. data/app/views/decidim/budgets/admin/projects/bulk_actions/_recategorize.html.erb +15 -0
  52. data/app/views/decidim/budgets/admin/projects/bulk_actions/_scope-change.html.erb +25 -0
  53. data/app/views/decidim/budgets/admin/projects/edit.html.erb +0 -1
  54. data/app/views/decidim/budgets/admin/projects/index.html.erb +6 -55
  55. data/app/views/decidim/budgets/admin/projects/new.html.erb +0 -1
  56. data/app/views/decidim/budgets/admin/projects/update_attribute.js.erb +26 -0
  57. data/app/views/decidim/budgets/admin/proposals_imports/new.html.erb +0 -1
  58. data/app/views/decidim/budgets/order_summary_mailer/order_summary.html.erb +0 -1
  59. data/app/views/decidim/budgets/projects/_filters.html.erb +4 -4
  60. data/app/views/decidim/budgets/projects/index.html.erb +28 -1
  61. data/app/views/decidim/budgets/projects/show.html.erb +7 -2
  62. data/app/views/decidim/budgets/vote_reminder_mailer/vote_reminder.html.erb +21 -0
  63. data/config/assets.rb +2 -1
  64. data/config/locales/am-ET.yml +1 -0
  65. data/config/locales/ar.yml +5 -1
  66. data/config/locales/bg.yml +1 -0
  67. data/config/locales/ca.yml +48 -2
  68. data/config/locales/cs.yml +53 -3
  69. data/config/locales/da.yml +1 -0
  70. data/config/locales/de.yml +48 -11
  71. data/config/locales/el.yml +3 -33
  72. data/config/locales/en.yml +46 -1
  73. data/config/locales/eo.yml +1 -0
  74. data/config/locales/es-MX.yml +46 -3
  75. data/config/locales/es-PY.yml +49 -3
  76. data/config/locales/es.yml +50 -4
  77. data/config/locales/et.yml +1 -0
  78. data/config/locales/eu.yml +7 -12
  79. data/config/locales/fi-plain.yml +47 -1
  80. data/config/locales/fi.yml +93 -47
  81. data/config/locales/fr-CA.yml +47 -2
  82. data/config/locales/fr.yml +48 -3
  83. data/config/locales/ga-IE.yml +2 -1
  84. data/config/locales/gl.yml +11 -0
  85. data/config/locales/hr.yml +1 -0
  86. data/config/locales/hu.yml +21 -149
  87. data/config/locales/id-ID.yml +2 -1
  88. data/config/locales/is-IS.yml +4 -2
  89. data/config/locales/it.yml +4 -2
  90. data/config/locales/ja.yml +44 -3
  91. data/config/locales/ko.yml +1 -0
  92. data/config/locales/lb.yml +5 -5
  93. data/config/locales/lt.yml +1 -337
  94. data/config/locales/lv.yml +2 -1
  95. data/config/locales/mt.yml +1 -0
  96. data/config/locales/nl.yml +16 -30
  97. data/config/locales/no.yml +18 -1
  98. data/config/locales/om-ET.yml +1 -0
  99. data/config/locales/pl.yml +3 -3
  100. data/config/locales/pt-BR.yml +4 -19
  101. data/config/locales/pt.yml +3 -3
  102. data/config/locales/ro-RO.yml +2 -3
  103. data/config/locales/ru.yml +2 -1
  104. data/config/locales/si-LK.yml +1 -0
  105. data/config/locales/sk.yml +2 -1
  106. data/config/locales/sl.yml +1 -0
  107. data/config/locales/so-SO.yml +1 -0
  108. data/config/locales/sr-CS.yml +1 -0
  109. data/config/locales/sv.yml +25 -5
  110. data/config/locales/sw-KE.yml +1 -0
  111. data/config/locales/ti-ER.yml +1 -0
  112. data/config/locales/tr-TR.yml +3 -3
  113. data/config/locales/uk.yml +3 -2
  114. data/config/locales/val-ES.yml +1 -0
  115. data/config/locales/vi.yml +1 -0
  116. data/config/locales/zh-CN.yml +1 -3
  117. data/config/locales/zh-TW.yml +1 -326
  118. data/db/migrate/20200804175222_votes_enabled_to_votes_choices.rb +2 -2
  119. data/db/migrate/20220428072638_add_geolocalization_fields_to_projects.rb +9 -0
  120. data/lib/decidim/budgets/admin_engine.rb +3 -0
  121. data/lib/decidim/budgets/component.rb +7 -6
  122. data/lib/decidim/budgets/engine.rb +17 -0
  123. data/lib/decidim/budgets/test/factories.rb +11 -0
  124. data/lib/decidim/budgets/version.rb +1 -1
  125. data/lib/decidim/budgets/workflows/all.rb +1 -1
  126. data/lib/decidim/budgets/workflows/base.rb +1 -1
  127. metadata +38 -25
  128. data/app/services/decidim/budgets/project_search.rb +0 -45
  129. data/config/environment.rb +0 -0
  130. data/config/locales/fa-IR.yml +0 -1
  131. data/config/locales/gn-PY.yml +0 -1
  132. data/config/locales/ka-GE.yml +0 -1
  133. data/config/locales/kaa.yml +0 -11
  134. data/config/locales/lo-LA.yml +0 -1
  135. data/config/locales/oc-FR.yml +0 -1
@@ -0,0 +1,143 @@
1
+ /* eslint-disable no-invalid-this */
2
+ $(() => {
3
+ const selectedResourcesCount = () => {
4
+ return $(".table-list .js-check-all-resources:checked").length
5
+ }
6
+
7
+ const selectedResourcesNotPublishedAnswerCount = () => {
8
+ return $(".table-list [data-published-state=false] .js-check-all-resources:checked").length
9
+ }
10
+
11
+ const selectedResourcesCountUpdate = () => {
12
+ const selectedResources = selectedResourcesCount();
13
+ const selectedResourcesNotPublishedAnswer = selectedResourcesNotPublishedAnswerCount();
14
+
15
+ if (selectedResources === 0) {
16
+ $("#js-selected-resources-count").text("")
17
+ } else {
18
+ $("#js-selected-resources-count").text(selectedResources);
19
+ }
20
+
21
+ if (selectedResources >= 2) {
22
+ $('button[data-action="merge-resources"]').parent().show();
23
+ } else {
24
+ $('button[data-action="merge-resources"]').parent().hide();
25
+ }
26
+
27
+ if (selectedResourcesNotPublishedAnswer > 0) {
28
+ $('button[data-action="publish-answers"]').parent().show();
29
+ $("#js-form-publish-answers-number").text(selectedResourcesNotPublishedAnswer);
30
+ } else {
31
+ $('button[data-action="publish-answers"]').parent().hide();
32
+ }
33
+ }
34
+
35
+ const showBulkActionsButton = () => {
36
+ if (selectedResourcesCount() > 0) {
37
+ $("#js-bulk-actions-button").removeClass("hide");
38
+ }
39
+ }
40
+
41
+ const hideBulkActionsButton = (force = false) => {
42
+ if (selectedResourcesCount() === 0 || force === true) {
43
+ $("#js-bulk-actions-button").addClass("hide");
44
+ $("#js-bulk-actions-dropdown").removeClass("is-open");
45
+ }
46
+ }
47
+
48
+ const showOtherActionsButtons = () => {
49
+ $("#js-other-actions-wrapper").removeClass("hide");
50
+ }
51
+
52
+ const hideOtherActionsButtons = () => {
53
+ $("#js-other-actions-wrapper").addClass("hide");
54
+ }
55
+
56
+ const hideBulkActionForms = () => {
57
+ $(".js-bulk-action-form").addClass("hide");
58
+ }
59
+
60
+ if ($("#js-bulk-actions-wrapper").length === 0) {
61
+ return;
62
+ }
63
+
64
+ // Expose functions to make them available in .js.erb templates
65
+ window.hideBulkActionForms = hideBulkActionForms;
66
+ window.hideBulkActionsButton = hideBulkActionsButton;
67
+ window.showOtherActionsButtons = showOtherActionsButtons;
68
+ window.selectedResourcesCountUpdate = selectedResourcesCountUpdate;
69
+
70
+
71
+ if ($(".js-bulk-action-form").length) {
72
+ hideBulkActionForms();
73
+ $("#js-bulk-actions-button").addClass("hide");
74
+
75
+ $("#js-bulk-actions-dropdown ul li button").on("click", (event) => {
76
+ event.preventDefault();
77
+ let action = $(event.target).data("action");
78
+
79
+ if (action) {
80
+ $(`#js-form-${action}`).on("submit", () => {
81
+ $(".layout-content > .callout-wrapper").html("");
82
+ })
83
+
84
+ $(`#js-${action}-actions`).removeClass("hide");
85
+ hideBulkActionsButton(true);
86
+ hideOtherActionsButtons();
87
+ }
88
+ })
89
+
90
+ // select all checkboxes
91
+ $(".js-check-all").on("change", function() {
92
+ $(".js-check-all-resources").prop("checked", $(this).prop("checked"));
93
+
94
+ if ($(this).prop("checked")) {
95
+ $(".js-check-all-resources").closest("tr").addClass("selected");
96
+ showBulkActionsButton();
97
+ } else {
98
+ $(".js-check-all-resources").closest("tr").removeClass("selected");
99
+ hideBulkActionsButton();
100
+ }
101
+
102
+ selectedResourcesCountUpdate();
103
+ });
104
+
105
+ // resource checkbox change
106
+ $(".table-list").on("change", ".js-check-all-resources", function() {
107
+ let resourceId = $(this).val()
108
+ let checked = $(this).prop("checked")
109
+
110
+ // uncheck "select all", if one of the listed checkbox item is unchecked
111
+ if ($(this).prop("checked") === false) {
112
+ $(".js-check-all").prop("checked", false);
113
+ }
114
+ // check "select all" if all checkbox resources are checked
115
+ if ($(".js-check-all-resources:checked").length === $(".js-check-all-resources").length) {
116
+ $(".js-check-all").prop("checked", true);
117
+ showBulkActionsButton();
118
+ }
119
+
120
+ if ($(this).prop("checked")) {
121
+ showBulkActionsButton();
122
+ $(this).closest("tr").addClass("selected");
123
+ } else {
124
+ hideBulkActionsButton();
125
+ $(this).closest("tr").removeClass("selected");
126
+ }
127
+
128
+ if ($(".js-check-all-resources:checked").length === 0) {
129
+ hideBulkActionsButton();
130
+ }
131
+
132
+ $(".js-bulk-action-form").find(`.js-resource-id-${resourceId}`).prop("checked", checked);
133
+ selectedResourcesCountUpdate();
134
+ });
135
+
136
+ $(".js-cancel-bulk-action").on("click", () => {
137
+ hideBulkActionForms()
138
+ showBulkActionsButton();
139
+ showOtherActionsButtons();
140
+ });
141
+ }
142
+ });
143
+ /* eslint-enable no-invalid-this */
@@ -19,13 +19,18 @@ module Decidim
19
19
  end
20
20
  when :project, :projects
21
21
  case permission_action.action
22
- when :create
23
- permission_action.allow!
24
- when :import_proposals
22
+ when :create, :import_proposals, :project_category
25
23
  permission_action.allow!
26
24
  when :update, :destroy
27
25
  permission_action.allow! if project.present?
28
26
  end
27
+ when :order
28
+ case permission_action.action
29
+ when :remind
30
+ permission_action.allow!
31
+ end
32
+ when :project_category, :project_scope, :project_selected
33
+ permission_action.allow!
29
34
  end
30
35
 
31
36
  permission_action
@@ -3,7 +3,7 @@
3
3
  module Decidim
4
4
  module Budgets
5
5
  # A class used to find projects filtered by components and a date range
6
- class FilteredProjects < Rectify::Query
6
+ class FilteredProjects < Decidim::Query
7
7
  # Syntactic sugar to initialize the class and return the queried objects.
8
8
  #
9
9
  # components - An array of Decidim::Component
@@ -2,13 +2,13 @@
2
2
 
3
3
  module Decidim
4
4
  module Budgets
5
- class DataPortabilityBudgetsOrderSerializer < Decidim::Exporters::Serializer
5
+ class DownloadYourDataBudgetsOrderSerializer < Decidim::Exporters::Serializer
6
6
  # Public: Initializes the serializer with a conversation.
7
7
  def initialize(order)
8
8
  @order = order
9
9
  end
10
10
 
11
- # Serializes a Debate for data portability
11
+ # Serializes a Debate for download your data
12
12
  def serialize
13
13
  {
14
14
  id: order.id,
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Budgets
5
+ # This class is the generator class which creates and updates order related reminders,
6
+ # after reminder is generated it is send to user who have not checked out his/her/their vote.
7
+ class OrderReminderGenerator
8
+ attr_reader :reminder_jobs_queued
9
+
10
+ def initialize
11
+ @reminder_manifest = Decidim.reminders_registry.for(:orders)
12
+ @reminder_jobs_queued = 0
13
+ end
14
+
15
+ # Creates reminders and updates them if they already exists.
16
+ def generate
17
+ Decidim::Component.where(manifest_name: "budgets").each do |component|
18
+ next if component.current_settings.votes != "enabled"
19
+
20
+ send_reminders(component)
21
+ end
22
+ end
23
+
24
+ def generate_for(component, &block)
25
+ @alternative_refresh_state = block
26
+ send_reminders(component)
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :reminder_manifest
32
+
33
+ def send_reminders(component)
34
+ budgets = Decidim::Budgets::Budget.where(component: component)
35
+ pending_orders = Decidim::Budgets::Order.where(budget: budgets, checked_out_at: nil)
36
+ users = Decidim::User.where(id: pending_orders.pluck(:decidim_user_id).uniq)
37
+ users.each do |user|
38
+ reminder = Decidim::Reminder.find_or_create_by(user: user, component: component)
39
+ users_pending_orders = pending_orders.where(user: user)
40
+ update_reminder_records(reminder, users_pending_orders)
41
+ if reminder.records.active.any?
42
+ Decidim::Budgets::SendVoteReminderJob.perform_later(reminder)
43
+ @reminder_jobs_queued += 1
44
+ end
45
+ end
46
+ end
47
+
48
+ def update_reminder_records(reminder, users_pending_orders)
49
+ clean_checked_out_and_deleted_orders(reminder)
50
+ add_pending_orders(reminder, users_pending_orders)
51
+ end
52
+
53
+ def clean_checked_out_and_deleted_orders(reminder)
54
+ reminder.records.each do |record|
55
+ if record.remindable.nil?
56
+ record.update(state: "deleted")
57
+ elsif record.remindable.checked_out_at.present?
58
+ record.update(state: "completed")
59
+ end
60
+ end
61
+ end
62
+
63
+ def add_pending_orders(reminder, users_pending_orders)
64
+ reminder.records << users_pending_orders.map { |order| Decidim::ReminderRecord.find_or_create_by(reminder: reminder, remindable: order) }
65
+ return @alternative_refresh_state.call(reminder) if @alternative_refresh_state.present?
66
+
67
+ reminder.records.each do |record|
68
+ refresh_state(record, reminder.deliveries.length) if %w(active pending).include? record.state
69
+ end
70
+ end
71
+
72
+ def refresh_state(record, delivered_count)
73
+ intervals = Array(reminder_manifest.settings.attributes[:reminder_times].default)
74
+ return record.update(state: "pending") if delivered_count >= intervals.length
75
+
76
+ record.state = intervals[delivered_count].ago > record.remindable.created_at ? "active" : "pending"
77
+ record.save if record.changed?
78
+ end
79
+
80
+ def voting_enabled?(component)
81
+ component.current_settings.votes == "enabled"
82
+ end
83
+ end
84
+ end
85
+ end
@@ -9,7 +9,7 @@
9
9
 
10
10
  <%= form.number_field :weight %>
11
11
 
12
- <%= form.translated :editor, :description, toolbar: :content %>
12
+ <%= form.translated :editor, :description %>
13
13
 
14
14
  <%= form.number_field :total_budget %>
15
15
 
@@ -1,4 +1,3 @@
1
- <% add_decidim_page_title(t(".title")) %>
2
1
  <%= decidim_form_for(@form, html: { class: "form edit_budget" }) do |f| %>
3
2
  <%= render partial: "form", object: f, locals: { title: t(".title") } %>
4
3
 
@@ -1,4 +1,3 @@
1
- <% add_decidim_page_title(t(".title")) %>
2
1
  <div class="card with-overflow">
3
2
  <div class="card-divider">
4
3
  <h2 class="card-title flex--sbc">
@@ -6,6 +5,9 @@
6
5
  <%= t(".title") %>
7
6
  </div>
8
7
  <div class="flex--cc flex-gap--1">
8
+ <% if allowed_to? :remind, :order %>
9
+ <%= link_to "Send voting reminders", admin_reminders_path(current_component, name: "orders"), class: "button tiny button--title" %>
10
+ <% end %>
9
11
  <% if allowed_to? :export, :budget %>
10
12
  <%= export_dropdown %>
11
13
  <% end %>
@@ -1,4 +1,3 @@
1
- <% add_decidim_page_title(t(".title")) %>
2
1
  <%= decidim_form_for(@form, html: { class: "form new_budget" }) do |f| %>
3
2
  <%= render partial: "form", object: f, locals: { title: t(".title") } %>
4
3
 
@@ -0,0 +1,13 @@
1
+ <div class="flex--cc flex-gap--1">
2
+ <%= render partial: "decidim/budgets/admin/projects/bulk_actions/dropdown" %>
3
+
4
+ <%= render partial: "decidim/budgets/admin/projects/bulk_actions/recategorize" %>
5
+ <%= render partial: "decidim/budgets/admin/projects/bulk_actions/scope-change" %>
6
+ <%= render partial: "decidim/budgets/admin/projects/bulk_actions/change-selected" %>
7
+
8
+ <%= link_to t("actions.import", scope: "decidim.budgets", name: t("models.project.name", scope: "decidim.budgets.admin")), new_budget_proposals_import_path(budget), class: "button tiny button--title" if allowed_to? :import_proposals, :project %>
9
+ <% if allowed_to? :export, :budget %>
10
+ <%= export_dropdown(current_component, budget.id) %>
11
+ <% end %>
12
+ <%= link_to t("actions.new", scope: "decidim.budgets", name: t("models.project.name", scope: "decidim.budgets.admin")), new_budget_project_path, class: "button tiny button--title new" if allowed_to? :create, :project %>
13
+ </div>
@@ -18,6 +18,12 @@
18
18
  <%= form.number_field :budget_amount %>
19
19
  </div>
20
20
 
21
+ <% if @form.geocoding_enabled? %>
22
+ <div class="row column">
23
+ <%= form.geocoding_field :address %>
24
+ </div>
25
+ <% end %>
26
+
21
27
  <% if current_component.has_subscopes? %>
22
28
  <div class="row column">
23
29
  <%= scopes_picker_field form, :decidim_scope_id, root: budget.scope %>
@@ -0,0 +1,50 @@
1
+ <tr data-id="<%= project.id %>">
2
+ <td>
3
+ <%= check_box_tag "project_ids_s[]", project.id, false, class: "js-check-all-resources js-project-list-check js-resource-id-#{project.id}" %><br>
4
+ </td>
5
+ <td>
6
+ <%= project.id %><br>
7
+ </td>
8
+ <td>
9
+ <%= translated_attribute(project.title) %><br>
10
+ </td>
11
+ <td>
12
+ <% if project.category %>
13
+ <%= translated_attribute project.category.name %>
14
+ <% end %>
15
+ </td>
16
+ <%= td_resource_scope_for(project.scope) %>
17
+ <td>
18
+ <%= project.confirmed_orders_count %>
19
+ </td>
20
+ <td>
21
+ <% if project.selected? %>
22
+ <%= content_tag :strong, t("projects.index.selected", scope: "decidim.budgets.admin"), class: "text-success" %>
23
+ <% else %>
24
+ <%= content_tag :span, "x", class: "text-muted" %>
25
+ <% end %>
26
+ </td>
27
+ <td class="table-list__actions">
28
+ <%= icon_link_to "eye", resource_locator([budget, project]).path, t("actions.preview", scope: "decidim.budgets"), target: :blank, class: "action-icon--preview" %>
29
+
30
+ <% if allowed_to? :update, :project, project: project %>
31
+ <%= icon_link_to "pencil", resource_locator([budget, project]).edit, t("actions.edit", scope: "decidim.budgets"), class: "action-icon--edit" %>
32
+ <% end %>
33
+
34
+ <% if allowed_to? :update, :project, project: project %>
35
+ <%= icon_link_to "folder", project_attachment_collections_path(project), t("actions.attachment_collections", scope: "decidim.budgets"), class: "action-icon--attachment_collections" %>
36
+ <% end %>
37
+
38
+ <% if allowed_to? :update, :project, project: project %>
39
+ <%= icon_link_to "paperclip", project_attachments_path(project), t("actions.attachments", scope: "decidim.budgets"), class: "action-icon--attachments" %>
40
+ <% end %>
41
+
42
+ <%= resource_permissions_link(project) %>
43
+
44
+ <% if allowed_to? :destroy, :project, project: project %>
45
+ <%= icon_link_to "circle-x", resource_locator([budget, project]).show, t("actions.destroy", scope: "decidim.budgets"), method: :delete, class: "action-icon--remove", data: { confirm: t("actions.confirm_destroy", scope: "decidim.budgets") } %>
46
+ <% else %>
47
+ <%= icon "circle-x", class: "action-icon action-icon--disabled", role: "img", aria_label: t("actions.destroy", scope: "decidim.budgets") %>
48
+ <% end %>
49
+ </td>
50
+ </tr>
@@ -0,0 +1,15 @@
1
+ <div id="js-change-selected-projects-actions" class="hide js-bulk-action-form">
2
+ <%= form_tag(update_selected_budget_projects_path, method: :post, remote: true, id: "js-form-change-selected-projects", class: "flex--cc flex-gap--1") do %>
3
+ <div class="checkboxes hide">
4
+ <% projects.each do |project| %>
5
+ <%= check_box_tag "project_ids[]", project.id, false, class: "js-check-all-resources js-resource-id-#{project.id}" %>
6
+ <% end %>
7
+ </div>
8
+
9
+ <%= bulk_selections %>
10
+
11
+ <%= submit_tag(t("decidim.budgets.admin.projects.index.update"), id: "js-submit-edit-selected", class: "button small button--simple float-left") %>
12
+
13
+ <button id="js-cancel-edit-selected" class="button tiny clear compact js-cancel-bulk-action" type="button"><%= t("decidim.budgets.admin.projects.index.cancel") %></button>
14
+ <% end %>
15
+ </div>
@@ -0,0 +1,36 @@
1
+ <div id="js-bulk-actions-wrapper">
2
+ <button
3
+ id="js-bulk-actions-button"
4
+ class="button button--nomargin tiny success light dropdown hide"
5
+ type="button"
6
+ data-toggle="js-bulk-actions-dropdown">
7
+ <%= t("decidim.budgets.admin.projects.index.actions") %>
8
+ </button>
9
+
10
+ <div
11
+ id="js-bulk-actions-dropdown"
12
+ class="dropdown-pane"
13
+ data-position="bottom"
14
+ data-alignment="right"
15
+ data-dropdown="dropdown"
16
+ data-auto-focus="false"
17
+ data-events="resize">
18
+ <ul class="list-reset">
19
+ <li>
20
+ <button type="button" data-action="recategorize-projects">
21
+ <%= t("decidim.budgets.admin.projects.index.change_category") %>
22
+ </button>
23
+ </li>
24
+ <li>
25
+ <button type="button" data-action="scope-change-projects">
26
+ <%= t("decidim.budgets.admin.projects.index.change_scope") %>
27
+ </button>
28
+ </li>
29
+ <li>
30
+ <button type="button" data-action="change-selected-projects">
31
+ <%= t("decidim.budgets.admin.projects.index.change_selected") %>
32
+ </button>
33
+ </li>
34
+ </ul>
35
+ </div>
36
+ </div>
@@ -0,0 +1,15 @@
1
+ <div id="js-recategorize-projects-actions" class="hide js-bulk-action-form">
2
+ <%= form_tag(update_category_budget_projects_path, method: :post, remote: true, id: "js-form-recategorize-projects", class: "flex--cc flex-gap--1") do %>
3
+ <div class="checkboxes hide">
4
+ <% projects.each do |project| %>
5
+ <%= check_box_tag "project_ids[]", project.id, false, class: "js-check-all-resources js-resource-id-#{project.id}" %>
6
+ <% end %>
7
+ </div>
8
+
9
+ <%= bulk_categories_select current_component.categories %>
10
+
11
+ <%= submit_tag(t("decidim.budgets.admin.projects.index.update"), id: "js-submit-edit-category", class: "button small button--simple float-left") %>
12
+
13
+ <button id="js-cancel-edit-category" class="button tiny clear compact js-cancel-bulk-action" type="button"><%= t("decidim.budgets.admin.projects.index.cancel") %></button>
14
+ <% end %>
15
+ </div>
@@ -0,0 +1,25 @@
1
+ <div id="js-scope-change-projects-actions" class="hide js-bulk-action-form">
2
+ <%= form_tag(update_scope_budget_projects_path, method: :post, remote: true, id: "js-form-scope-change-projects", class: "flex--cc flex-gap--1") do %>
3
+ <div class="checkboxes hide">
4
+ <% projects.each do |project| %>
5
+ <%= check_box_tag "project_ids[]", project.id, false, class: "js-check-all-resources js-resource-id-#{project.id}" %>
6
+ <% end %>
7
+ </div>
8
+
9
+ <%= scopes_picker_tag(
10
+ :scope_id,
11
+ try(:current_participatory_space)&.scope,
12
+ field: t("models.project.fields.scope", scope: "decidim.budgets")
13
+ ) %>
14
+
15
+ <%= submit_tag(
16
+ t("decidim.budgets.admin.projects.index.update_scope_button"),
17
+ id: "js-submit-scope-change-projects",
18
+ class: "button small button--simple float-left"
19
+ ) %>
20
+
21
+ <button id="js-cancel-scope-change-projects" class="button tiny clear compact js-cancel-bulk-action" type="button">
22
+ <%= t("decidim.budgets.admin.projects.index.cancel") %>
23
+ </button>
24
+ <% end %>
25
+ </div>
@@ -1,4 +1,3 @@
1
- <% add_decidim_page_title("#{translated_attribute(budget.title)} - #{t(".title")}") %>
2
1
  <%= decidim_form_for([budget, @form], html: { class: "form edit_project" }) do |f| %>
3
2
  <%= render partial: "form", object: f, locals: { title: "#{translated_attribute(budget.title)} &gt; #{t(".title")}" } %>
4
3
 
@@ -1,18 +1,12 @@
1
- <% add_decidim_page_title(t(".title")) %>
2
1
  <div class="card with-overflow">
3
2
  <div class="card-divider">
4
3
  <h2 class="card-title flex--sbc">
5
4
  <div>
6
5
  <%= link_to translated_attribute(budget.title), budgets_path %> &gt;
7
6
  <%= t(".title") %>
7
+ <span id="js-selected-resources-count" class="component-counter component-counter--inline" title="<%= t("decidim.budgets.admin.projects.index.selected") %>"></span>
8
8
  </div>
9
- <div class="flex--cc flex-gap--1">
10
- <%= link_to t("actions.import", scope: "decidim.budgets", name: t("models.project.name", scope: "decidim.budgets.admin")), new_budget_proposals_import_path(budget), class: "button tiny button--title" if allowed_to? :import_proposals, :project %>
11
- <% if allowed_to? :export, :budget %>
12
- <%= export_dropdown(current_component, budget.id) %>
13
- <% end %>
14
- <%= link_to t("actions.new", scope: "decidim.budgets", name: t("models.project.name", scope: "decidim.budgets.admin")), new_budget_project_path, class: "button tiny button--title new" if allowed_to? :create, :project %>
15
- </div>
9
+ <%= render partial: "bulk-actions" %>
16
10
  </h2>
17
11
  </div>
18
12
 
@@ -22,6 +16,7 @@
22
16
  <table class="table-list">
23
17
  <thead>
24
18
  <tr>
19
+ <th><%= check_box_tag "projects_bulk", "all", false, class: "js-check-all" %></th>
25
20
  <th><%= sort_link(query, :id, t("models.project.fields.id", scope: "decidim.budgets"), default_order: :desc) %>
26
21
  <th><%= sort_link(query, :title, t("models.project.fields.title", scope: "decidim.budgets")) %></th>
27
22
  <th><%= sort_link(query, :category_name, t("models.project.fields.category", scope: "decidim.budgets") ) %></th>
@@ -33,53 +28,7 @@
33
28
  </thead>
34
29
  <tbody>
35
30
  <% projects.each do |project| %>
36
- <tr data-id="<%= project.id %>">
37
- <td>
38
- <%= project.id %><br>
39
- </td>
40
- <td>
41
- <%= translated_attribute(project.title) %><br>
42
- </td>
43
- <td>
44
- <% if project.category %>
45
- <%= translated_attribute project.category.name %>
46
- <% end %>
47
- </td>
48
- <%= td_resource_scope_for(project.scope) %>
49
- <td>
50
- <%= project.confirmed_orders_count %>
51
- </td>
52
- <td>
53
- <% if project.selected? %>
54
- <%= content_tag :strong, t(".selected"), class: "text-success" %>
55
- <% else %>
56
- <%= content_tag :span, "×", class: "text-muted" %>
57
- <% end %>
58
- </td>
59
- <td class="table-list__actions">
60
- <%= icon_link_to "eye", resource_locator([budget, project]).path, t("actions.preview", scope: "decidim.budgets"), target: :blank, class: "action-icon--preview" %>
61
-
62
- <% if allowed_to? :update, :project, project: project %>
63
- <%= icon_link_to "pencil", resource_locator([budget, project]).edit, t("actions.edit", scope: "decidim.budgets"), class: "action-icon--edit" %>
64
- <% end %>
65
-
66
- <% if allowed_to? :update, :project, project: project %>
67
- <%= icon_link_to "folder", project_attachment_collections_path(project), t("actions.attachment_collections", scope: "decidim.budgets"), class: "action-icon--attachment_collections" %>
68
- <% end %>
69
-
70
- <% if allowed_to? :update, :project, project: project %>
71
- <%= icon_link_to "paperclip", project_attachments_path(project), t("actions.attachments", scope: "decidim.budgets"), class: "action-icon--attachments" %>
72
- <% end %>
73
-
74
- <%= resource_permissions_link(project) %>
75
-
76
- <% if allowed_to? :destroy, :project, project: project %>
77
- <%= icon_link_to "circle-x", resource_locator([budget, project]).show, t("actions.destroy", scope: "decidim.budgets"), method: :delete, class: "action-icon--remove", data: { confirm: t("actions.confirm_destroy", scope: "decidim.budgets") } %>
78
- <% else %>
79
- <%= icon "circle-x", class: "action-icon action-icon--disabled", role: "img", aria_label: t("actions.destroy", scope: "decidim.budgets") %>
80
- <% end %>
81
- </td>
82
- </tr>
31
+ <%= render partial: "project-tr", locals: { project: project } %>
83
32
  <% end %>
84
33
  </tbody>
85
34
  </table>
@@ -92,3 +41,5 @@
92
41
  <strong><%= t ".pending_orders" %>:&nbsp;</strong><span><%= pending_orders.count %></span>
93
42
  </div>
94
43
  </div>
44
+
45
+ <%= javascript_pack_tag "decidim_budgets_admin" %>
@@ -1,4 +1,3 @@
1
- <% add_decidim_page_title("#{translated_attribute(budget.title)} - #{t(".title")}") %>
2
1
  <%= decidim_form_for([budget, @form], html: { class: "form new_project" }) do |f| %>
3
2
  <%= render partial: "form", object: f, locals: { title: "#{translated_attribute(budget.title)} &gt; #{t(".title")}" } %>
4
3
 
@@ -0,0 +1,26 @@
1
+ <% if flash.now[:error].present? %>
2
+ $("<%= form_selector %> <%= attribute_selector %>").addClass("is-invalid-input")
3
+ $("<%= escape_javascript(render partial: %q{decidim/admin/shared/js-callout}, locals: { css: %q{alert}, text: flash.now[:error] }) %>").appendTo(".callout-wrapper");
4
+ <% end %>
5
+
6
+ <% if flash.now[:alert].present? %>
7
+ $("<%= form_selector %> <%= attribute_selector %>").removeClass("is-invalid-input")
8
+ $("<%= escape_javascript(render partial: %q{decidim/admin/shared/js-callout}, locals: { css: %q{warning}, text: flash.now[:alert] }) %>").appendTo(".callout-wrapper");
9
+ <% end %>
10
+
11
+ <% if flash.now[:notice].present? %>
12
+ $("<%= escape_javascript(render partial: %q{decidim/admin/shared/js-callout}, locals: { css: %q{success}, text: flash.now[:notice] }) %>").appendTo(".callout-wrapper");
13
+
14
+ <% project_ids.each do |id| %>
15
+ $(".table-list [data-id='<%= id %>']")
16
+ .replaceWith("<%= escape_javascript(render partial: %q{project-tr}, locals: { project: Decidim::Budgets::Project.find(id) }) %>");
17
+ <% end %>
18
+
19
+ $("<%= form_selector %> <%= attribute_selector %>").removeClass("is-invalid-input")
20
+ $(".js-check-all").prop('checked', false);
21
+ $(".js-check-all-resources").prop('checked', false);
22
+ window.hideBulkActionsButton();
23
+ window.hideBulkActionForms();
24
+ window.showOtherActionsButtons();
25
+ window.selectedResourcesCountUpdate();
26
+ <% end %>
@@ -1,4 +1,3 @@
1
- <% add_decidim_page_title(t(".title")) %>
2
1
  <%= decidim_form_for(@form, url: budget_proposals_import_path(budget), html: { class: "form import_proposals" }) do |f| %>
3
2
  <% if @form.origin_components.any? %>
4
3
  <div class="card">
@@ -1,7 +1,6 @@
1
1
  <% if @component.try(:scopes_enabled) %>
2
2
  <%= t(
3
3
  ".voted_on_space_with_scope",
4
- budget_name: translated_attribute(@budget.title),
5
4
  space_name: translated_attribute(@space.title),
6
5
  scope_name: translated_attribute(@component.scope.name),
7
6
  scope_type: translated_attribute(@component.scope.scope_type.name)