decidim-accountability 0.27.9 → 0.28.0.rc4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (161) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/accountability/content_blocks/highlighted_results_cell.rb +3 -8
  3. data/app/cells/decidim/accountability/highlighted_results/show.erb +3 -1
  4. data/app/cells/decidim/accountability/highlighted_results_for_component/show.erb +19 -19
  5. data/app/cells/decidim/accountability/highlighted_results_for_component_cell.rb +29 -6
  6. data/app/cells/decidim/accountability/project/show.erb +11 -0
  7. data/app/cells/decidim/accountability/project/timeline.erb +22 -0
  8. data/app/cells/decidim/accountability/project_cell.rb +75 -0
  9. data/app/cells/decidim/accountability/result_activity_cell.rb +1 -4
  10. data/app/cells/decidim/accountability/result_card_cell.rb +21 -0
  11. data/app/cells/decidim/accountability/result_cell.rb +2 -2
  12. data/app/cells/decidim/accountability/result_l/extra_data.erb +3 -0
  13. data/app/cells/decidim/accountability/result_l_cell.rb +30 -0
  14. data/app/cells/decidim/accountability/result_metadata/project_aside.erb +6 -0
  15. data/app/cells/decidim/accountability/result_metadata/show_footer.erb +11 -0
  16. data/app/cells/decidim/accountability/result_metadata/versions.erb +1 -0
  17. data/app/cells/decidim/accountability/result_metadata_cell.rb +136 -0
  18. data/app/cells/decidim/accountability/results/show.erb +7 -0
  19. data/app/cells/decidim/accountability/results_cell.rb +14 -0
  20. data/app/cells/decidim/accountability/status/show.erb +9 -0
  21. data/app/cells/decidim/accountability/status/status.erb +16 -0
  22. data/app/cells/decidim/accountability/status_cell.rb +91 -0
  23. data/app/commands/decidim/accountability/admin/import_projects_to_accountability.rb +37 -0
  24. data/app/controllers/concerns/decidim/accountability/admin/filterable.rb +2 -2
  25. data/app/controllers/decidim/accountability/admin/import_results_controller.rb +18 -6
  26. data/app/controllers/decidim/accountability/admin/projects_import_controller.rb +31 -0
  27. data/app/controllers/decidim/accountability/admin/results_controller.rb +4 -5
  28. data/app/controllers/decidim/accountability/admin/statuses_controller.rb +3 -3
  29. data/app/controllers/decidim/accountability/admin/timeline_entries_controller.rb +3 -3
  30. data/app/controllers/decidim/accountability/results_controller.rb +20 -2
  31. data/app/events/decidim/accountability/proposal_linked_event.rb +18 -1
  32. data/app/events/decidim/accountability/result_progress_updated_event.rb +18 -2
  33. data/app/forms/decidim/accountability/admin/import_results_form.rb +22 -0
  34. data/app/forms/decidim/accountability/admin/result_import_projects_form.rb +51 -0
  35. data/app/helpers/decidim/accountability/admin/application_helper.rb +1 -0
  36. data/app/helpers/decidim/accountability/application_helper.rb +43 -2
  37. data/app/helpers/decidim/accountability/breadcrumb_helper.rb +10 -0
  38. data/app/jobs/decidim/accountability/admin/import_projects_job.rb +72 -0
  39. data/app/mailers/decidim/accountability/import_projects_mailer.rb +25 -0
  40. data/app/models/decidim/accountability/result.rb +1 -1
  41. data/app/packs/entrypoints/decidim_accountability.js +4 -1
  42. data/app/packs/entrypoints/decidim_accountability_admin_imports.js +3 -0
  43. data/app/packs/images/decidim/accountability/breadcrumb_arrow.svg +3 -0
  44. data/app/packs/src/decidim/accountability/admin/imports.js +15 -0
  45. data/app/packs/stylesheets/accountability.scss +169 -0
  46. data/app/permissions/decidim/accountability/admin/permissions.rb +17 -6
  47. data/app/presenters/decidim/accountability/admin_log/result_presenter.rb +2 -2
  48. data/app/presenters/decidim/accountability/admin_log/status_presenter.rb +2 -2
  49. data/app/presenters/decidim/accountability/admin_log/timeline_entry_presenter.rb +2 -2
  50. data/app/queries/decidim/accountability/metrics/results_metric_manage.rb +1 -1
  51. data/app/services/decidim/accountability/results_calculator.rb +1 -1
  52. data/app/services/decidim/accountability/results_csv_importer.rb +2 -2
  53. data/app/views/decidim/accountability/admin/import_results/new.html.erb +27 -23
  54. data/app/views/decidim/accountability/admin/projects_import/new.html.erb +45 -0
  55. data/app/views/decidim/accountability/admin/results/_form.html.erb +46 -46
  56. data/app/views/decidim/accountability/admin/results/edit.html.erb +18 -7
  57. data/app/views/decidim/accountability/admin/results/index.html.erb +102 -91
  58. data/app/views/decidim/accountability/admin/results/new.html.erb +18 -8
  59. data/app/views/decidim/accountability/admin/shared/_subnav.html.erb +1 -1
  60. data/app/views/decidim/accountability/admin/statuses/_form.html.erb +15 -17
  61. data/app/views/decidim/accountability/admin/statuses/edit.html.erb +16 -6
  62. data/app/views/decidim/accountability/admin/statuses/index.html.erb +33 -36
  63. data/app/views/decidim/accountability/admin/statuses/new.html.erb +16 -6
  64. data/app/views/decidim/accountability/admin/timeline_entries/_form.html.erb +12 -14
  65. data/app/views/decidim/accountability/admin/timeline_entries/edit.html.erb +17 -6
  66. data/app/views/decidim/accountability/admin/timeline_entries/index.html.erb +35 -34
  67. data/app/views/decidim/accountability/admin/timeline_entries/new.html.erb +17 -6
  68. data/app/views/decidim/accountability/import_projects_mailer/import.html.erb +2 -0
  69. data/app/views/decidim/accountability/results/_home_categories.html.erb +28 -73
  70. data/app/views/decidim/accountability/results/_linked_results.html.erb +3 -12
  71. data/app/views/decidim/accountability/results/_project.html.erb +29 -0
  72. data/app/views/decidim/accountability/results/_projects_aside.html.erb +40 -0
  73. data/app/views/decidim/accountability/results/_scope_filters.html.erb +27 -27
  74. data/app/views/decidim/accountability/results/_search.html.erb +15 -9
  75. data/app/views/decidim/accountability/results/_stats_box.html.erb +3 -3
  76. data/app/views/decidim/accountability/results/home.html.erb +34 -8
  77. data/app/views/decidim/accountability/results/index.html.erb +15 -15
  78. data/app/views/decidim/accountability/results/index.js.erb +3 -0
  79. data/app/views/decidim/accountability/results/show.html.erb +9 -10
  80. data/app/views/decidim/accountability/versions/show.html.erb +13 -10
  81. data/app/views/decidim/participatory_spaces/_result.html.erb +4 -4
  82. data/config/assets.rb +2 -2
  83. data/config/locales/ar.yml +23 -61
  84. data/config/locales/bg.yml +0 -96
  85. data/config/locales/ca.yml +54 -31
  86. data/config/locales/cs.yml +58 -29
  87. data/config/locales/da.yml +1 -36
  88. data/config/locales/de.yml +59 -36
  89. data/config/locales/el.yml +62 -29
  90. data/config/locales/en.yml +55 -31
  91. data/config/locales/es-MX.yml +52 -29
  92. data/config/locales/es-PY.yml +52 -29
  93. data/config/locales/es.yml +55 -32
  94. data/config/locales/eu.yml +53 -30
  95. data/config/locales/fi-plain.yml +51 -28
  96. data/config/locales/fi.yml +54 -31
  97. data/config/locales/fr-CA.yml +51 -28
  98. data/config/locales/fr.yml +52 -29
  99. data/config/locales/ga-IE.yml +0 -4
  100. data/config/locales/gl.yml +0 -41
  101. data/config/locales/hu.yml +41 -29
  102. data/config/locales/id-ID.yml +1 -30
  103. data/config/locales/is-IS.yml +0 -32
  104. data/config/locales/it.yml +0 -43
  105. data/config/locales/ja.yml +55 -39
  106. data/config/locales/kaa.yml +20 -1
  107. data/config/locales/ko.yml +0 -188
  108. data/config/locales/lb.yml +1 -42
  109. data/config/locales/lt.yml +60 -41
  110. data/config/locales/lv.yml +1 -36
  111. data/config/locales/nl.yml +0 -41
  112. data/config/locales/no.yml +8 -49
  113. data/config/locales/pl.yml +17 -61
  114. data/config/locales/pt-BR.yml +41 -29
  115. data/config/locales/pt.yml +0 -42
  116. data/config/locales/ro-RO.yml +39 -66
  117. data/config/locales/ru.yml +1 -30
  118. data/config/locales/si-LK.yml +0 -4
  119. data/config/locales/sk.yml +1 -40
  120. data/config/locales/sl.yml +0 -11
  121. data/config/locales/sq-AL.yml +82 -7
  122. data/config/locales/sr-CS.yml +1 -30
  123. data/config/locales/sv.yml +36 -51
  124. data/config/locales/th-TH.yml +0 -140
  125. data/config/locales/tr-TR.yml +9 -75
  126. data/config/locales/uk.yml +1 -28
  127. data/config/locales/zh-CN.yml +1 -36
  128. data/config/locales/zh-TW.yml +52 -43
  129. data/lib/decidim/accountability/admin_engine.rb +3 -2
  130. data/lib/decidim/accountability/component.rb +4 -99
  131. data/lib/decidim/accountability/engine.rb +9 -1
  132. data/lib/decidim/accountability/result_serializer.rb +1 -1
  133. data/lib/decidim/accountability/seeds.rb +115 -0
  134. data/lib/decidim/accountability/test/factories.rb +18 -29
  135. data/lib/decidim/accountability/version.rb +1 -1
  136. data/lib/decidim/api/result_type.rb +1 -0
  137. metadata +62 -51
  138. data/app/cells/decidim/accountability/content_blocks/highlighted_results/elements.erb +0 -5
  139. data/app/cells/decidim/accountability/content_blocks/highlighted_results/heading.erb +0 -1
  140. data/app/cells/decidim/accountability/result_m/data.erb +0 -23
  141. data/app/cells/decidim/accountability/result_m/footer.erb +0 -22
  142. data/app/cells/decidim/accountability/result_m/tags.erb +0 -1
  143. data/app/cells/decidim/accountability/result_m_cell.rb +0 -37
  144. data/app/events/decidim/accountability/base_result_event.rb +0 -29
  145. data/app/packs/stylesheets/decidim/accountability/_accountability.scss +0 -4
  146. data/app/packs/stylesheets/decidim/accountability/accountability/_cards.scss +0 -11
  147. data/app/packs/stylesheets/decidim/accountability/accountability/_categories.scss +0 -121
  148. data/app/packs/stylesheets/decidim/accountability/accountability/_lines_breadcrumb.scss +0 -37
  149. data/app/packs/stylesheets/decidim/accountability/accountability/_results.scss +0 -148
  150. data/app/views/decidim/accountability/admin/results/proposals_picker.html.erb +0 -1
  151. data/app/views/decidim/accountability/result_widgets/show.html.erb +0 -2
  152. data/app/views/decidim/accountability/results/_home_header.html.erb +0 -23
  153. data/app/views/decidim/accountability/results/_results_leaf.html.erb +0 -47
  154. data/app/views/decidim/accountability/results/_results_parent.html.erb +0 -32
  155. data/app/views/decidim/accountability/results/_show_leaf.html.erb +0 -81
  156. data/app/views/decidim/accountability/results/_show_parent.html.erb +0 -30
  157. data/app/views/decidim/accountability/results/_stats.html.erb +0 -15
  158. data/app/views/decidim/accountability/results/_timeline.html.erb +0 -30
  159. data/app/views/decidim/accountability/versions/index.html.erb +0 -12
  160. data/config/locales/he-IL.yml +0 -219
  161. data/decidim-accountability.gemspec +0 -38
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Accountability
5
+ module Admin
6
+ # A command with all the business logic when an admin imports projects from
7
+ # one component to accountability.
8
+ class ImportProjectsToAccountability < Decidim::Command
9
+ # Public: Initializes the command.
10
+ #
11
+ # form - A form object with the params.
12
+ def initialize(form)
13
+ @form = form
14
+ end
15
+
16
+ def call
17
+ return broadcast(:invalid) unless @form.valid?
18
+
19
+ ImportProjectsJob.perform_later(projects.pluck(:id), @form.current_component, @form.current_user)
20
+ broadcast(:ok, projects.count)
21
+ end
22
+
23
+ private
24
+
25
+ def projects
26
+ Decidim::Budgets::Project.joins(:budget).selected.where(
27
+ budget: { component: origin_component }
28
+ ).reject { |item| @form.project_already_copied?(item) }
29
+ end
30
+
31
+ def origin_component
32
+ @form.origin_component
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -39,7 +39,7 @@ module Decidim
39
39
  }
