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
@@ -5,7 +5,7 @@ class DisplayCondition {
5
5
  this.wrapperField = options.wrapperField;
6
6
  this.type = options.type;
7
7
  this.conditionQuestion = options.conditionQuestion;
8
- this.answerOption = options.answerOption;
8
+ this.responseOption = options.responseOption;
9
9
  this.mandatory = options.mandatory;
10
10
  this.value = options.value;
11
11
  this.onFulfilled = options.onFulfilled;
@@ -34,7 +34,7 @@ class DisplayCondition {
34
34
  if (checked) {
35
35
  const text = $(el).find("input[name$=\\[custom_body\\]]").val();
36
36
  const value = $input.val();
37
- const id = $(el).find("input[name$=\\[answer_option_id\\]]").val();
37
+ const id = $(el).find("input[name$=\\[response_option_id\\]]").val();
38
38
 
39
39
  multipleInput.push({ id, value, text });
40
40
  }
@@ -54,7 +54,7 @@ class DisplayCondition {
54
54
  return $conditionWrapperField.find(".js-collection-input").find("input:not([type='hidden'])");
55
55
  }
56
56
 
57
- checkAnsweredCondition(value) {
57
+ checkRespondedCondition(value) {
58
58
  if (typeof (value) !== "object") {
59
59
  return Boolean(value);
60
60
  }
@@ -62,20 +62,20 @@ class DisplayCondition {
62
62
  return Boolean(value.some((it) => it.value));
63
63
  }
64
64
 
65
- checkNotAnsweredCondition(value) {
66
- return !this.checkAnsweredCondition(value);
65
+ checkNotRespondedCondition(value) {
66
+ return !this.checkRespondedCondition(value);
67
67
  }
68
68
 
69
69
  checkEqualCondition(value) {
70
70
  if (value.length) {
71
- return value.some((it) => it.id === this.answerOption.toString());
71
+ return value.some((it) => it.id === this.responseOption.toString());
72
72
  }
73
73
  return false;
74
74
  }
75
75
 
76
76
  checkNotEqualCondition(value) {
77
77
  if (value.length) {
78
- return value.every((it) => it.id !== this.answerOption.toString());
78
+ return value.every((it) => it.id !== this.responseOption.toString());
79
79
  }
80
80
  return false;
81
81
  }
@@ -99,11 +99,11 @@ class DisplayCondition {
99
99
  let fulfilled = false;
100
100
 
101
101
  switch (this.type) {
102
- case "answered":
103
- fulfilled = this.checkAnsweredCondition(value);
102
+ case "responded":
103
+ fulfilled = this.checkRespondedCondition(value);
104
104
  break;
105
- case "not_answered":
106
- fulfilled = this.checkNotAnsweredCondition(value);
105
+ case "not_responded":
106
+ fulfilled = this.checkNotRespondedCondition(value);
107
107
  break;
108
108
  case "equal":
109
109
  fulfilled = this.checkEqualCondition(value);
@@ -143,7 +143,7 @@ class DisplayConditionsComponent {
143
143
  wrapperField: this.wrapperField,
144
144
  type: $condition.data("type"),
145
145
  conditionQuestion: $condition.data("condition"),
146
- answerOption: $condition.data("option"),
146
+ responseOption: $condition.data("option"),
147
147
  mandatory: $condition.data("mandatory"),
148
148
  value: $condition.data("value"),
149
149
  onFulfilled: (fulfilled) => {
@@ -13,7 +13,7 @@ import createDisplayConditions from "src/decidim/forms/display_conditions.compon
13
13
  import createMaxChoicesAlertComponent from "src/decidim/forms/max_choices_alert.component"
14
14
  import { preventUnload } from "src/decidim/utilities/dom"
15
15
 
16
- $(() => {
16
+ document.addEventListener("turbo:load", () => {
17
17
  $(".js-radio-button-collection, .js-check-box-collection").each((idx, el) => {
18
18
  createOptionAttachedInputs({
19
19
  wrapperField: $(el),
@@ -22,7 +22,7 @@ $(() => {
22
22
  });
23
23
  });
24
24
 
25
- $.unique($(".js-check-box-collection").parents(".answer")).each((idx, el) => {
25
+ $.unique($(".js-check-box-collection").parents(".response")).each((idx, el) => {
26
26
  const maxChoices = $(el).data("max-choices");
27
27
  if (maxChoices) {
28
28
  createMaxChoicesAlertComponent({
@@ -35,39 +35,18 @@ $(() => {
35
35
  }
36
36
  });
37
37
 
38
- document.querySelectorAll(".js-sortable-check-box-collection").forEach((el) => {
38
+ document.querySelectorAll(".js-sortable-check-box-collection").forEach((el) => new DragonDrop(el, {
39
+ handle: false,
40
+ item: ".js-collection-input"
41
+ }));
39
42
 
40
- /**
41
- * Due to a bug reported in https://github.com/decidim/decidim/issues/15191
42
- * we have to listen to the `drag` event and prevent the scrolling
43
- * and enabling it back again after it.
44
- */
45
-
46
- let preventScroll = function(event) {
47
- event.preventDefault();
48
- }
49
-
50
- el.addEventListener("touchmove", (event) => {
51
- preventScroll(event);
52
- }, { passive: false });
53
-
54
- el.addEventListener("touchend", () => {
55
- el.removeEventListener("touchmove", preventScroll)
56
- });
57
-
58
- return new DragonDrop(el, {
59
- handle: false,
60
- item: ".js-collection-input"
61
- });
62
- });
63
-
64
- $(".answer-questionnaire .question[data-conditioned='true']").each((idx, el) => {
43
+ $(".response-questionnaire .question[data-conditioned='true']").each((idx, el) => {
65
44
  createDisplayConditions({
66
45
  wrapperField: $(el)
67
46
  });
68
47
  });
69
48
 
70
- const form = document.querySelector("form.answer-questionnaire");
49
+ const form = document.querySelector("form.response-questionnaire");
71
50
  if (form) {
72
51
  const safePath = form.dataset.safePath.split("?")[0];
73
52
  let exitUrl = "";
@@ -1,4 +1,4 @@
1
- .answer-questionnaire {
1
+ .response-questionnaire {
2
2
  @apply border-t-2 border-background pt-8;
3
3
 
4
4
  &__step {
@@ -22,7 +22,7 @@
22
22
  }
23
23
 
24
24
  &__question-label {
25
- @apply text-black text-xl font-semibold;
25
+ @apply text-black text-xl font-semibold relative before:content-[attr(data-response-idx)] before:w-6 before:h-6 md:before:absolute md:before:-left-4 md:before:-translate-x-full before:inline-flex before:justify-center before:rounded-full before:bg-background before:text-lg before:text-gray-2 before:font-semibold;
26
26
  }
27
27
 
28
28
  &__question-description {
@@ -19,7 +19,7 @@ module Decidim
19
19
  participant.ip_hash || "-"
20
20
  end
21
21
 
22
- def answered_at
22
+ def responded_at
23
23
  participant.created_at
24
24
  end
25
25
 
@@ -30,23 +30,23 @@ module Decidim
30
30
  end
31
31
 
32
32
  def status
33
- I18n.t(registered? ? "registered" : "unregistered", scope: "decidim.forms.user_answers_serializer")
33
+ I18n.t(registered? ? "registered" : "unregistered", scope: "decidim.forms.user_responses_serializer")
34
34
  end
35
35
 
36
- def answers
37
- siblings.map { |answer| QuestionnaireAnswerPresenter.new(answer:) }
36
+ def responses
37
+ siblings.map { |response| QuestionnaireResponsePresenter.new(response:) }
38
38
  end
39
39
 
40
- def first_short_answer
41
- short = siblings.where(decidim_forms_questions: { question_type: %w(short_answer) })
40
+ def first_short_response
41
+ short = siblings.where(decidim_forms_questions: { question_type: %w(short_response) })
42
42
  short.first
43
43
  end
44
44
 
45
45
  def completion
46
- with_body = siblings.where(decidim_forms_questions: { question_type: %w(short_answer long_answer) })
46
+ with_body = siblings.where(decidim_forms_questions: { question_type: %w(short_response long_response) })
47
47
  .where.not(body: "").count
48
- with_choices = siblings.where.not(decidim_forms_questions: { question_type: %w(short_answer long_answer) })
49
- .where("decidim_forms_answers.id IN (SELECT decidim_answer_id FROM decidim_forms_answer_choices)").count
48
+ with_choices = siblings.where.not(decidim_forms_questions: { question_type: %w(short_response long_response) })
49
+ .where("decidim_forms_responses.id IN (SELECT decidim_response_id FROM decidim_forms_response_choices)").count
50
50
 
51
51
  (with_body + with_choices).to_f / questionnaire.questions.not_separator.not_title_and_description.count * 100
52
52
  end
@@ -54,10 +54,10 @@ module Decidim
54
54
  private
55
55
 
56
56
  def siblings
57
- Answer.not_separator
58
- .not_title_and_description
59
- .where(questionnaire:, session_token: participant.session_token)
60
- .joins(:question).order("decidim_forms_questions.position ASC")
57
+ Response.not_separator
58
+ .not_title_and_description
59
+ .where(questionnaire:, session_token: participant.session_token)
60
+ .joins(:question).order("decidim_forms_questions.position ASC")
61
61
  end
62
62
  end
63
63
  end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Forms
5
+ module Admin
6
+ #
7
+ # Presenter for questionnaire response
8
+ #
9
+ class QuestionnaireResponsePresenter < SimpleDelegator
10
+ delegate :content_tag, :safe_join, :link_to, :number_to_human_size, to: :view_context
11
+
12
+ include Decidim::TranslatableAttributes
13
+
14
+ def response
15
+ __getobj__.fetch(:response)
16
+ end
17
+
18
+ def view_context
19
+ __getobj__.fetch(:view_context, ActionController::Base.new.view_context)
20
+ end
21
+
22
+ def question
23
+ translated_attribute(response.question.body)
24
+ end
25
+
26
+ def body
27
+ return response.body if response.body.present?
28
+ return attachments if response.attachments.any?
29
+ return "-" if response.choices.empty?
30
+
31
+ choices = response.choices.map do |choice|
32
+ {
33
+ response_option_body: choice.try(:response_option).try(:translated_body),
34
+ choice_body: body_or_custom_body(choice)
35
+ }
36
+ end
37
+
38
+ return choice(choices.first) if response.question.question_type == "single_option"
39
+
40
+ content_tag(:ul) do
41
+ safe_join(choices.map { |c| choice(c) })
42
+ end
43
+ end
44
+
45
+ def attachments
46
+ content_tag(:ul) do
47
+ safe_join(response.attachments.map { |a| pretty_attachment(a) })
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def pretty_attachment(attachment)
54
+ # rubocop:disable Style/StringConcatenation
55
+ # Interpolating strings that are `html_safe` is problematic with Rails.
56
+ content_tag :li do
57
+ link_to(attachment.url, target: "_blank", rel: "noopener noreferrer") do
58
+ content_tag(:span) do
59
+ translated_attribute(attachment.title).presence ||
60
+ I18n.t("download_attachment", scope: "decidim.forms.questionnaire_response_presenter")
61
+ end + " " + content_tag(:small) do
62
+ "#{attachment.file_type} #{number_to_human_size(attachment.file_size)}"
63
+ end
64
+ end
65
+ end
66
+ # rubocop:enable Style/StringConcatenation
67
+ end
68
+
69
+ def choice(choice_hash)
70
+ content_tag :li do
71
+ render_body_for choice_hash
72
+ end
73
+ end
74
+
75
+ def render_body_for(choice_hash)
76
+ return choice_hash[:response_option_body] if choice_hash[:choice_body].blank?
77
+
78
+ "#{choice_hash[:response_option_body]} (#{choice_hash[:choice_body]})"
79
+ end
80
+
81
+ def body_or_custom_body(choice)
82
+ return choice.custom_body if choice.try(:custom_body).present?
83
+
84
+ ""
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -15,11 +15,11 @@ module Decidim
15
15
  class QuestionPresenter < Decidim::Log::BasePresenter
16
16
  private
17
17
 
18
- # i18n-tasks-use t("decidim.forms.admin_log.question.publish_answers")
19
- # i18n-tasks-use t("decidim.forms.admin_log.question.unpublish_answers")
18
+ # i18n-tasks-use t("decidim.forms.admin_log.question.publish_responses")
19
+ # i18n-tasks-use t("decidim.forms.admin_log.question.unpublish_responses")
20
20
  def action_string
21
21
  case action
22
- when "publish_answers", "unpublish_answers"
22
+ when "publish_responses", "unpublish_responses"
23
23
  "decidim.forms.admin_log.question.#{action}"
24
24
  else
25
25
  super
@@ -3,9 +3,9 @@
3
3
  module Decidim
4
4
  module Forms
5
5
  #
6
- # Decorator for answer_options
6
+ # Decorator for response_options
7
7
  #
8
- class AnswerOptionPresenter < SimpleDelegator
8
+ class ResponseOptionPresenter < SimpleDelegator
9
9
  include Decidim::TranslationsHelper
10
10
 
11
11
  def translated_body
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Decidim
4
4
  module Forms
5
- # A class used to collect user answers for a questionnaire
5
+ # A class used to collect user responses for a questionnaire
6
6
  class QuestionnaireParticipant < Decidim::Query
7
7
  # Syntactic sugar to initialize the class and return the queried objects.
8
8
  #
@@ -23,12 +23,12 @@ module Decidim
23
23
 
24
24
  # Returns query with participant info
25
25
  def query
26
- answers.select(:session_token, :decidim_user_id, :ip_hash).first
26
+ responses.select(:session_token, :decidim_user_id, :ip_hash).first
27
27
  end
28
28
 
29
- # Finds the participant's answers.
30
- def answers
31
- Answer.where(questionnaire: @questionnaire, session_token: @token)
29
+ # Finds the participant's responses.
30
+ def responses
31
+ Response.where(questionnaire: @questionnaire, session_token: @token)
32
32
  end
33
33
  end
34
34
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Decidim
4
4
  module Forms
5
- # A class used to collect user answers for a questionnaire
5
+ # A class used to collect user responses for a questionnaire
6
6
  class QuestionnaireParticipants < Decidim::Query
7
7
  # Syntactic sugar to initialize the class and return the queried objects.
8
8
  #
@@ -18,12 +18,12 @@ module Decidim
18
18
  @questionnaire = questionnaire
19
19
  end
20
20
 
21
- # Finds all Answers for the questionnaire (unique session_tokens).
22
- # Because exporters only have access to the Answers this
21
+ # Finds all Responses for the questionnaire (unique session_tokens).
22
+ # Because exporters only have access to the Responses this
23
23
  # is used as an indirect way to access the participants
24
24
  # (see #participants and #participant)
25
25
  def query
26
- Answer.where(questionnaire: @questionnaire)
26
+ Response.where(questionnaire: @questionnaire)
27
27
  end
28
28
 
29
29
  def participant(session_token)
@@ -31,8 +31,8 @@ module Decidim
31
31
  end
32
32
 
33
33
  def participants
34
- subquery = query.select("DISTINCT ON (decidim_forms_answers.session_token) decidim_forms_answers.*")
35
- Answer.select("*").from(subquery).order(:created_at)
34
+ subquery = query.select("DISTINCT ON (decidim_forms_responses.session_token) decidim_forms_responses.*")
35
+ Response.select("*").from(subquery).order(:created_at)
36
36
  end
37
37
 
38
38
  def count_participants
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Forms
5
+ # A class used to collect user responses for a questionnaire
6
+ class QuestionnaireUserResponses < Decidim::Query
7
+ # Syntactic sugar to initialize the class and return the queried objects.
8
+ #
9
+ # questionnaire - a Questionnaire object
10
+ def self.for(questionnaire)
11
+ new(questionnaire).query
12
+ end
13
+
14
+ # Initializes the class.
15
+ #
16
+ # questionnaire = a Questionnaire object
17
+ def initialize(questionnaire)
18
+ @questionnaire = questionnaire
19
+ end
20
+
21
+ # Finds and group responses by user for each questionnaire's question.
22
+ def query
23
+ responses = Response.not_separator
24
+ .not_title_and_description
25
+ .joins(:question)
26
+ .where(questionnaire: @questionnaire)
27
+
28
+ responses.sort_by { |response| response.question.position.to_i }.group_by { |a| a.user || a.session_token }.values
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,13 +1,13 @@
1
1
  <% display_condition = form.object %>
2
2
 
3
- <div class="card questionnaire-question-display-condition">
3
+ <div class="card questionnaire-question-display-condition m-4">
4
4
  <div class="card-divider">
5
5
  <h2 class="card-title">
6
6
  <span><%= t(".display_condition") %></span>
7
7
  <% if editable %>
8
- <button class="button button__sm button__transparent-secondary small alert remove-display-condition button--title">
8
+ <button class="button button__xs button__transparent-secondary small alert remove-display-condition button--title">
9
9
  <%= icon("delete-bin-line") %>
10
- <%= t(".remove") %>
10
+ <span class="hidden md:block"><%= t(".remove") %></span>
11
11
  </button>
12
12
  <% end %>
13
13
  </h2>
@@ -27,7 +27,7 @@
27
27
  options_for_select(display_condition.questions_for_select(questionnaire, question.id), selected: display_condition.decidim_condition_question_id),
28
28
  { prompt: t(".select_condition_question"), label: t(".condition_question") },
29
29
  disabled: !editable,
30
- data: { url: answer_options_url(id: question.id) }
30
+ data: { url: response_options_url(id: question.id) }
31
31
  )
32
32
  %>
33
33
  </div>
@@ -41,14 +41,14 @@
41
41
  )
42
42
  %>
43
43
  </div>
44
- <div class="row column questionnaire-question-display-condition-answer-option">
44
+ <div class="row column questionnaire-question-display-condition-response-option">
45
45
  <%=
46
46
  form.select(
47
- :decidim_answer_option_id,
48
- options_from_collection_for_select(display_condition.answer_options, :id, :translated_body, selected: display_condition.decidim_answer_option_id),
49
- { prompt: t(".select_answer_option"), label: t(".answer_option") },
47
+ :decidim_response_option_id,
48
+ options_from_collection_for_select(display_condition.response_options, :id, :translated_body, selected: display_condition.decidim_response_option_id),
49
+ { prompt: t(".select_response_option"), label: t(".response_option") },
50
50
  disabled: !editable,
51
- data: { selected: display_condition.decidim_answer_option_id }
51
+ data: { selected: display_condition.decidim_response_option_id }
52
52
  )
53
53
  %>
54
54
  </div>
@@ -5,10 +5,10 @@
5
5
  <%= form.translated :text_field, :title, autofocus: true, aria: { label: :title } %>
6
6
  </div>
7
7
  <div class="row column">
8
- <%= form.translated :editor, :description, toolbar: :full, lines: 30, label: t("models.components.description", scope: "decidim.forms.admin"), aria: { label: :description } %>
8
+ <%= form.translated :editor, :description, toolbar: :full, lines: 8, label: t("models.components.description", scope: "decidim.forms.admin"), aria: { label: :description } %>
9
9
  </div>
10
10
  <div class="row column">
11
- <%= form.translated :editor, :tos, toolbar: :content, lines: 10, label: t("models.components.tos", scope: "decidim.forms.admin"), aria: { label: :tos } %>
11
+ <%= form.translated :editor, :tos, toolbar: :content, lines: 8, label: t("models.components.tos", scope: "decidim.forms.admin"), aria: { label: :tos } %>
12
12
  </div>
13
13
  </div>
14
14
  </div>
@@ -1,13 +1,13 @@
1
1
  <% matrix_row = form.object %>
2
2
 
3
- <div class="card questionnaire-question-matrix-row">
3
+ <div class="card questionnaire-question-matrix-row mt-4">
4
4
  <div class="card-divider">
5
5
  <h2 class="card-title">
6
6
  <span><%= t(".matrix_row") %></span>
7
7
  <% if editable %>
8
- <button class="button button__sm button__transparent-secondary small alert remove-matrix-row button--title mb-2">
8
+ <button class="button button__xs button__transparent-secondary small alert remove-matrix-row button--title mb-2">
9
9
  <%= icon("delete-bin-line") %>
10
- <%= t(".remove") %>
10
+ <span class="hidden md:block"><%= t(".remove") %></span>
11
11
  </button>
12
12
  <% end %>
13
13
  </h2>
@@ -1,12 +1,12 @@
1
1
  <% question = form.object %>
2
2
  <% is_expanded = question.errors.any? %>
3
3
  <div class="card questionnaire-question" id="accordion-<%= id %>-field">
4
- <div class="form__wrapper" data-component="accordion">
5
- <%= content_tag :div, class: ["card-divider", ("hover:cursor-grab" if editable)] do %>
4
+ <div class="form__wrapper" data-controller="accordion">
5
+ <div class="card-divider">
6
6
  <h2 class="card-title flex items-center">
7
7
  <span>
8
8
  <% if editable %>
9
- <%= icon("draggable", class: "dragger") %>
9
+ <%== icon("drag-move-2-fill") %>
10
10
  <% end %>
11
11
  <%= dynamic_title(translated_attribute(question.body), class: "question-title-statement", max_length: 50, omission: "...", placeholder: t(".question")) %>
12
12
  </span>
@@ -23,16 +23,26 @@
23
23
  </button>
24
24
 
25
25
  <% if editable %>
26
- <button type="button" class="button button__xs button__transparent-secondary small alert remove-question button--title">
26
+ <button class="button button__xs button__transparent-secondary small alert move-up-question button--title">
27
+ <%= icon("arrow-up-line") %>
28
+ <span class="hidden md:block"><%= t(".up") %></span>
29
+ </button>
30
+
31
+ <button class="button button__xs button__transparent-secondary small alert move-down-question button--title">
32
+ <%= icon("arrow-down-line") %>
33
+ <span class="hidden md:block"><%= t(".down") %></span>
34
+ </button>
35
+
36
+ <button class="button button__xs button__transparent-secondary small alert remove-question button--title">
27
37
  <%= icon("delete-bin-line") %>
28
- <%= t(".remove") %>
38
+ <span class="hidden md:block"><%= t(".remove") %></span>
29
39
  </button>
30
40
  <% end %>
31
41
  </span>
32
42
  </h2>
33
- <% end %>
43
+ </div>
34
44
 
35
- <div id="panel-<%= id %>-question-card" class="panel-question-card card-section collapsible <%= "panel-error" if is_expanded %>">
45
+ <div id="panel-<%= id %>-question-card" class="panel-question-card card-section collapsible <%= "panel-error" if is_expanded %>" aria-hidden="<%= is_expanded ? "false" : "true" %>">
36
46
  <div class="row column">
37
47
  <%=
38
48
  form.translated(
@@ -108,23 +118,23 @@
108
118
  </div>
109
119
 
110
120
  <% if editable %>
111
- <button class="button button__sm button__secondary add-matrix-row"><%= t(".add_matrix_row") %></button>
121
+ <button class="button button__xs button__transparent-secondary mt-4 add-matrix-row"><%= t(".add_matrix_row") %></button>
112
122
  <% end %>
113
123
  </div>
114
124
  </div>
115
125
 
116
- <div class="questionnaire-question-answer-options" data-template="<%= answer_option_template_selector %>">
117
- <div class="questionnaire-question-answer-options-list">
118
- <% question.answer_options.each do |answer_option| %>
119
- <%= fields_for "questions[questions][#{question.to_param}][answer_options][]", answer_option do |answer_option_form| %>
120
- <%= render "decidim/forms/admin/questionnaires/answer_option", form: answer_option_form, question:, editable: %>
126
+ <div class="questionnaire-question-response-options" data-template="<%= response_option_template_selector %>">
127
+ <div class="questionnaire-question-response-options-list">
128
+ <% question.response_options.each do |response_option| %>
129
+ <%= fields_for "questions[questions][#{question.to_param}][response_options][]", response_option do |response_option_form| %>
130
+ <%= render "decidim/forms/admin/questionnaires/response_option", form: response_option_form, question:, editable: %>
121
131
  <% end %>
122
132
  <% end %>
123
133
  </div>
124
134
 
125
135
  <% if editable %>
126
136
  <div class="row column">
127
- <button type="button" class="button button__sm button__secondary mt-4 add-answer-option"><%= t(".add_answer_option") %></button>
137
+ <button type="button" class="button button__xs button__transparent-secondary mt-4 add-response-option"><%= t(".add_response_option") %></button>
128
138
  </div>
129
139
  <% end %>
130
140
  </div>
@@ -152,7 +162,7 @@
152
162
  <% if editable %>
153
163
  <% disabled = !question.persisted? %>
154
164
  <div class="row column">
155
- <button <%= "disabled" if disabled %> title="<%= disabled ? t(".add_display_condition_info") : t(".add_display_condition_info") %>" class="button button__sm button__secondary mt-4 add-display-condition"><%= t(".add_display_condition") %></button>
165
+ <button <%= "disabled" if disabled %> title="<%= disabled ? t(".add_display_condition_info") : t(".add_display_condition_info") %>" class="button button__xs button__transparent-secondary mt-4 add-display-condition"><%= t(".add_display_condition") %></button>
156
166
  </div>
157
167
  <% end %>
158
168
  </div>