decidim-forms 0.30.9 → 0.31.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 (162) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -10
  3. data/app/cells/decidim/forms/matrix_readonly/show.erb +1 -1
  4. data/app/cells/decidim/forms/matrix_readonly_cell.rb +3 -3
  5. data/app/cells/decidim/forms/question_readonly/show.erb +5 -5
  6. data/app/cells/decidim/forms/question_readonly/title_and_description.erb +3 -3
  7. data/app/cells/decidim/forms/response_readonly_cell.rb +9 -0
  8. data/app/cells/decidim/forms/step_navigation/show.erb +3 -3
  9. data/app/cells/decidim/forms/step_navigation_cell.rb +3 -3
  10. data/app/commands/decidim/forms/admin/update_questions.rb +6 -6
  11. data/app/commands/decidim/forms/{answer_questionnaire.rb → response_questionnaire.rb} +35 -35
  12. data/app/controllers/decidim/forms/admin/concerns/has_questionnaire.rb +12 -12
  13. data/app/controllers/decidim/forms/admin/concerns/{has_questionnaire_answers.rb → has_questionnaire_responses.rb} +23 -19
  14. data/app/controllers/decidim/forms/concerns/has_questionnaire.rb +38 -33
  15. data/app/forms/decidim/forms/admin/display_condition_form.rb +15 -15
  16. data/app/forms/decidim/forms/admin/question_form.rb +3 -3
  17. data/app/forms/decidim/forms/admin/{answer_option_form.rb → response_option_form.rb} +3 -3
  18. data/app/forms/decidim/forms/questionnaire_form.rb +18 -16
  19. data/app/forms/decidim/forms/{answer_choice_form.rb → response_choice_form.rb} +5 -5
  20. data/app/forms/decidim/forms/{answer_form.rb → response_form.rb} +9 -9
  21. data/app/helpers/decidim/forms/admin/application_helper.rb +2 -2
  22. data/app/helpers/decidim/forms/admin/concerns/{has_questionnaire_answers_pagination_helper.rb → has_questionnaire_responses_pagination_helper.rb} +4 -4
  23. data/app/helpers/decidim/forms/admin/concerns/{has_questionnaire_answers_url_helper.rb → has_questionnaire_responses_url_helper.rb} +5 -5
  24. data/app/helpers/decidim/forms/admin/questionnaire_responses_helper.rb +32 -0
  25. data/app/helpers/decidim/forms/application_helper.rb +1 -2
  26. data/app/jobs/decidim/forms/{export_questionnaire_answers_job.rb → export_questionnaire_responses_job.rb} +5 -5
  27. data/app/models/decidim/forms/display_condition.rb +20 -20
  28. data/app/models/decidim/forms/question.rb +14 -13
  29. data/app/models/decidim/forms/question_matrix_row.rb +1 -1
  30. data/app/models/decidim/forms/questionnaire.rb +11 -11
  31. data/app/models/decidim/forms/{answer.rb → response.rb} +9 -9
  32. data/app/models/decidim/forms/response_choice.rb +22 -0
  33. data/app/models/decidim/forms/{answer_option.rb → response_option.rb} +5 -5
  34. data/app/packs/entrypoints/decidim_forms_admin.js +3 -3
  35. data/app/packs/src/decidim/forms/admin/collapsible_questions.js +12 -10
  36. data/app/packs/src/decidim/forms/admin/forms.js +47 -66
  37. data/app/packs/src/decidim/forms/admin/{publish_answers_buttons.js → publish_responses_buttons.js} +12 -12
  38. data/app/packs/src/decidim/forms/display_conditions.component.js +12 -12
  39. data/app/packs/src/decidim/forms/forms.js +8 -29
  40. data/app/packs/stylesheets/decidim/forms/forms.scss +2 -2
  41. data/app/presenters/decidim/forms/admin/questionnaire_participant_presenter.rb +13 -13
  42. data/app/presenters/decidim/forms/admin/questionnaire_response_presenter.rb +89 -0
  43. data/app/presenters/decidim/forms/admin_log/question_presenter.rb +3 -3
  44. data/app/presenters/decidim/forms/{answer_option_presenter.rb → response_option_presenter.rb} +2 -2
  45. data/app/queries/decidim/forms/questionnaire_participant.rb +5 -5
  46. data/app/queries/decidim/forms/questionnaire_participants.rb +6 -6
  47. data/app/queries/decidim/forms/questionnaire_user_responses.rb +32 -0
  48. data/app/views/decidim/forms/admin/questionnaires/_display_condition.html.erb +9 -9
  49. data/app/views/decidim/forms/admin/questionnaires/_form.html.erb +2 -2
  50. data/app/views/decidim/forms/admin/questionnaires/_matrix_row.html.erb +3 -3
  51. data/app/views/decidim/forms/admin/questionnaires/_question.html.erb +25 -15
  52. data/app/views/decidim/forms/admin/questionnaires/_questions_form.html.erb +14 -21
  53. data/app/views/decidim/forms/admin/questionnaires/_response_option.html.erb +45 -0
  54. data/app/views/decidim/forms/admin/questionnaires/_response_option_template.html.erb +7 -0
  55. data/app/views/decidim/forms/admin/questionnaires/_separator.html.erb +15 -5
  56. data/app/views/decidim/forms/admin/questionnaires/_title_and_description.html.erb +15 -5
  57. data/app/views/decidim/forms/admin/questionnaires/edit.html.erb +1 -1
  58. data/app/views/decidim/forms/admin/questionnaires/edit_questions.html.erb +8 -8
  59. data/app/views/decidim/forms/admin/questionnaires/responses/index.html.erb +80 -0
  60. data/app/views/decidim/forms/admin/questionnaires/responses/show.html.erb +43 -0
  61. data/app/views/decidim/forms/questionnaires/_questionnaire.html.erb +24 -22
  62. data/app/views/decidim/forms/questionnaires/_questionnaire_readonly.html.erb +8 -3
  63. data/app/views/decidim/forms/questionnaires/_response.html.erb +61 -0
  64. data/app/views/decidim/forms/questionnaires/edit.html.erb +2 -2
  65. data/app/views/decidim/forms/questionnaires/responses/_files.html.erb +1 -0
  66. data/app/views/decidim/forms/questionnaires/responses/_long_response.html.erb +3 -0
  67. data/app/views/decidim/forms/questionnaires/{answers → responses}/_matrix_multiple.html.erb +16 -15
  68. data/app/views/decidim/forms/questionnaires/{answers → responses}/_matrix_single.html.erb +17 -16
  69. data/app/views/decidim/forms/questionnaires/responses/_multiple_option.html.erb +26 -0
  70. data/app/views/decidim/forms/questionnaires/responses/_separator.html.erb +1 -0
  71. data/app/views/decidim/forms/questionnaires/responses/_short_response.html.erb +3 -0
  72. data/app/views/decidim/forms/questionnaires/responses/_single_option.html.erb +33 -0
  73. data/app/views/decidim/forms/questionnaires/responses/_sorting.html.erb +26 -0
  74. data/app/views/decidim/forms/questionnaires/responses/_title_and_description.html.erb +1 -0
  75. data/app/views/decidim/forms/questionnaires/show.html.erb +10 -10
  76. data/config/assets.rb +2 -2
  77. data/config/locales/ar.yml +2 -22
  78. data/config/locales/bg.yml +10 -36
  79. data/config/locales/ca-IT.yml +82 -76
  80. data/config/locales/ca.yml +82 -76
  81. data/config/locales/cs.yml +79 -76
  82. data/config/locales/de.yml +80 -74
  83. data/config/locales/el.yml +10 -36
  84. data/config/locales/en.yml +90 -84
  85. data/config/locales/es-MX.yml +82 -76
  86. data/config/locales/es-PY.yml +82 -76
  87. data/config/locales/es.yml +80 -74
  88. data/config/locales/eu.yml +83 -77
  89. data/config/locales/fi-plain.yml +78 -72
  90. data/config/locales/fi.yml +76 -70
  91. data/config/locales/fr-CA.yml +72 -70
  92. data/config/locales/fr.yml +72 -70
  93. data/config/locales/ga-IE.yml +4 -7
  94. data/config/locales/gl.yml +4 -22
  95. data/config/locales/hu.yml +4 -22
  96. data/config/locales/id-ID.yml +2 -22
  97. data/config/locales/it.yml +8 -40
  98. data/config/locales/ja.yml +87 -81
  99. data/config/locales/lb.yml +8 -36
  100. data/config/locales/lt.yml +10 -36
  101. data/config/locales/lv.yml +4 -22
  102. data/config/locales/nl.yml +10 -36
  103. data/config/locales/no.yml +10 -36
  104. data/config/locales/pl.yml +10 -36
  105. data/config/locales/pt-BR.yml +10 -120
  106. data/config/locales/pt.yml +8 -36
  107. data/config/locales/ro-RO.yml +30 -52
  108. data/config/locales/ru.yml +2 -9
  109. data/config/locales/sk.yml +3 -183
  110. data/config/locales/sl.yml +0 -5
  111. data/config/locales/sv.yml +77 -70
  112. data/config/locales/tr-TR.yml +8 -36
  113. data/config/locales/val-ES.yml +2 -0
  114. data/config/locales/zh-CN.yml +8 -36
  115. data/config/locales/zh-TW.yml +10 -36
  116. data/db/migrate/20190315203056_add_session_token_to_decidim_forms_answers.rb +1 -1
  117. data/db/migrate/20250314150250_rename_answer_to_response_in_decidim_forms.rb +30 -0
  118. data/db/migrate/20250319130003_change_question_types_in_questions.rb +14 -0
  119. data/lib/decidim/api/question_matrix_row_type.rb +13 -0
  120. data/lib/decidim/api/question_type.rb +4 -3
  121. data/lib/decidim/api/questionnaire_type.rb +1 -0
  122. data/lib/decidim/api/response_option_type.rb +13 -0
  123. data/lib/decidim/exporters/form_pdf.rb +35 -43
  124. data/lib/decidim/forms/api.rb +2 -1
  125. data/lib/decidim/forms/{download_your_data_user_answers_serializer.rb → download_your_data_user_responses_serializer.rb} +3 -3
  126. data/lib/decidim/forms/engine.rb +2 -2
  127. data/lib/decidim/forms/test/factories.rb +24 -24
  128. data/lib/decidim/forms/test/shared_examples/has_questionnaire.rb +178 -176
  129. data/lib/decidim/forms/test/shared_examples/manage_questionnaire_responses.rb +159 -0
  130. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/add_display_conditions.rb +19 -19
  131. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/add_questions.rb +44 -90
  132. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/update_display_conditions.rb +10 -10
  133. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/update_questions.rb +43 -140
  134. data/lib/decidim/forms/test/shared_examples/manage_questionnaires.rb +4 -6
  135. data/lib/decidim/forms/test.rb +1 -1
  136. data/lib/decidim/forms/user_responses_serializer.rb +110 -0
  137. data/lib/decidim/forms/version.rb +1 -1
  138. data/lib/decidim/forms.rb +2 -2
  139. metadata +48 -46
  140. data/app/cells/decidim/forms/answer_readonly_cell.rb +0 -9
  141. data/app/helpers/decidim/forms/admin/questionnaire_answers_helper.rb +0 -30
  142. data/app/models/decidim/forms/answer_choice.rb +0 -22
  143. data/app/presenters/decidim/forms/admin/questionnaire_answer_presenter.rb +0 -112
  144. data/app/queries/decidim/forms/questionnaire_user_answers.rb +0 -32
  145. data/app/views/decidim/forms/admin/questionnaires/_answer_option.html.erb +0 -45
  146. data/app/views/decidim/forms/admin/questionnaires/_answer_option_template.html.erb +0 -7
  147. data/app/views/decidim/forms/admin/questionnaires/answers/index.html.erb +0 -49
  148. data/app/views/decidim/forms/admin/questionnaires/answers/show.html.erb +0 -43
  149. data/app/views/decidim/forms/questionnaires/_answer.html.erb +0 -60
  150. data/app/views/decidim/forms/questionnaires/answers/_files.html.erb +0 -1
  151. data/app/views/decidim/forms/questionnaires/answers/_long_answer.html.erb +0 -3
  152. data/app/views/decidim/forms/questionnaires/answers/_multiple_option.html.erb +0 -25
  153. data/app/views/decidim/forms/questionnaires/answers/_separator.html.erb +0 -1
  154. data/app/views/decidim/forms/questionnaires/answers/_short_answer.html.erb +0 -3
  155. data/app/views/decidim/forms/questionnaires/answers/_single_option.html.erb +0 -32
  156. data/app/views/decidim/forms/questionnaires/answers/_sorting.html.erb +0 -26
  157. data/app/views/decidim/forms/questionnaires/answers/_title_and_description.html.erb +0 -1
  158. data/lib/decidim/api/answer_option_type.rb +0 -13
  159. data/lib/decidim/forms/test/shared_examples/manage_questionnaire_answers.rb +0 -149
  160. data/lib/decidim/forms/test/shared_examples/manage_questionnaires/draggable_behavior.rb +0 -47
  161. data/lib/decidim/forms/user_answers_serializer.rb +0 -105
  162. /data/app/cells/decidim/forms/{answer_readonly → response_readonly}/show.erb +0 -0