40
40
  end
41
41
 
42
- # Can't user `super` here, because it does not belong to a superclass
42
+ # Cannot user `super` here, because it does not belong to a superclass
43
43
  # but to a concern.
44
44
  def dynamically_translated_filters
45
45
  [:scope_id_eq, :category_id_eq, :status_id_eq]
@@ -50,7 +50,7 @@ module Decidim
50
50
  end
51
51
 
52
52
  def translated_status_id_eq(id)
53
- translated_attribute(statuses.find_by(id: id).name)
53
+ translated_attribute(statuses.find_by(id:).name)
54
54
  end
55
55
  end
56
56
  end
@@ -5,18 +5,30 @@ module Decidim
5
5
  module Admin
6
6
  # This controller allows an admin to import results from a csv file for the Accountability component
7
7
  class ImportResultsController < Admin::ApplicationController
8
+ before_action :ensure_permissions
9
+
8
10
  def new
9
- @errors = []
11
+ @form = form(Admin::ImportResultsForm).instance
10
12
  end
11
13
 
12
14
  def create
13
- @csv_file = params[:csv_file]
14
- redirect_to(new_import_path) && return if @csv_file.blank?
15
+ @form = form(Admin::ImportResultsForm).from_params(params, current_component:)
16
+
17
+ if @form.valid?
18
+ Decidim::Accountability::Admin::ImportResultsCsvJob.perform_later(current_user, current_component, @form.local_file_path)
19
+
20
+ flash[:notice] = I18n.t("imports.create.success", scope: "decidim.accountability.admin")
21
+ redirect_to import_results_path(current_participatory_space, current_component)
22
+ else
23
+ flash[:alert] = I18n.t("imports.create.invalid", scope: "decidim.accountability.admin")
24
+ render action: "new"
25
+ end
26
+ end
15
27
 