@@ -6,7 +6,7 @@ module Decidim
6
6
  class Question < Forms::ApplicationRecord
7
7
  include Decidim::TranslatableResource
8
8
 
9
- QUESTION_TYPES = %w(short_answer long_answer single_option multiple_option sorting files matrix_single matrix_multiple).freeze
9
+ QUESTION_TYPES = %w(short_response long_response single_option multiple_option sorting files matrix_single matrix_multiple).freeze
10
10
  SEPARATOR_TYPE = "separator"
11
11
  TITLE_AND_DESCRIPTION_TYPE = "title_and_description"
12
12
  TYPES = (QUESTION_TYPES + [SEPARATOR_TYPE, TITLE_AND_DESCRIPTION_TYPE]).freeze
@@ -21,8 +21,8 @@ module Decidim
21
21
  dependent: :destroy,
22
22
  inverse_of: :question
23
23
 
24
- has_many :answer_options,
25
- class_name: "AnswerOption",
24
+ has_many :response_options,
25
+ class_name: "ResponseOption",
26
26
  foreign_key: "decidim_question_id",
27
27
  dependent: :destroy,
28
28
  inverse_of: :question
@@ -34,21 +34,22 @@ module Decidim
34
34
  dependent: :destroy,
35
35
  inverse_of: :question