16
- Decidim::Accountability::Admin::ImportResultsCsvJob.perform_later(current_user, current_component, @csv_file.read.force_encoding("utf-8").encode("utf-8"))
28
+ private
17
29
 
18
- flash[:notice] = I18n.t("imports.create.success", scope: "decidim.accountability.admin")
19
- redirect_to import_results_path(current_participatory_space, current_component)
30
+ def ensure_permissions
31
+ enforce_permission_to :create, :result
20
32
  end
21
33
  end
22
34
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Accountability
5
+ module Admin
6
+ # This controller allows an admin to import results from a csv file for the Accountability component
7
+ class ProjectsImportController < Admin::ApplicationController
8
+ def new
9
+ enforce_permission_to :create, :import_projects
10
+ @form = form(Admin::ResultImportProjectsForm).instance
11
+ end
12
+
13
+ def create
14
+ enforce_permission_to :create, :import_projects
15
+ @form = form(Admin::ResultImportProjectsForm).from_params(params, accountability_component: current_component)
16
+ Admin::ImportProjectsToAccountability.call(@form) do
17
+ on(:ok) do |projects|
18
+ flash[:notice] = I18n.t("projects_import.new.success", scope: "decidim.accountability.admin", count: projects)
19
+ redirect_to results_path
20
+ end
21
+
22
+ on(:invalid) do
23
+ flash[:alert] = I18n.t("projects_import.create.invalid", scope: "decidim.accountability.admin")
24
+ render action: "new"
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -7,14 +7,13 @@ module Decidim
7
7
  class ResultsController < Admin::ApplicationController