36
36
 
37
- # Conditions to display other questions based on the value of this question's answer
37
+ # Conditions to display other questions based on the value of this question's response
38
38
  has_many :display_conditions_for_other_questions,
39
39
  class_name: "DisplayCondition",
40
40
  foreign_key: "decidim_condition_question_id",
41
41
  dependent: :destroy,
42
- inverse_of: :question
42
+ inverse_of: :question,
43
+ counter_cache: :display_conditions_for_other_questions_count
43
44
 
44
- # Questions which have display conditions based on the value of this question's answer
45
+ # Questions which have display conditions based on the value of this question's response
45
46
  has_many :conditioned_questions,
46
47
  through: :display_conditions_for_other_questions,
47
48
  foreign_key: "decidim_condition_question_id",
48
49
  class_name: "Question"
49
50
 
50
- has_many :answers,
51
- class_name: "Answer",
51
+ has_many :responses,
52
+ class_name: "Response",
52
53
  foreign_key: "decidim_question_id",
53
54
  dependent: :destroy,
54
55
  inverse_of: :question
@@ -58,8 +59,8 @@ module Decidim
58
59
  scope :not_separator, -> { where.not(question_type: SEPARATOR_TYPE) }
59
60
  scope :not_title_and_description, -> { where.not(question_type: TITLE_AND_DESCRIPTION_TYPE) }
60
61
 
61
- scope :with_body, -> { where(question_type: %w(short_answer long_answer)) }
62
- scope :with_choices, -> { where.not(question_type: %w(short_answer long_answer)) }
62
+ scope :with_body, -> { where(question_type: %w(short_response long_response)) }
63
+ scope :with_choices, -> { where.not(question_type: %w(short_response long_response)) }
63
64
 
64
65
  scope :conditioned, -> { includes(:display_conditions).where.not(decidim_forms_display_conditions: { id: nil }) }
65
66
  scope :not_conditioned, -> { includes(:display_conditions).where(decidim_forms_display_conditions: { id: nil }) }
@@ -81,7 +82,7 @@ module Decidim
81
82
  end
82
83
 
83
84
  def number_of_options
84
- answer_options.size
85
+ response_options.size
85
86
  end
86
87
 
87
88
  def translated_body
@@ -100,8 +101,8 @@ module Decidim
100
101
  question_type.to_s == "files"
101
102
  end
102
103
 
103
- def answers_count
104
- questionnaire.answers.where(question: self).count
104
+ def responses_count
105
+ questionnaire.responses.where(question: self).count
105
106
  end
106
107
 
107
108
  def self.log_presenter_class_for(_log)
@@ -9,7 +9,7 @@ module Decidim
9
9
  translatable_fields :body
10
10
  belongs_to :question, class_name: "Question", foreign_key: "decidim_question_id", counter_cache: :matrix_rows_count
11
11
 
12
- delegate :answer_options, :mandatory, :max_choices, to: :question
12
+ delegate :response_options, :mandatory, :max_choices, to: :question
13
13
 
14
14
  scope :by_position, -> { order(:position) }
15
15
  default_scope { by_position }
@@ -13,22 +13,27 @@ module Decidim
13
13
  belongs_to :questionnaire_for, polymorphic: true
14
14
 
15
15
  has_many :questions, -> { order(:position) }, class_name: "Question", foreign_key: "decidim_questionnaire_id", dependent: :destroy
16
- has_many :answers, class_name: "Answer", foreign_key: "decidim_questionnaire_id", dependent: :destroy
16
+ has_many :responses, class_name: "Response", foreign_key: "decidim_questionnaire_id", dependent: :destroy
17
17
 
18
18
  after_initialize :set_default_salt
19
19
 
20
20
  attr_accessor :questionnaire_template_id
21
+ attr_reader :override_edit
21
22
 
22
23
  # Public: returns whether the questionnaire questions can be modified or not.
23
24
  def questions_editable?
24
25
  has_component = questionnaire_for.respond_to? :component
25
- (has_component && !questionnaire_for.component.published?) || answers.empty?
26
+ (has_component && !questionnaire_for.component.published?) || override_edit.presence || responses.empty?
26
27
  end
27
28
 
28
- # Public: returns whether the questionnaire is answered by the user or not.
29
- def answered_by?(user)
29
+ def override_edit!
30
+ @override_edit = true
31
+ end
32
+
33
+ # Public: returns whether the questionnaire is responded by the user or not.
34
+ def responded_by?(user)
30
35
  query = user.is_a?(String) ? { session_token: user } : { user: }
31
- answers.where(query).any? if questions.present?
36
+ responses.where(query).any? if questions.present?
32
37
  end
33
38
 
34
39
  def pristine?
@@ -43,14 +48,9 @@ module Decidim
43
48
  Decidim::Forms::QuestionnaireParticipants.new(self).count_participants
44
49
  end
45
50
 
46
- # Public: Returns only the actual question types (excludes separators and title_and_description)
47
- def question_types
48
- questions.not_separator.not_title_and_description
49
- end
50
-
51
51
  private
52
52
 
53
- # salt is used to generate secure hash in anonymous answers
53
+ # salt is used to generate secure hash in anonymous responses
54
54
  def set_default_salt
55
55
  return unless defined?(salt)
56
56
 
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Decidim
4
4
  module Forms
5
- # The data store for an Answer in the Decidim::Forms
6
- class Answer < Forms::ApplicationRecord
5
+ # The data store for an Response in the Decidim::Forms
6
+ class Response < Forms::ApplicationRecord
7
7
  include Decidim::DownloadYourData
8
8
  include Decidim::NewsletterParticipant
9
9
  include Decidim::HasAttachments
@@ -13,10 +13,10 @@ module Decidim
13
13
  belongs_to :question, class_name: "Question", foreign_key: "decidim_question_id"
14
14
 
15
15
  has_many :choices,
16
- class_name: "AnswerChoice",
17
- foreign_key: "decidim_answer_id",
16
+ class_name: "ResponseChoice",
17
+ foreign_key: "decidim_response_id",
18
18
  dependent: :destroy,
19
- inverse_of: :answer
19
+ inverse_of: :response
20
20
 
21
21
  validate :user_questionnaire_same_organization
22
22
  validate :question_belongs_to_questionnaire
@@ -29,7 +29,7 @@ module Decidim
29
29
  end
30
30
 
31
31
  def self.export_serializer
32
- Decidim::Forms::DownloadYourDataUserAnswersSerializer
32
+ Decidim::Forms::DownloadYourDataUserResponsesSerializer
33
33
  end
34
34
 
35
35
  def self.newsletter_participant_ids(component)
@@ -37,10 +37,10 @@ module Decidim
37
37
  questionnaires = Decidim::Forms::Questionnaire.includes(:questionnaire_for)
38
38
  .where(questionnaire_for_type: Decidim::Surveys::Survey.name, questionnaire_for_id: surveys.pluck(:id))
39
39
 
40
- answers = Decidim::Forms::Answer.joins(:questionnaire)
41
- .where(questionnaire: questionnaires)
40
+ responses = Decidim::Forms::Response.joins(:questionnaire)
41
+ .where(questionnaire: questionnaires)
42
42
 
43
- answers.pluck(:decidim_user_id).flatten.compact.uniq
43
+ responses.pluck(:decidim_user_id).flatten.compact.uniq
44
44
  end
45
45
 
46
46
  def organization
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Forms
5
+ class ResponseChoice < Forms::ApplicationRecord
6
+ belongs_to :response,
7
+ class_name: "Response",
8
+ foreign_key: "decidim_response_id"
9
+
10
+ belongs_to :response_option,
11
+ class_name: "ResponseOption",
12
+ foreign_key: "decidim_response_option_id"
13
+
14
+ belongs_to :matrix_row,
15
+ class_name: "QuestionMatrixRow",
16
+ foreign_key: "decidim_question_matrix_row_id",
17
+ optional: true
18
+
19
+ validates :matrix_row, presence: true, if: -> { response.question.matrix? }
20
+ end
21
+ end
22
+ end
@@ -2,23 +2,23 @@
2
2
 