8
8
  include Decidim::ApplicationHelper
9
9
  include Decidim::SanitizeHelper
10
- include Decidim::Proposals::Admin::Picker if Decidim::Accountability.enable_proposal_linking
11
10
  include Decidim::Accountability::Admin::Filterable
12
11
 
13
12
  helper_method :results, :parent_result, :parent_results, :statuses, :present
14
13
 
15
14
  def collection
16
15
  parent_id = params[:parent_id].presence
17
- @collection ||= Result.where(component: current_component, parent_id: parent_id).page(params[:page]).per(15)
16
+ @collection ||= Result.where(component: current_component, parent_id:).page(params[:page]).per(15)
18
17
  end
19
18
 
20
19
  def new
@@ -43,13 +42,13 @@ module Decidim
43
42
  end
44
43
 
45
44
  def edit
46
- enforce_permission_to :update, :result, result: result
45
+ enforce_permission_to(:update, :result, result:)
47
46
 
48
47
  @form = form(ResultForm).from_model(result)
49
48
  end
50
49
 
51
50
  def update
52
- enforce_permission_to :update, :result, result: result
51
+ enforce_permission_to(:update, :result, result:)
53
52
 
54
53
  @form = form(ResultForm).from_params(params)
55
54
 
@@ -67,7 +66,7 @@ module Decidim
67
66
  end