3
3
  module Decidim
4
4
  module Forms
5
- class AnswerOption < Forms::ApplicationRecord
5
+ class ResponseOption < Forms::ApplicationRecord
6
6
  include Decidim::TranslatableResource
7
7
 
8
8
  default_scope { order(arel_table[:id].asc) }
9
9
 
10
10
  translatable_fields :body
11
11
 
12
- belongs_to :question, class_name: "Question", foreign_key: "decidim_question_id", counter_cache: :answer_options_count
12
+ belongs_to :question, class_name: "Question", foreign_key: "decidim_question_id", counter_cache: :response_options_count
13
13
 
14
14
  has_many :display_conditions,
15
15
  class_name: "DisplayCondition",
16
- foreign_key: "decidim_answer_option_id",
16
+ foreign_key: "decidim_response_option_id",
17
17
  dependent: :nullify,
18
- inverse_of: :answer_option
18
+ inverse_of: :response_option
19
19
 
20
20
  def translated_body
21
- Decidim::Forms::AnswerOptionPresenter.new(self).translated_body
21
+ Decidim::Forms::ResponseOptionPresenter.new(self).translated_body
22
22
  end
23
23
  end
24
24
  end
@@ -1,10 +1,10 @@
1
1
  import "src/decidim/forms/admin/collapsible_questions"
2
2
 
3
3
  import createEditableForm from "src/decidim/forms/admin/forms"
4
- import createPublicableQuestionAnswersButtons from "src/decidim/forms/admin/publish_answers_buttons"
4
+ import createPublicableQuestionResponsesButtons from "src/decidim/forms/admin/publish_responses_buttons"
5
5
 
6
6
  window.Decidim.createEditableForm = createEditableForm
7
7
 