68
67
 
69
68
  def destroy
70
- enforce_permission_to :destroy, :result, result: result
69
+ enforce_permission_to(:destroy, :result, result:)
71
70
 
72
71
  DestroyResult.call(result, current_user) do
73
72
  on(:ok) do
@@ -32,13 +32,13 @@ module Decidim
32
32
  end
33
33
 
34
34
  def edit
35
- enforce_permission_to :update, :status, status: status
35
+ enforce_permission_to(:update, :status, status:)
36
36
 
37
37
  @form = form(StatusForm).from_model(status)
38
38
  end
39
39
 
40
40
  def update
41
- enforce_permission_to :update, :status, status: status
41
+ enforce_permission_to(:update, :status, status:)
42
42
 
43
43
  @form = form(StatusForm).from_params(params)
44
44
 
@@ -56,7 +56,7 @@ module Decidim
56
56
  end
57
57
 
58
58
  def destroy
59
- enforce_permission_to :destroy, :status, status: status
59
+ enforce_permission_to(:destroy, :status, status:)
60
60
 
61
61
  Decidim.traceability.perform_action!("delete", status, current_user) do
62
62
  status.destroy!
@@ -33,13 +33,13 @@ module Decidim
33
33
  end
34
34
 
35
35
  def edit
36
- enforce_permission_to :update, :timeline_entry, timeline_entry: timeline_entry
36
+ enforce_permission_to(:update, :timeline_entry, timeline_entry:)
37
37
 
38
38
  @form = form(TimelineEntryForm).from_model(timeline_entry)
39
39
  end
40
40
 
41
41
  def update
42
- enforce_permission_to :update, :timeline_entry, timeline_entry: timeline_entry
42
+ enforce_permission_to(:update, :timeline_entry, timeline_entry:)
43
43
 
44
44
  @form = form(TimelineEntryForm).from_params(params)
45
45
 
@@ -57,7 +57,7 @@ module Decidim
57
57
  end
58
58
 
59
59
  def destroy
60
- enforce_permission_to :destroy, :timeline_entry, timeline_entry: timeline_entry
60
+ enforce_permission_to(:destroy, :timeline_entry, timeline_entry:)
61
61
 
62
62
  Decidim.traceability.perform_action!("delete", timeline_entry, current_user) do
63
63
  timeline_entry.destroy!
@@ -8,7 +8,7 @@ module Decidim
8
8
  helper Decidim::TraceabilityHelper
9
9
  helper Decidim::Accountability::BreadcrumbHelper
10
10
 
11
- helper_method :results, :result, :first_class_categories, :count_calculator
11
+ helper_method :results, :result, :first_class_categories, :count_calculator, :nav_paths
12
12
 
13
13
  def show
14
14
  raise ActionController::RoutingError, "Not Found" unless result
@@ -20,7 +20,7 @@ module Decidim
20
20
  @results ||= begin
21
21
  parent_id = params[:parent_id].presence
22
22
  search.result.where(
23
- parent_id: [parent_id] + Result.where(parent_id: parent_id).pluck(:id)
23
+ parent_id: [parent_id] + Result.where(parent_id:).pluck(:id)
24
24
  ).page(params[:page]).per(12)
25
25
  end
26
26
  end
@@ -29,6 +29,24 @@ module Decidim
29
29
  @result ||= search_collection.includes(:timeline_entries).find_by(id: params[:id])
30
30
  end
31
31
 
32
+ def next_result
33
+ return if search_collection.size < 2
34
+
35
+ search_collection.order(:start_date, :id).where(Decidim::Accountability::Result.arel_table[:id].gt(result.id)).first
36
+ end
37
+
38
+ def prev_result
39
+ return if search_collection.size < 2
40
+
41
+ search_collection.order(:start_date, :id).where(Decidim::Accountability::Result.arel_table[:id].lt(result.id)).last
42
+ end
43
+
44
+ def nav_paths
45
+ return {} if result.blank?
46
+
47
+ { prev_path: prev_result, next_path: next_result }.compact_blank.transform_values { |result| result_path(result) }
48
+ end
49
+
32
50
  def search_collection
33
51
  Result.where(component: current_component)
34
52
  end
@@ -2,7 +2,24 @@
2
2
 
3
3
  module Decidim
4
4
  module Accountability
5
- class ProposalLinkedEvent < BaseResultEvent
5
+ class ProposalLinkedEvent < Decidim::Events::SimpleEvent
6
+ i18n_attributes :proposal_title, :proposal_path
7
+
8
+ def proposal_path
9
+ @proposal_path ||= Decidim::ResourceLocatorPresenter.new(proposal).path
10
+ end
11
+
12
+ def proposal_title
13
+ @proposal_title ||= translated_attribute(proposal.title)
14
+ end
15
+
16
+ def proposal
17
+ @proposal ||= resource.linked_resources(:proposals, "included_proposals").find_by(id: extra[:proposal_id])
18
+ end
19
+
20
+ def resource_text
21
+ translated_attribute(resource.description)
22
+ end
6
23
  end
7
24
  end
8
25
  end
@@ -2,12 +2,28 @@
2
2
 
3
3
  module Decidim
4
4
  module Accountability
5
- class ResultProgressUpdatedEvent < BaseResultEvent
6
- i18n_attributes :progress
5
+ class ResultProgressUpdatedEvent < Decidim::Events::SimpleEvent
6
+ i18n_attributes :proposal_title, :proposal_path, :progress
7
+
8
+ def proposal_path
9
+ @proposal_path ||= Decidim::ResourceLocatorPresenter.new(proposal).path
10
+ end
11
+
12
+ def proposal_title
13
+ @proposal_title ||= translated_attribute(proposal.title)
14
+ end
15
+
16
+ def proposal
17
+ @proposal ||= resource.linked_resources(:proposals, "included_proposals").find_by(id: extra[:proposal_id])
18
+ end
7
19
 
8
20
  def progress
9
21
  extra[:progress]
10
22
  end
23
+
24
+ def resource_text
25
+ translated_attribute(resource.description)
26
+ end
11
27
  end
12
28
  end
13
29
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Accountability
5
+ module Admin
6
+ class ImportResultsForm < Decidim::Form
7
+ include Decidim::HasUploadValidations
8
+ include Decidim::ProcessesFileLocally
9
+
10
+ attribute :file, Decidim::Attributes::Blob
11
+
12
+ validates :file, presence: true, file_content_type: { allow: ["text/csv"] }
13
+
14
+ def local_file_path
15
+ process_file_locally(file) do |file_path|
16
+ file_path
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Accountability
5
+ module Admin
6
+ # A form object to be used when admin users want to import a collection of projects
7
+ # from another component into Accountability component.
8
+ class ResultImportProjectsForm < Decidim::Form
9
+ attribute :origin_component_id, Integer
10
+ attribute :import_all_selected_projects, Boolean
11
+
12
+ validates :origin_component_id, presence: true
13
+ validates :import_all_selected_projects, allow_nil: false, acceptance: true
14
+ validates :origin_projects_count, numericality: { greater_than: 0 }, if: ->(form) { form.origin_component_id }
15
+
16
+ def origin_component
17
+ @origin_component ||= origin_components.find_by(id: origin_component_id)
18
+ end
19
+
20
+ def origin_components_collection
21
+ origin_components.map do |component|
22
+ [component.name[I18n.locale.to_s], component.id]
23
+ end
24
+ end
25
+
26
+ def origin_components
27
+ @budgets_component ||= current_participatory_space.components.where(manifest_name: "budgets")
28
+ end
29
+
30
+ def selected_projects_count(component)
31
+ projects = Decidim::Budgets::Project.joins(:budget).selected.where(
32
+ budget: { component: }
33
+ )
34
+ projects.reject { |project| project_already_copied?(project) }.count
35
+ end
36
+
37
+ def project_already_copied?(original_project)
38
+ original_project.linked_resources(:results, "included_projects").any? do |result|
39
+ result.component == current_component
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def origin_projects_count
46
+ selected_projects_count(origin_component_id)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -7,6 +7,7 @@ module Decidim
7
7
  #
8
8
  module ApplicationHelper
9
9
  include Decidim::Admin::ResourceScopeHelper
10
+ include Decidim::PaginateHelper
10
11
  end