8
- document.addEventListener("DOMContentLoaded", function() {
9
- document.querySelectorAll("[data-publish-question-answer-action]").forEach((el) => createPublicableQuestionAnswersButtons(el));
8
+ document.addEventListener("turbo:load", () => {
9
+ document.querySelectorAll("[data-publish-question-response-action]").forEach((el) => createPublicableQuestionResponsesButtons(el));
10
10
  });
@@ -1,19 +1,21 @@
1
- (() => {
1
+ document.addEventListener("turbo:load", () => {
2
2
  const getButtons = document.querySelectorAll("button.question--collapse");
3
3
 
4
4
  setTimeout(() => {
5
- [...getButtons].forEach((button) => {
5
+ getButtons.forEach((button) => {
6
6
  if (button.classList.contains("question-error")) {
7
- button.click()
7
+ button.click();
8
8
  }
9
- })
10
- }, 100)
9
+ });
10
+ }, 100);
11
11
 
12
- $("button.collapse-all").on("click", () => {
13
- $("[id$=field]").find("button.question--collapse[aria-expanded='true']").click()
12
+ document.querySelector("button.collapse-all")?.addEventListener("click", () => {
13
+ document.querySelectorAll("[id$=field] button.question--collapse[aria-expanded='true']").
14
+ forEach((button) => button.click());
14
15
  });
15
16
 
16
- $("button.expand-all").on("click", () => {
17
- $("[id$=field]").find("button.question--collapse[aria-expanded='false']").click()
17
+ document.querySelector("button.expand-all")?.addEventListener("click", () => {
18
+ document.querySelectorAll("[id$=field] button.question--collapse[aria-expanded='false']").
19
+ forEach((button) => button.click());
18
20
  });
19
- })(window);
21
+ });
@@ -7,36 +7,35 @@ import AutoSelectOptionsFromUrl from "src/decidim/forms/admin/auto_select_option
7
7
  import createLiveTextUpdateComponent from "src/decidim/forms/admin/live_text_update.component"
8
8
  import AutoButtonsByPositionComponent from "src/decidim/admin/auto_buttons_by_position.component"
9
9
  import AutoLabelByPositionComponent from "src/decidim/admin/auto_label_by_position.component"
10
+ import createSortList from "src/decidim/admin/sort_list.component"
10
11
  import createDynamicFields from "src/decidim/admin/dynamic_fields.component"
11
12
  import createFieldDependentInputs from "src/decidim/admin/field_dependent_inputs.component"
12
13
  import initLanguageChangeSelect from "src/decidim/admin/choose_language"
13
- import sortable from "html5sortable/dist/html5sortable.es"
14
14
 
15
15
  export default function createEditableForm() {
16
16
  const wrapperSelector = ".questionnaire-questions";
17
17
  const fieldSelector = ".questionnaire-question";
18
18
  const questionTypeSelector = "select[name$=\\[question_type\\]]";
19
- const answerOptionFieldSelector = ".questionnaire-question-answer-option";
20
- const answerOptionsWrapperSelector = ".questionnaire-question-answer-options";
21
- const answerOptionRemoveFieldButtonSelector = ".remove-answer-option";
19
+ const responseOptionFieldSelector = ".questionnaire-question-response-option";
20
+ const responseOptionsWrapperSelector = ".questionnaire-question-response-options";
21
+ const responseOptionRemoveFieldButtonSelector = ".remove-response-option";
22
22
  const matrixRowFieldSelector = ".questionnaire-question-matrix-row";
23
23
  const matrixRowsWrapperSelector = ".questionnaire-question-matrix-rows";
24
24
  const matrixRowRemoveFieldButtonSelector = ".remove-matrix-row";
25
25
  const addMatrixRowButtonSelector = ".add-matrix-row";
26
26
  const maxChoicesWrapperSelector = ".questionnaire-question-max-choices";
27
- const responseOptionFreeTextSelector = ".questionnaire-question-answer-option-free-text";
28
27
 
29
28
  const displayConditionFieldSelector = ".questionnaire-question-display-condition";
30
29
  const displayConditionsWrapperSelector = ".questionnaire-question-display-conditions";
31
30
  const displayConditionRemoveFieldButtonSelector = ".remove-display-condition";
32
31
 
33
32
  const displayConditionQuestionSelector = "select[name$=\\[decidim_condition_question_id\\]]";
34
- const displayConditionAnswerOptionSelector = "select[name$=\\[decidim_answer_option_id\\]]";
33
+ const displayConditionResponseOptionSelector = "select[name$=\\[decidim_response_option_id\\]]";
35
34
  const displayConditionTypeSelector = "select[name$=\\[condition_type\\]]";
36
35
  const deletedInputSelector = "input[name$=\\[deleted\\]]";
37
36
 
38
37
  const displayConditionValueWrapperSelector = ".questionnaire-question-display-condition-value";
39
- const displayconditionAnswerOptionWrapperSelector = ".questionnaire-question-display-condition-answer-option";
38
+ const displayconditionResponseOptionWrapperSelector = ".questionnaire-question-display-condition-response-option";
40
39
 
41
40
  const addDisplayConditionButtonSelector = ".add-display-condition";
42
41
 
@@ -75,41 +74,41 @@ export default function createEditableForm() {
75
74
  const MULTIPLE_CHOICE_VALUES = ["single_option", "multiple_option", "sorting", "matrix_single", "matrix_multiple"];
76
75
  const MATRIX_VALUES = ["matrix_single", "matrix_multiple"];
77
76
 
78
- const createAutoMaxChoicesByNumberOfAnswerOptions = (fieldId) => {
77
+ const createAutoMaxChoicesByNumberOfResponseOptions = (fieldId) => {
79
78
  return new AutoSelectOptionsByTotalItemsComponent({
80
79
  wrapperSelector: fieldSelector,
81
80
  selectSelector: `${maxChoicesWrapperSelector} select`,
82
- listSelector: `#${fieldId} ${answerOptionsWrapperSelector} .questionnaire-question-answer-option:not(.hidden)`
81
+ listSelector: `#${fieldId} ${responseOptionsWrapperSelector} .questionnaire-question-response-option:not(.hidden)`
83
82
  })
84
83
  };
85
84
 
86
- const createAutoButtonsByMinItemsForAnswerOptions = (fieldId) => {
85
+ const createAutoButtonsByMinItemsForResponseOptions = (fieldId) => {
87
86
  return new AutoButtonsByMinItemsComponent({
88
87
  wrapperSelector: fieldSelector,
89
- listSelector: `#${fieldId} ${answerOptionsWrapperSelector} .questionnaire-question-answer-option:not(.hidden)`,
88
+ listSelector: `#${fieldId} ${responseOptionsWrapperSelector} .questionnaire-question-response-option:not(.hidden)`,
90
89
  minItems: 2,
91
- hideOnMinItemsOrLessSelector: answerOptionRemoveFieldButtonSelector
90
+ hideOnMinItemsOrLessSelector: responseOptionRemoveFieldButtonSelector
92
91
  })
93
92
  };
94
93
 
95
94
  const createAutoSelectOptionsFromUrl = ($field) => {
96
95
  return new AutoSelectOptionsFromUrl({
97
96
  source: $field.find(displayConditionQuestionSelector),
98
- select: $field.find(displayConditionAnswerOptionSelector),
97
+ select: $field.find(displayConditionResponseOptionSelector),
99
98
  sourceToParams: ($element) => { return { id: $element.val() } }
100
99
  })
101
100
  };
102
101
 
103
- // Listen for sortupdate events from html5sortable (initialized by draggable-table.js)
104
- const setupSortUpdateListener = () => {
105
- const container = document.querySelector(".questionnaire-questions-list:not(.published)");
106
- if (container && !container.dataset.sortListenerAttached) {
107
- container.addEventListener("sortupdate", () => {
102
+ const createSortableList = () => {
103
+ createSortList(".questionnaire-questions-list:not(.published)", {
104
+ handle: ".question-divider",
105
+ placeholder: '<div style="border-style: dashed; border-color: #000"></div>',
106
+ forcePlaceholderSize: true,
107
+ onSortUpdate: () => {
108
108
  autoLabelByPosition.run();
109
109
  autoButtonsByPosition.run();
110
- });
111
- container.dataset.sortListenerAttached = "true";
112
- }
110
+ }
111
+ });
113
112
  };
114
113
 
115
114
  const createDynamicQuestionTitle = (fieldId) => {
@@ -139,18 +138,18 @@ export default function createEditableForm() {
139
138
  }
140
139
  };
141
140
 
142
- const createDynamicFieldsForAnswerOptions = (fieldId) => {
143
- const autoButtons = createAutoButtonsByMinItemsForAnswerOptions(fieldId);
144
- const autoSelectOptions = createAutoMaxChoicesByNumberOfAnswerOptions(fieldId);
141
+ const createDynamicFieldsForResponseOptions = (fieldId) => {
142
+ const autoButtons = createAutoButtonsByMinItemsForResponseOptions(fieldId);
143
+ const autoSelectOptions = createAutoMaxChoicesByNumberOfResponseOptions(fieldId);
145
144
 
146
145
  return createDynamicFields({
147
- placeholderId: "questionnaire-question-answer-option-id",
148
- wrapperSelector: `#${fieldId} ${answerOptionsWrapperSelector}`,
149
- containerSelector: ".questionnaire-question-answer-options-list",
150
- fieldSelector: answerOptionFieldSelector,
151
- addFieldButtonSelector: ".add-answer-option",
152
- fieldTemplateSelector: ".decidim-answer-option-template",
153
- removeFieldButtonSelector: answerOptionRemoveFieldButtonSelector,
146
+ placeholderId: "questionnaire-question-response-option-id",
147
+ wrapperSelector: `#${fieldId} ${responseOptionsWrapperSelector}`,
148
+ containerSelector: ".questionnaire-question-response-options-list",
149
+ fieldSelector: responseOptionFieldSelector,
150
+ addFieldButtonSelector: ".add-response-option",
151
+ fieldTemplateSelector: ".decidim-response-option-template",
152
+ removeFieldButtonSelector: responseOptionRemoveFieldButtonSelector,
154
153
  onAddField: () => {
155
154
  autoButtons.run();
156
155
  autoSelectOptions.run();
@@ -162,7 +161,7 @@ export default function createEditableForm() {
162
161
  });
163
162
  };
164
163
 
165
- const dynamicFieldsForAnswerOptions = {};
164
+ const dynamicFieldsForResponseOptions = {};
166
165
 
167
166
  const createDynamicFieldsForMatrixRows = (fieldId) => {
168
167
  return createDynamicFields({
@@ -201,7 +200,7 @@ export default function createEditableForm() {
201
200
 
202
201
  const isMultiple = isMultipleChoiceOption(selectedQuestionType);
203
202
 
204
- let conditionTypes = ["answered", "not_answered"];
203
+ let conditionTypes = ["responded", "not_responded"];
205
204
 
206
205
  if (isMultiple) {
207
206
  conditionTypes.push("equal");
@@ -237,7 +236,7 @@ export default function createEditableForm() {
237
236
  const onDisplayConditionTypeChange = ($field) => {
238
237
  const value = $field.find(displayConditionTypeSelector).val();
239
238
  const $valueWrapper = $field.find(displayConditionValueWrapperSelector);
240
- const $answerOptionWrapper = $field.find(displayconditionAnswerOptionWrapperSelector);
239
+ const $responseOptionWrapper = $field.find(displayconditionResponseOptionWrapperSelector);
241
240
 
242
241
  const $questionSelector = $field.find(displayConditionQuestionSelector);
243
242
  const selectedQuestionType = getSelectedQuestionType($questionSelector[0]);
@@ -252,10 +251,10 @@ export default function createEditableForm() {
252
251
  }
253
252
 
254
253
  if (isMultiple && (value === "not_equal" || value === "equal")) {
255
- $answerOptionWrapper.show();
254
+ $responseOptionWrapper.show();
256
255
  }
257
256
  else {
258
- $answerOptionWrapper.hide();
257
+ $responseOptionWrapper.hide();
259
258
  }
260
259
  };
261
260
 
@@ -303,8 +302,8 @@ export default function createEditableForm() {
303
302
  createFieldDependentInputs({
304
303
  controllerField: $fieldQuestionTypeSelect,
305
304
  wrapperSelector: fieldSelector,
306
- dependentFieldsSelector: answerOptionsWrapperSelector,
307
- dependentInputSelector: `${answerOptionFieldSelector} input`,
305
+ dependentFieldsSelector: responseOptionsWrapperSelector,
306
+ dependentInputSelector: `${responseOptionFieldSelector} input`,
308
307
  enablingCondition: ($field) => {
309
308
  return isMultipleChoiceOption($field.val());
310
309
  }
@@ -330,23 +329,20 @@ export default function createEditableForm() {
330
329
  }
331
330
  });
332
331
 
333
- dynamicFieldsForAnswerOptions[fieldId] = createDynamicFieldsForAnswerOptions(fieldId);
332
+ dynamicFieldsForResponseOptions[fieldId] = createDynamicFieldsForResponseOptions(fieldId);
334
333
  dynamicFieldsForMatrixRows[fieldId] = createDynamicFieldsForMatrixRows(fieldId);
335
334
  dynamicFieldsForDisplayConditions[fieldId] = createDynamicFieldsForDisplayConditions(fieldId);
336
335
 
337
- const dynamicFieldsAnswerOptions = dynamicFieldsForAnswerOptions[fieldId];
336
+ const dynamicFieldsResponseOptions = dynamicFieldsForResponseOptions[fieldId];
338
337
  const dynamicFieldsMatrixRows = dynamicFieldsForMatrixRows[fieldId];
339
338
 
340
339
  const onQuestionTypeChange = () => {
341
- const $currentField = $fieldQuestionTypeSelect.parents(fieldSelector);
342
- const questionType = $fieldQuestionTypeSelect.val();
343
-
344
- if (isMultipleChoiceOption(questionType)) {
345
- const nOptions = $fieldQuestionTypeSelect.parents(fieldSelector).find(answerOptionFieldSelector).length;
340
+ if (isMultipleChoiceOption($fieldQuestionTypeSelect.val())) {
341
+ const nOptions = $fieldQuestionTypeSelect.parents(fieldSelector).find(responseOptionFieldSelector).length;
346
342
 
347
343
  if (nOptions === 0) {
348
- dynamicFieldsAnswerOptions._addField();
349
- dynamicFieldsAnswerOptions._addField();
344
+ dynamicFieldsResponseOptions._addField();
345
+ dynamicFieldsResponseOptions._addField();
350
346
  }
351
347
  }
352
348
 
@@ -358,12 +354,6 @@ export default function createEditableForm() {
358
354
  dynamicFieldsMatrixRows._addField();
359
355
  }
360
356
  }
361
-
362
- if (questionType === "sorting") {
363
- $currentField.find(responseOptionFreeTextSelector).addClass("hidden");
364
- } else {
365
- $currentField.find(responseOptionFreeTextSelector).removeClass("hidden");
366
- }
367
357
  };
368
358
 
369
359
  $fieldQuestionTypeSelect.on("change", onQuestionTypeChange);
@@ -396,22 +386,13 @@ export default function createEditableForm() {
396
386
  moveDownFieldButtonSelector: ".move-down-question",
397
387
  onAddField: ($field) => {
398
388
  setupInitialQuestionAttributes($field);
399
- setupSortUpdateListener();
389
+ createSortableList();
400
390
 
401
391
  autoLabelByPosition.run();
402
392
  autoButtonsByPosition.run();
403
393
 
404
394
  initLanguageChangeSelect($field.find("select.language-change").toArray());
405
395
 
406
- const sortableContainer = document.querySelector(".questionnaire-questions-list[data-draggable-table]");
407
- if (sortableContainer) {
408
- sortable(sortableContainer, {
409
- forcePlaceholderSize: true,
410
- items: ".questionnaire-question",
411
- handle: sortableContainer.dataset.draggableHandle || ".card-divider"
412
- });
413
- }
414
-
415
396
  // instead of initialize specific stuff, we send an event, with the DOM fragment we wanna update/refresh/bind
416
397
  document.dispatchEvent(new CustomEvent("ajax:loaded", { detail: $field[0] }));
417
398
  },
@@ -419,8 +400,8 @@ export default function createEditableForm() {
419
400
  autoLabelByPosition.run();
420
401
  autoButtonsByPosition.run();
421
402
 
422
- $field.find(answerOptionRemoveFieldButtonSelector).each((idx, el) => {
423
- dynamicFieldsForAnswerOptions[$field.attr("id")]._removeField(el);
403
+ $field.find(responseOptionRemoveFieldButtonSelector).each((idx, el) => {
404
+ dynamicFieldsForResponseOptions[$field.attr("id")]._removeField(el);
424
405
  });
425
406
 
426
407
  $field.find(matrixRowRemoveFieldButtonSelector).each((idx, el) => {
@@ -441,7 +422,7 @@ export default function createEditableForm() {
441
422
  }
442
423
  });
443
424
 
444
- setupSortUpdateListener();
425
+ createSortableList();
445
426
 
446
427
  $(fieldSelector).each((idx, el) => {
447
428
  const $target = $(el);
@@ -1,7 +1,7 @@
1
1
  /**
2
- * Initializes event listeners for a button to toggle the publication status of question answers.
2
+ * Initializes event listeners for a button to toggle the publication status of question responses.
3
3
  *
4
- * The button's `data-publish-question-answer-action` attribute determines whether the action is
4
+ * The button's `data-publish-question-response-action` attribute determines whether the action is
5
5
  * to "publish" or "unpublish." Based on this attribute, the function makes an AJAX request using
6
6
  * Rails' AJAX helper and updates the button's state and associated labels.
7
7
  *
@@ -10,8 +10,8 @@
10
10
  * @returns {void}
11
11
  *
12
12
  * Button element must have the following data attributes:
13
- * - `data-publish-question-answer-action`: Specifies the current action ("publish" or "unpublish").
14
- * - `data-publish-question-answer-question-url`: Specifies the endpoint URL for the AJAX request.
13
+ * - `data-publish-question-response-action`: Specifies the current action ("publish" or "unpublish").
14
+ * - `data-publish-question-response-question-url`: Specifies the endpoint URL for the AJAX request.
15
15
  *
16
16
  * The DOM structure must include:
17
17
  * - A parent element with the class `toggle__switch-toggle`.
@@ -28,16 +28,16 @@
28
28
  * <span class="label alert hidden">Unpublished</span>
29
29
  * <span>
30
30
  * <input
31
- * data-publish-question-answer-action="unpublish"
32
- * data-publish-question-answer-question-url="/path/to/publish/or/unpublish">
31
+ * data-publish-question-response-action="unpublish"
32
+ * data-publish-question-response-question-url="/path/to/publish/or/unpublish">
33
33
  * </span>
34
34
  * </label>
35
35
  * ```
36
36
  */
37
- export default function createPublicableQuestionAnswersButtons(button) {
37
+ export default function createPublicableQuestionResponsesButtons(button) {
38
38
  button.addEventListener("click", () => {
39
- const action = button.dataset.publishQuestionAnswerAction;
40
- const url = button.dataset.publishQuestionAnswerQuestionUrl;
39
+ const action = button.dataset.publishQuestionResponseAction;
40
+ const url = button.dataset.publishQuestionResponseQuestionUrl;
41
41
  const buttonText = button.closest(".toggle__switch-toggle").querySelector(".toggle__switch-trigger-text");
42
42
  const publishedLabel = buttonText.querySelector(".label.success");
43
43
  const unpublishedLabel = buttonText.querySelector(".label.alert");
@@ -48,7 +48,7 @@ export default function createPublicableQuestionAnswersButtons(button) {
48
48
  url: url,
49
49
  type: "PUT",
50
50
  success: function() {
51
- button.setAttribute("data-publish-question-answer-action", "unpublish")
51
+ button.setAttribute("data-publish-question-response-action", "unpublish")
52
52
  publishedLabel.classList.remove("hidden");
53
53
  unpublishedLabel.classList.add("hidden");
54
54
  }
@@ -59,14 +59,14 @@ export default function createPublicableQuestionAnswersButtons(button) {
59
59
  url: url,
60
60
  type: "DELETE",
61
61
  success: function() {
62
- button.setAttribute("data-publish-question-answer-action", "publish")
62
+ button.setAttribute("data-publish-question-response-action", "publish")
63
63
  unpublishedLabel.classList.remove("hidden");
64
64
  publishedLabel.classList.add("hidden");
65
65
  }
66
66
  });
67
67
  break;
68
68
  default:
69
- console.log(`Publish questions answers: Unknown action ${action}`);
69
+ console.log(`Publish questions responses: Unknown action ${action}`);
70
70
  }
71
71
  });
72
72
  }