11
12
  end
12
13
  end
@@ -22,6 +22,10 @@ module Decidim
22
22
  "class=active" if scope.to_s == current_scope.to_s
23
23
  end
24
24
 
25
+ def component_name
26
+ (defined?(current_component) && translated_attribute(current_component&.name).presence) || t("decidim.components.accountability.name")
27
+ end
28
+
25
29
  def categories_label
26
30
  translated_attribute(component_settings.categories_label).presence || t("results.home.categories_label", scope: "decidim.accountability")
27
31
  end
@@ -35,7 +39,7 @@ module Decidim
35
39
  if text
36
40
  pluralize(count, text)
37
41
  else
38
- t("results.count.results_count", scope: "decidim.accountability", count: count)
42
+ t("results.count.results_count", scope: "decidim.accountability", count:)
39
43
  end
40
44
  end
41
45
 
@@ -44,9 +48,46 @@ module Decidim
44
48
  if text
45
49
  pluralize(count, text)
46
50
  else
47
- t("results.count.results_count", scope: "decidim.accountability", count: count)
51
+ t("results.count.results_count", scope: "decidim.accountability", count:)
48
52
  end
49
53
  end
54
+
55
+ def filter_items_for(participatory_space:, category:)
56
+ [
57
+ {
58
+ url: url_for(filter: { with_category: category.try(:id) }),
59
+ text: t("results.filters.all", scope: "decidim.accountability"),
60
+ icon: "apps-2-line",
61
+ active: current_scope.blank?,
62
+ sr_text: Decidim::Scope.model_name.human(count: 2)
63
+ },
64
+ *(
65
+ if participatory_space.scope
66
+ [{
67
+ url: url_for(filter: { with_scope: participatory_space.scope.id, with_category: category.try(:id) }),
68
+ text: translated_attribute(participatory_space.scope.name),
69
+ icon: resource_type_icon_key(participatory_space.scope.class),
70
+ active: participatory_space.scope.id.to_s == current_scope.to_s,
71
+ sr_text: Decidim::Scope.model_name.human(count: 1)
72
+ }]
73
+ end
74
+ ),
75
+ *participatory_space.subscopes.map do |scope|
76
+ {
77
+ url: url_for(filter: { with_scope: scope.id, with_category: category.try(:id) }),
78
+ text: translated_attribute(scope.name),
79
+ icon: resource_type_icon_key(scope.class),
80
+ active: scope.id.to_s == current_scope.to_s,
81
+ sr_text: Decidim::Scope.model_name.human(count: 1)
82
+ }
83
+ end
84
+ ]
85
+ end
86
+
87
+ def apply_accountability_pack_tags
88
+ append_stylesheet_pack_tag("decidim_accountability", media: "all")
89
+ append_javascript_pack_tag("decidim_accountability")
90
+ end
50
91
  end
51
92
  end
52
93
  end
@@ -22,6 +22,16 @@ module Decidim
22
22
 
23
23
  @category ||= current_participatory_space.categories.find(category_id.is_a?(Array) ? category_id.first : category_id)
24
24
  end
25
+
26
+ def parent_categories(category)
27
+ return [] if category&.parent.blank?
28
+
29
+ [*parent_categories(category.parent), category.parent]
30
+ end
31
+
32
+ def categories_hierarchy
33
+ parent_categories(category)
34
+ end
25
35
  end
26
36
  end
27
37
  end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Accountability
5
+ module Admin
6
+ class ImportProjectsJob < ApplicationJob
7
+ queue_as :default
8
+
9
+ def perform(projects, component, user)
10
+ projects.map do |id|
11
+ original_project = Decidim::Budgets::Project.find_by(id:)
12
+
13
+ new_result = create_result_from_project!(original_project, statuses(component).first, component, user)
14
+ new_result.link_resources([original_project], "included_projects")
15
+ new_result.link_resources(
16
+ original_project.linked_resources(:proposals, "included_proposals"),
17
+ "included_proposals"
18
+ )
19
+
20
+ copy_attachments(original_project, new_result)
21
+ end.compact
22
+ Decidim::Accountability::ImportProjectsMailer.import(user, component, projects.count).deliver_now
23
+ end
24
+
25
+ private
26
+
27
+ def create_result_from_project!(project, status, component, user)
28
+ params = {
29
+ title: project.title,
30
+ description: project.description,
31
+ category: project.category,
32
+ scope: project.scope || project.budget.scope,
33
+ component:,
34
+ status:,
35
+ progress: status&.progress || 0
36
+ }
37
+ @result = Decidim.traceability.create!(
38
+ Result,
39
+ user,
40
+ params,
41
+ visibility: "all"
42
+ )
43
+ end
44
+
45
+ def copy_attachments(project, result)
46
+ project.attachments.each do |attachment|
47
+ new_attachment = Decidim::Attachment.new(
48
+ {
49
+ # Attached to needs to be always defined before the file is set
50
+ attached_to: result
51
+ }.merge(
52
+ attachment.attributes.slice("content_type", "description", "file_size", "title", "weight")
53
+ )
54
+ )
55
+
56
+ if attachment.file.attached?
57
+ new_attachment.file = attachment.file.blob
58
+ else
59
+ new_attachment.attached_uploader(:file).remote_url = attachment.attached_uploader(:file)
60
+ end
61
+
62
+ new_attachment.save!
63
+ end
64
+ end
65
+
66
+ def statuses(component)
67
+ Decidim::Accountability::Status.where(component:).order(:progress)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Accountability
5
+ # This mailer sends a notification email containing the result of importing
6
+ # Cprojects to the results.
7
+ class ImportProjectsMailer < Decidim::ApplicationMailer
8
+ # Public: Sends a notification email with the result of projects import selected projects to Accountability
9
+ #
10
+ # user - The user to be notified.
11
+ #
12
+ # Returns nothing.
13
+ def import(user, component, projects)
14
+ @user = user
15
+ @organization = user.organization
16
+ @component = component
17
+ @projects = projects
18
+
19
+ with_user(user) do
20
+ mail(to: "#{user.name} <#{user.email}>", subject: I18n.t("decidim.accountability.import_projects_mailer.import.subject"))
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -96,7 +96,7 @@ module Decidim
96
96
 
97
97
  private
98
98
 
99
- # Private: When a row uses weight 1 and there's more than one, weight shouldn't be considered
99
+ # Private: When a row uses weight 1 and there is more than one, weight should not be considered
100
100
  # Handle special case when all children weight are nil
101
101
  def children_use_weighted_progress?
102
102
  return false if children.pluck(:weight).all?(&:nil?)
@@ -1,4 +1,7 @@
1
- import "src/decidim/accountability/index"
1
+ // import "src/decidim/accountability/index" --deprecated
2
2
 
3
3
  // Images
4
4
  require.context("../images", true)
5
+
6
+ // CSS
7
+ import "stylesheets/accountability.scss"
@@ -0,0 +1,3 @@
1
+ import initializeElement from "src/decidim/accountability/admin/imports"
2
+
3
+ window.addEventListener("DOMContentLoaded", initializeElement);
@@ -0,0 +1,3 @@
1
+ <svg width="24" height="43" viewBox="0 0 24 43" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M23.6979 35.6924C24.0211 36.0156 24.0211 36.5396 23.6979 36.8628L18.4312 42.1296C18.108 42.4527 17.5841 42.4527 17.2608 42.1296C16.9376 41.8064 16.9376 41.2824 17.2608 40.9592L21.9424 36.2776L17.2608 31.5961C16.9376 31.2729 16.9376 30.7489 17.2608 30.4257C17.584 30.1025 18.108 30.1025 18.4312 30.4257L23.6979 35.6924ZM2.4247 0V20.9346H0.769531V0.000447832L2.4247 0ZM16.9402 35.45H23.1127V37.1052H16.9402V35.45ZM2.4247 20.9346C2.4247 28.9512 8.92354 35.45 16.9402 35.45V37.1052C8.00938 37.1052 0.769531 29.8654 0.769531 20.9346H2.4247Z" fill="currentColor" />
3
+ </svg>
@@ -0,0 +1,15 @@
1
+ const toggleInfo = (val) => {
2
+ document.querySelectorAll(".help-text").forEach((toBeHidden) => {
3
+ if (val === toBeHidden.id && toBeHidden.classList.value.includes("hide")) {
4
+ toBeHidden.classList.remove("hide")
5
+ } else {
6
+ toBeHidden.classList.add("hide")
7
+ }
8
+ });
9
+ };
10
+ export default () => {
11
+ const item = document.querySelector("#result_import_projects_origin_component_id");
12
+ item.addEventListener(("change"), (event) => {
13
+ toggleInfo(`component_${event.target.value}`);
14
+ });
15
+ };