decidim-forms 0.30.2 → 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.
- checksums.yaml +4 -4
- data/README.md +10 -10
- data/app/cells/decidim/forms/matrix_readonly/show.erb +1 -1
- data/app/cells/decidim/forms/matrix_readonly_cell.rb +3 -3
- data/app/cells/decidim/forms/question_readonly/show.erb +5 -5
- data/app/cells/decidim/forms/question_readonly/title_and_description.erb +3 -3
- data/app/cells/decidim/forms/response_readonly_cell.rb +9 -0
- data/app/cells/decidim/forms/step_navigation/show.erb +3 -3
- data/app/cells/decidim/forms/step_navigation_cell.rb +3 -3
- data/app/commands/decidim/forms/admin/update_questions.rb +6 -6
- data/app/commands/decidim/forms/{answer_questionnaire.rb → response_questionnaire.rb} +35 -35
- data/app/controllers/decidim/forms/admin/concerns/has_questionnaire.rb +12 -12
- data/app/controllers/decidim/forms/admin/concerns/{has_questionnaire_answers.rb → has_questionnaire_responses.rb} +23 -19
- data/app/controllers/decidim/forms/concerns/has_questionnaire.rb +38 -33
- data/app/forms/decidim/forms/admin/display_condition_form.rb +14 -14
- data/app/forms/decidim/forms/admin/question_form.rb +3 -3
- data/app/forms/decidim/forms/admin/{answer_option_form.rb → response_option_form.rb} +3 -3
- data/app/forms/decidim/forms/questionnaire_form.rb +8 -9
- data/app/forms/decidim/forms/{answer_choice_form.rb → response_choice_form.rb} +5 -5
- data/app/forms/decidim/forms/{answer_form.rb → response_form.rb} +9 -9
- data/app/helpers/decidim/forms/admin/application_helper.rb +2 -2
- data/app/helpers/decidim/forms/admin/concerns/{has_questionnaire_answers_pagination_helper.rb → has_questionnaire_responses_pagination_helper.rb} +4 -4
- data/app/helpers/decidim/forms/admin/concerns/{has_questionnaire_answers_url_helper.rb → has_questionnaire_responses_url_helper.rb} +5 -5
- data/app/helpers/decidim/forms/admin/questionnaire_responses_helper.rb +32 -0
- data/app/helpers/decidim/forms/application_helper.rb +1 -2
- data/app/jobs/decidim/forms/{export_questionnaire_answers_job.rb → export_questionnaire_responses_job.rb} +5 -5
- data/app/models/decidim/forms/display_condition.rb +20 -20
- data/app/models/decidim/forms/question.rb +14 -13
- data/app/models/decidim/forms/question_matrix_row.rb +1 -1
- data/app/models/decidim/forms/questionnaire.rb +11 -6
- data/app/models/decidim/forms/{answer.rb → response.rb} +9 -9
- data/app/models/decidim/forms/response_choice.rb +22 -0
- data/app/models/decidim/forms/{answer_option.rb → response_option.rb} +5 -5
- data/app/packs/entrypoints/decidim_forms_admin.js +3 -3
- data/app/packs/src/decidim/forms/admin/collapsible_questions.js +12 -10
- data/app/packs/src/decidim/forms/admin/forms.js +35 -35
- data/app/packs/src/decidim/forms/admin/{publish_answers_buttons.js → publish_responses_buttons.js} +12 -12
- data/app/packs/src/decidim/forms/display_conditions.component.js +12 -12
- data/app/packs/src/decidim/forms/forms.js +4 -4
- data/app/packs/stylesheets/decidim/forms/forms.scss +2 -2
- data/app/presenters/decidim/forms/admin/questionnaire_participant_presenter.rb +13 -13
- data/app/presenters/decidim/forms/admin/{questionnaire_answer_presenter.rb → questionnaire_response_presenter.rb} +15 -15
- data/app/presenters/decidim/forms/admin_log/question_presenter.rb +3 -3
- data/app/presenters/decidim/forms/{answer_option_presenter.rb → response_option_presenter.rb} +2 -2
- data/app/queries/decidim/forms/questionnaire_participant.rb +5 -5
- data/app/queries/decidim/forms/questionnaire_participants.rb +6 -6
- data/app/queries/decidim/forms/questionnaire_user_responses.rb +32 -0
- data/app/views/decidim/forms/admin/questionnaires/_display_condition.html.erb +9 -9
- data/app/views/decidim/forms/admin/questionnaires/_form.html.erb +2 -2
- data/app/views/decidim/forms/admin/questionnaires/_matrix_row.html.erb +3 -3
- data/app/views/decidim/forms/admin/questionnaires/_question.html.erb +16 -16
- data/app/views/decidim/forms/admin/questionnaires/_questions_form.html.erb +11 -10
- data/app/views/decidim/forms/admin/questionnaires/{_answer_option.html.erb → _response_option.html.erb} +7 -7
- data/app/views/decidim/forms/admin/questionnaires/_response_option_template.html.erb +7 -0
- data/app/views/decidim/forms/admin/questionnaires/_separator.html.erb +10 -7
- data/app/views/decidim/forms/admin/questionnaires/_title_and_description.html.erb +7 -7
- data/app/views/decidim/forms/admin/questionnaires/edit.html.erb +1 -1
- data/app/views/decidim/forms/admin/questionnaires/edit_questions.html.erb +8 -8
- data/app/views/decidim/forms/admin/questionnaires/responses/index.html.erb +80 -0
- data/app/views/decidim/forms/admin/questionnaires/responses/show.html.erb +43 -0
- data/app/views/decidim/forms/questionnaires/_questionnaire.html.erb +22 -26
- data/app/views/decidim/forms/questionnaires/_questionnaire_readonly.html.erb +8 -3
- data/app/views/decidim/forms/questionnaires/_response.html.erb +61 -0
- data/app/views/decidim/forms/questionnaires/edit.html.erb +2 -2
- data/app/views/decidim/forms/questionnaires/responses/_files.html.erb +1 -0
- data/app/views/decidim/forms/questionnaires/responses/_long_response.html.erb +3 -0
- data/app/views/decidim/forms/questionnaires/{answers → responses}/_matrix_multiple.html.erb +16 -15
- data/app/views/decidim/forms/questionnaires/{answers → responses}/_matrix_single.html.erb +17 -16
- data/app/views/decidim/forms/questionnaires/responses/_multiple_option.html.erb +26 -0
- data/app/views/decidim/forms/questionnaires/responses/_separator.html.erb +1 -0
- data/app/views/decidim/forms/questionnaires/responses/_short_response.html.erb +3 -0
- data/app/views/decidim/forms/questionnaires/responses/_single_option.html.erb +33 -0
- data/app/views/decidim/forms/questionnaires/responses/_sorting.html.erb +26 -0
- data/app/views/decidim/forms/questionnaires/responses/_title_and_description.html.erb +1 -0
- data/app/views/decidim/forms/questionnaires/show.html.erb +10 -10
- data/config/assets.rb +2 -2
- data/config/locales/ar.yml +0 -22
- data/config/locales/bg.yml +3 -36
- data/config/locales/ca-IT.yml +73 -73
- data/config/locales/ca.yml +73 -73
- data/config/locales/cs.yml +72 -75
- data/config/locales/de.yml +72 -72
- data/config/locales/el.yml +3 -36
- data/config/locales/en.yml +83 -83
- data/config/locales/es-MX.yml +75 -75
- data/config/locales/es-PY.yml +75 -75
- data/config/locales/es.yml +73 -73
- data/config/locales/eu.yml +71 -71
- data/config/locales/fi-plain.yml +71 -71
- data/config/locales/fi.yml +69 -69
- data/config/locales/fr-CA.yml +70 -67
- data/config/locales/fr.yml +70 -67
- data/config/locales/ga-IE.yml +0 -7
- data/config/locales/gl.yml +0 -22
- data/config/locales/hu.yml +0 -22
- data/config/locales/id-ID.yml +0 -22
- data/config/locales/it.yml +3 -40
- data/config/locales/ja.yml +80 -80
- data/config/locales/lb.yml +3 -36
- data/config/locales/lt.yml +3 -36
- data/config/locales/lv.yml +0 -22
- data/config/locales/nl.yml +3 -36
- data/config/locales/no.yml +3 -36
- data/config/locales/pl.yml +3 -36
- data/config/locales/pt-BR.yml +3 -36
- data/config/locales/pt.yml +3 -36
- data/config/locales/ro-RO.yml +22 -51
- data/config/locales/ru.yml +0 -9
- data/config/locales/sk.yml +0 -22
- data/config/locales/sl.yml +0 -5
- data/config/locales/sv.yml +82 -54
- data/config/locales/tr-TR.yml +3 -36
- data/config/locales/zh-CN.yml +3 -36
- data/config/locales/zh-TW.yml +3 -36
- data/db/migrate/20190315203056_add_session_token_to_decidim_forms_answers.rb +1 -1
- data/db/migrate/20250314150250_rename_answer_to_response_in_decidim_forms.rb +30 -0
- data/db/migrate/20250319130003_change_question_types_in_questions.rb +14 -0
- data/lib/decidim/api/question_matrix_row_type.rb +13 -0
- data/lib/decidim/api/question_type.rb +4 -3
- data/lib/decidim/api/questionnaire_type.rb +1 -0
- data/lib/decidim/api/response_option_type.rb +13 -0
- data/lib/decidim/exporters/form_pdf.rb +33 -33
- data/lib/decidim/forms/api.rb +2 -1
- data/lib/decidim/forms/{download_your_data_user_answers_serializer.rb → download_your_data_user_responses_serializer.rb} +3 -3
- data/lib/decidim/forms/engine.rb +2 -2
- data/lib/decidim/forms/test/factories.rb +24 -24
- data/lib/decidim/forms/test/shared_examples/has_questionnaire.rb +176 -176
- data/lib/decidim/forms/test/shared_examples/manage_questionnaire_responses.rb +159 -0
- data/lib/decidim/forms/test/shared_examples/manage_questionnaires/add_display_conditions.rb +19 -19
- data/lib/decidim/forms/test/shared_examples/manage_questionnaires/add_questions.rb +44 -44
- data/lib/decidim/forms/test/shared_examples/manage_questionnaires/update_display_conditions.rb +10 -10
- data/lib/decidim/forms/test/shared_examples/manage_questionnaires/update_questions.rb +18 -18
- data/lib/decidim/forms/test/shared_examples/manage_questionnaires.rb +4 -4
- data/lib/decidim/forms/test.rb +1 -1
- data/lib/decidim/forms/user_responses_serializer.rb +110 -0
- data/lib/decidim/forms/version.rb +1 -1
- data/lib/decidim/forms.rb +2 -2
- metadata +48 -45
- data/app/cells/decidim/forms/answer_readonly_cell.rb +0 -9
- data/app/helpers/decidim/forms/admin/questionnaire_answers_helper.rb +0 -30
- data/app/models/decidim/forms/answer_choice.rb +0 -22
- data/app/queries/decidim/forms/questionnaire_user_answers.rb +0 -32
- data/app/views/decidim/forms/admin/questionnaires/_answer_option_template.html.erb +0 -7
- data/app/views/decidim/forms/admin/questionnaires/answers/index.html.erb +0 -49
- data/app/views/decidim/forms/admin/questionnaires/answers/show.html.erb +0 -43
- data/app/views/decidim/forms/questionnaires/_answer.html.erb +0 -61
- data/app/views/decidim/forms/questionnaires/answers/_files.html.erb +0 -1
- data/app/views/decidim/forms/questionnaires/answers/_long_answer.html.erb +0 -3
- data/app/views/decidim/forms/questionnaires/answers/_multiple_option.html.erb +0 -25
- data/app/views/decidim/forms/questionnaires/answers/_separator.html.erb +0 -1
- data/app/views/decidim/forms/questionnaires/answers/_short_answer.html.erb +0 -3
- data/app/views/decidim/forms/questionnaires/answers/_single_option.html.erb +0 -32
- data/app/views/decidim/forms/questionnaires/answers/_sorting.html.erb +0 -26
- data/app/views/decidim/forms/questionnaires/answers/_title_and_description.html.erb +0 -1
- data/lib/decidim/api/answer_option_type.rb +0 -13
- data/lib/decidim/forms/test/shared_examples/manage_questionnaire_answers.rb +0 -149
- data/lib/decidim/forms/user_answers_serializer.rb +0 -105
- /data/app/cells/decidim/forms/{answer_readonly → response_readonly}/show.erb +0 -0
@@ -1,49 +0,0 @@
|
|
1
|
-
<div class="card" id="answers">
|
2
|
-
<div class="item_show__header">
|
3
|
-
<h1 class="item_show__header-title">
|
4
|
-
<%= t ".title", total: @total %>
|
5
|
-
<%= link_to t("actions.back", scope: "decidim.forms.admin.questionnaires"), questionnaire_url, class: "button button__sm button__secondary new" %>
|
6
|
-
</h1>
|
7
|
-
</div>
|
8
|
-
<div class="table-scroll">
|
9
|
-
<table class="table-list">
|
10
|
-
<thead>
|
11
|
-
<tr>
|
12
|
-
<th>#</th>
|
13
|
-
<th><%= first_table_th(@participants.first) %></th>
|
14
|
-
<th><%= t("user_status", scope: "decidim.forms.user_answers_serializer") %></th>
|
15
|
-
<th><%= t("ip_hash", scope: "decidim.forms.user_answers_serializer") %></th>
|
16
|
-
<th><%= t("completion", scope: "decidim.forms.user_answers_serializer") %></th>
|
17
|
-
<th><%= t("created_at", scope: "decidim.forms.user_answers_serializer") %></th>
|
18
|
-
<th></th>
|
19
|
-
</tr>
|
20
|
-
</thead>
|
21
|
-
<tbody>
|
22
|
-
<% @participants.each_with_index do |participant, idx| %>
|
23
|
-
<tr>
|
24
|
-
<td><%= idx + 1 + page_offset %></td>
|
25
|
-
<td>
|
26
|
-
<% if allowed_to? :show, :questionnaire_answers %>
|
27
|
-
<%= link_to first_table_td(participant), questionnaire_participant_answers_url(participant.session_token) %>
|
28
|
-
<% else %>
|
29
|
-
<%= first_table_td(participant) %></td>
|
30
|
-
<% end %>
|
31
|
-
<td><%= participant.status %></td>
|
32
|
-
<td><%= participant.ip_hash %></td>
|
33
|
-
<td><%= display_percentage(participant.completion) %></td>
|
34
|
-
<td><%= l participant.answered_at, format: :short %></td>
|
35
|
-
<td class="table-list__actions">
|
36
|
-
<% if allowed_to? :show, :questionnaire_answers %>
|
37
|
-
<%= icon_link_to "eye-line", questionnaire_participant_answers_url(participant.session_token), t("actions.show", scope: "decidim.forms.admin.questionnaires.answers"), class: "action-icon--eye", target: "_blank", data: { "external-link": false } %>
|
38
|
-
<% end %>
|
39
|
-
<% if allowed_to? :export_response, :questionnaire_answers %>
|
40
|
-
<%= icon_link_to "download-line", questionnaire_export_response_url(participant.session_token), t("actions.export", scope: "decidim.forms.admin.questionnaires.answers"), class: "action-icon--data-transfer-download" %>
|
41
|
-
<% end %>
|
42
|
-
</td>
|
43
|
-
</tr>
|
44
|
-
<% end %>
|
45
|
-
</tbody>
|
46
|
-
</table>
|
47
|
-
</div>
|
48
|
-
</div>
|
49
|
-
<%= decidim_paginate @query %>
|
@@ -1,43 +0,0 @@
|
|
1
|
-
<div class="card" id="answers">
|
2
|
-
<div class="item_show__header">
|
3
|
-
<h1 class="item_show__header-title">
|
4
|
-
<%= t ".title", number: current_idx %>
|
5
|
-
|
6
|
-
<%= link_to t("actions.next", scope: "decidim.forms.admin.questionnaires.answers").html_safe, next_url, rel: "next", class: "button button__sm button__secondary next" unless last? %>
|
7
|
-
<%= link_to t("actions.previous", scope: "decidim.forms.admin.questionnaires.answers").html_safe, prev_url, rel: "prev", class: "button button__sm button__secondary prev" unless first? %>
|
8
|
-
<%= link_to t("actions.export", scope: "decidim.forms.admin.questionnaires.answers"), questionnaire_export_response_url(@participant.session_token), class: "button button__sm button__secondary export" %>
|
9
|
-
<%= link_to t("actions.back", scope: "decidim.forms.admin.questionnaires.answers"), questionnaire_participants_url, class: "button button__sm button__secondary back" %>
|
10
|
-
</h1>
|
11
|
-
</div>
|
12
|
-
<div class="card-section">
|
13
|
-
<div class="table">
|
14
|
-
<table class="table-list">
|
15
|
-
<thead>
|
16
|
-
<tr>
|
17
|
-
<th><%= t("session_token", scope: "decidim.forms.user_answers_serializer") %></th>
|
18
|
-
<th><%= t("user_status", scope: "decidim.forms.user_answers_serializer") %></th>
|
19
|
-
<th><%= t("ip_hash", scope: "decidim.forms.user_answers_serializer") %></th>
|
20
|
-
<th><%= t("completion", scope: "decidim.forms.user_answers_serializer") %></th>
|
21
|
-
<th><%= t("created_at", scope: "decidim.forms.user_answers_serializer") %></th>
|
22
|
-
<th></th>
|
23
|
-
</tr>
|
24
|
-
</thead>
|
25
|
-
<tbody>
|
26
|
-
<tr>
|
27
|
-
<td><%= @participant.session_token %></td>
|
28
|
-
<td><%= @participant.status %></td>
|
29
|
-
<td><%= @participant.ip_hash %></td>
|
30
|
-
<td><%= display_percentage(@participant.completion) %></td>
|
31
|
-
<td><%= l @participant.answered_at, format: :short %></td>
|
32
|
-
</tr>
|
33
|
-
</tbody>
|
34
|
-
</table>
|
35
|
-
</div>
|
36
|
-
<dl class="p-4 [&_ul]:list-disc [&_ul]:pl-4">
|
37
|
-
<% @participant.answers.each do |answer| %>
|
38
|
-
<dt class="font-semibold"><%= answer.question %></dt>
|
39
|
-
<dd><%= answer.body %></dd>
|
40
|
-
<% end %>
|
41
|
-
</dl>
|
42
|
-
</div>
|
43
|
-
</div>
|
@@ -1,61 +0,0 @@
|
|
1
|
-
<% field_id = "questionnaire_responses_#{answer_idx}" %>
|
2
|
-
|
3
|
-
<% if answer.question.separator? %>
|
4
|
-
|
5
|
-
<%= render partial: "decidim/forms/questionnaires/answers/#{answer.question.question_type}", locals: { answer:, answer_form:, answer_idx:, field_id:, disabled: } %>
|
6
|
-
<%= answer_form.hidden_field :question_id %>
|
7
|
-
|
8
|
-
<% elsif answer.question.title_and_description? %>
|
9
|
-
|
10
|
-
<div class="answer-questionnaire__step-heading">
|
11
|
-
<h3 class="h3"><%= translated_attribute(answer.question.body) %></h3>
|
12
|
-
<% if translated_attribute(answer.question.description).present? %>
|
13
|
-
<div>
|
14
|
-
<%= decidim_sanitize_editor_admin translated_attribute(answer.question.description) %>
|
15
|
-
</div>
|
16
|
-
<% end %>
|
17
|
-
</div>
|
18
|
-
|
19
|
-
<%= render partial: "decidim/forms/questionnaires/answers/#{answer.question.question_type}", locals: { answer:, answer_form:, answer_idx:, field_id:, disabled: } %>
|
20
|
-
<%= answer_form.hidden_field :question_id %>
|
21
|
-
|
22
|
-
<% else %>
|
23
|
-
|
24
|
-
<div class="answer-questionnaire__question">
|
25
|
-
<%
|
26
|
-
label_options = {
|
27
|
-
class: "answer-questionnaire__question-label questionnaire-question",
|
28
|
-
data: { "answer-idx": cleaned_answer_idx }
|
29
|
-
}
|
30
|
-
label_options[:for] = nil if %w(matrix_single matrix_multiple single_option multiple_option).include?(answer.question.question_type)
|
31
|
-
%>
|
32
|
-
|
33
|
-
<%= label_tag field_id, **label_options do %>
|
34
|
-
<%= translated_attribute(answer.question.body) %>
|
35
|
-
<% if answer.question.mandatory? %>
|
36
|
-
<%= answer_form.send(:required_indicator) %>
|
37
|
-
<% end %>
|
38
|
-
<% if answer.question.max_choices %>
|
39
|
-
<%= " (#{answer.send(:max_choices_label)})" %>
|
40
|
-
<% end %>
|
41
|
-
<% end %>
|
42
|
-
|
43
|
-
<% if translated_attribute(answer.question.description).present? %>
|
44
|
-
<div class="answer-questionnaire__question-description">
|
45
|
-
<%= decidim_sanitize_editor_admin translated_attribute(answer.question.description) %>
|
46
|
-
</div>
|
47
|
-
<% end %>
|
48
|
-
|
49
|
-
<% if answer.question.max_choices %>
|
50
|
-
<small class="form-error max-choices-alert"><%= t(".max_choices_alert") %></small>
|
51
|
-
<% end %>
|
52
|
-
|
53
|
-
<% %w(short_answer long_answer).exclude?(answer&.question&.question_type) && answer.errors.full_messages.each do |msg| %>
|
54
|
-
<small class="form-error is-visible"><%= msg %></small>
|
55
|
-
<% end %>
|
56
|
-
|
57
|
-
<%= render partial: "decidim/forms/questionnaires/answers/#{answer.question.question_type}", locals: { answer:, answer_form:, answer_idx:, field_id:, disabled:, maxlength: (answer.question.max_characters if answer.question.max_characters.positive?) } %>
|
58
|
-
<%= answer_form.hidden_field :question_id %>
|
59
|
-
</div>
|
60
|
-
|
61
|
-
<% end %>
|
@@ -1 +0,0 @@
|
|
1
|
-
<%= answer_form.upload :add_documents, attachments: answer.documents, label: false, multiple: true, id: field_id, disabled:, button_class: "button button__lg button__transparent-secondary w-full" %>
|
@@ -1,25 +0,0 @@
|
|
1
|
-
<div class="answer-questionnaire__single-option js-check-box-collection">
|
2
|
-
<% answer.question.answer_options.each_with_index do |answer_option, idx| %>
|
3
|
-
<% choice = answer.selected_choices.find { |choice| choice.answer_option_id == answer_option.id } %>
|
4
|
-
|
5
|
-
<div class="js-collection-input">
|
6
|
-
<%= label_tag do %>
|
7
|
-
<%= check_box_tag "questionnaire[responses][#{answer_idx}][choices][#{idx}][body]",
|
8
|
-
translated_attribute(answer_option.body),
|
9
|
-
choice.present?, disabled: %>
|
10
|
-
|
11
|
-
<%= translated_attribute(answer_option.body) %>
|
12
|
-
|
13
|
-
<%= hidden_field_tag "questionnaire[responses][#{answer_idx}][choices][#{idx}][answer_option_id]", answer_option.id %>
|
14
|
-
<% end %>
|
15
|
-
|
16
|
-
<% if answer_option.free_text %>
|
17
|
-
<%= text_field_tag "questionnaire[responses][#{answer_idx}][choices][#{idx}][custom_body]",
|
18
|
-
choice.try(:custom_body),
|
19
|
-
disabled: true,
|
20
|
-
maxlength:,
|
21
|
-
class: "w-full" %>
|
22
|
-
<% end %>
|
23
|
-
</div>
|
24
|
-
<% end %>
|
25
|
-
</div>
|
@@ -1 +0,0 @@
|
|
1
|
-
<%= answer_form.hidden_field :body, value: "separator", id: field_id, disabled: %>
|
@@ -1,32 +0,0 @@
|
|
1
|
-
<div class="answer-questionnaire__single-option js-radio-button-collection">
|
2
|
-
<% choice = answer.choices.first %>
|
3
|
-
|
4
|
-
<% answer.question.answer_options.each_with_index do |answer_option, idx| %>
|
5
|
-
<% choice_id = "#{field_id}_choices_#{idx}" %>
|
6
|
-
|
7
|
-
<div class="js-collection-input">
|
8
|
-
<%= label_tag "#{choice_id}_body" do %>
|
9
|
-
<%= radio_button_tag "questionnaire[responses][#{answer_idx}][choices][][body]",
|
10
|
-
translated_attribute(answer_option.body),
|
11
|
-
answer_option.id == choice.try(:answer_option_id),
|
12
|
-
id: "#{choice_id}_body", disabled: %>
|
13
|
-
|
14
|
-
<%= translated_attribute(answer_option.body) %>
|
15
|
-
|
16
|
-
<%= hidden_field_tag "questionnaire[responses][#{answer_idx}][choices][][answer_option_id]",
|
17
|
-
answer_option.id,
|
18
|
-
id: "#{choice_id}_answer_option",
|
19
|
-
disabled: true %>
|
20
|
-
<% end %>
|
21
|
-
|
22
|
-
<% if answer_option.free_text %>
|
23
|
-
<%= text_field_tag "questionnaire[responses][#{answer_idx}][choices][][custom_body]",
|
24
|
-
choice.try(:custom_body),
|
25
|
-
id: "#{choice_id}_custom_body",
|
26
|
-
disabled: true,
|
27
|
-
maxlength:,
|
28
|
-
class: "w-full" %>
|
29
|
-
<% end %>
|
30
|
-
</div>
|
31
|
-
<% end %>
|
32
|
-
</div>
|
@@ -1,26 +0,0 @@
|
|
1
|
-
<div class="answer-questionnaire__sorting-container js-sortable-check-box-collection">
|
2
|
-
<%
|
3
|
-
# This is a block that aims to identify the order to the answer questions, and if there is an user answer, use that
|
4
|
-
# to order the date that needs to be displayed. It is done like this to help with edit user answer form
|
5
|
-
%>
|
6
|
-
<% ordering = answer.question.answer_options.pluck(:id) %>
|
7
|
-
<% if answer.selected_choices.present? %>
|
8
|
-
<% ordering = answer.selected_choices.map(&:answer_option_id) %>
|
9
|
-
<% end %>
|
10
|
-
|
11
|
-
<% ids_for_order = ordering.map.with_index { |id, index| "#{id}=#{index + 1}" }.join(", ") %>
|
12
|
-
|
13
|
-
<% answer.question.reload.answer_options.reorder(Arel.sql("POSITION(id::text in '#{ids_for_order}')")).each_with_index do |answer_option, idx| %>
|
14
|
-
<% choice = answer.selected_choices.find { |choice| choice.answer_option_id == answer_option.id } %>
|
15
|
-
|
16
|
-
<div class="answer-questionnaire__sorting js-collection-input" role="button">
|
17
|
-
<%= translated_attribute(answer_option.body) %>
|
18
|
-
|
19
|
-
<%= icon "drag-move-2-line" %>
|
20
|
-
|
21
|
-
<%= hidden_field_tag "questionnaire[responses][#{answer_idx}][choices][#{idx}][position]", choice.try(:position) %>
|
22
|
-
<%= hidden_field_tag "questionnaire[responses][#{answer_idx}][choices][#{idx}][body]", translated_attribute(answer_option.body) %>
|
23
|
-
<%= hidden_field_tag "questionnaire[responses][#{answer_idx}][choices][#{idx}][answer_option_id]", answer_option.id %>
|
24
|
-
</div>
|
25
|
-
<% end %>
|
26
|
-
</div>
|
@@ -1 +0,0 @@
|
|
1
|
-
<%= answer_form.hidden_field :body, value: "title-and-description", id: field_id, disabled: %>
|
@@ -1,13 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Decidim
|
4
|
-
module Forms
|
5
|
-
class AnswerOptionType < Decidim::Api::Types::BaseObject
|
6
|
-
description "An answer option for a multi-choice question in a questionnaire"
|
7
|
-
|
8
|
-
field :body, Decidim::Core::TranslatedFieldType, "The text answer response option.", null: false
|
9
|
-
field :free_text, GraphQL::Types::Boolean, "Whether if this answer accepts any free text from the user.", null: false
|
10
|
-
field :id, GraphQL::Types::ID, "ID of this answer option", null: false
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
@@ -1,149 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "spec_helper"
|
4
|
-
|
5
|
-
shared_examples_for "manage questionnaire answers" do
|
6
|
-
let(:first_type) { "short_answer" }
|
7
|
-
let!(:first) do
|
8
|
-
create(:questionnaire_question, questionnaire:, position: 1, question_type: first_type)
|
9
|
-
end
|
10
|
-
let!(:second) do
|
11
|
-
create(:questionnaire_question, questionnaire:, position: 2, question_type: "single_option")
|
12
|
-
end
|
13
|
-
let!(:third) do
|
14
|
-
create(:questionnaire_question, questionnaire:, position: 3, question_type: "files")
|
15
|
-
end
|
16
|
-
let(:questions) do
|
17
|
-
[first, second, third]
|
18
|
-
end
|
19
|
-
|
20
|
-
context "when there are no answers" do
|
21
|
-
it "do not answer admin link" do
|
22
|
-
click_on "Manage questions"
|
23
|
-
expect(page).to have_content("No answers yet")
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
context "when there are answers" do
|
28
|
-
let!(:answer1) { create(:answer, questionnaire:, question: first) }
|
29
|
-
let!(:answer2) { create(:answer, body: "second answer", questionnaire:, question: first) }
|
30
|
-
let!(:answer3) { create(:answer, questionnaire:, question: second) }
|
31
|
-
let!(:file_answer) { create(:answer, :with_attachments, questionnaire:, question: third, body: nil, user: answer3.user, session_token: answer3.session_token) }
|
32
|
-
|
33
|
-
it "shows the answer admin link" do
|
34
|
-
click_on "Manage questions"
|
35
|
-
expect(page).to have_content("Show responses")
|
36
|
-
end
|
37
|
-
|
38
|
-
context "and managing answers page" do
|
39
|
-
before do
|
40
|
-
click_on "Manage questions"
|
41
|
-
click_on "Show responses"
|
42
|
-
end
|
43
|
-
|
44
|
-
it "shows the answers page" do
|
45
|
-
expect(page).to have_content(answer1.body)
|
46
|
-
expect(page).to have_content(answer1.question.body["en"])
|
47
|
-
expect(page).to have_content(answer2.body)
|
48
|
-
expect(page).to have_content(answer2.question.body["en"])
|
49
|
-
end
|
50
|
-
|
51
|
-
it "shows the percentage" do
|
52
|
-
expect(page).to have_content("33%")
|
53
|
-
end
|
54
|
-
|
55
|
-
it "has a detail link" do
|
56
|
-
expect(page).to have_link("Show answers")
|
57
|
-
end
|
58
|
-
|
59
|
-
it "has an export link" do
|
60
|
-
expect(page).to have_link(answer1.body)
|
61
|
-
expect(page).to have_link(answer2.body)
|
62
|
-
expect(page).to have_link("Export")
|
63
|
-
end
|
64
|
-
|
65
|
-
context "when no short answer exist" do
|
66
|
-
let(:first_type) { "long_answer" }
|
67
|
-
|
68
|
-
it "shows session token" do
|
69
|
-
expect(page).to have_no_content(answer1.body)
|
70
|
-
expect(page).to have_content(answer1.session_token)
|
71
|
-
expect(page).to have_content(answer2.session_token)
|
72
|
-
expect(page).to have_content(answer3.session_token)
|
73
|
-
expect(page).to have_content("User identifier")
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
context "when multiple answer choice" do
|
78
|
-
let(:first_type) { "multiple_option" }
|
79
|
-
let!(:answer1) { create(:answer, questionnaire:, question: first, body: nil) }
|
80
|
-
let!(:answer_option) { create(:answer_option, question: first) }
|
81
|
-
let!(:answer_choice) { create(:answer_choice, answer: answer1, answer_option:, body: translated(answer_option.body, locale: I18n.locale)) }
|
82
|
-
|
83
|
-
it "shows the answers page with custom body" do
|
84
|
-
new_window = window_opened_by { find_all("a.action-icon.action-icon--eye").first.click }
|
85
|
-
|
86
|
-
page.within_window(new_window) do
|
87
|
-
within "#answers" do
|
88
|
-
expect(page).to have_css("dt", text: translated(first.body))
|
89
|
-
expect(page).to have_css("li", text: translated(answer_option.body))
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
context "and managing individual answer page" do
|
97
|
-
let!(:answer11) { create(:answer, questionnaire:, body: "", user: answer1.user, question: second) }
|
98
|
-
|
99
|
-
before do
|
100
|
-
click_on "Manage questions"
|
101
|
-
click_on "Show responses"
|
102
|
-
end
|
103
|
-
|
104
|
-
it "shows all the questions and responses" do
|
105
|
-
click_on answer1.body, match: :first
|
106
|
-
expect(page).to have_content(first.body["en"])
|
107
|
-
expect(page).to have_content(second.body["en"])
|
108
|
-
expect(page).to have_content(answer1.body)
|
109
|
-
end
|
110
|
-
|
111
|
-
it "first answer has a next link" do
|
112
|
-
click_on answer1.body, match: :first
|
113
|
-
expect(page).to have_link("Next ›")
|
114
|
-
expect(page).to have_no_link("‹ Prev")
|
115
|
-
end
|
116
|
-
|
117
|
-
it "second answer has prev/next links" do
|
118
|
-
click_on answer2.body, match: :first
|
119
|
-
expect(page).to have_link("Next ›")
|
120
|
-
expect(page).to have_link("‹ Prev")
|
121
|
-
end
|
122
|
-
|
123
|
-
it "third answer has prev link" do
|
124
|
-
click_on answer3.session_token, match: :first
|
125
|
-
expect(page).to have_no_link("Next ›")
|
126
|
-
expect(page).to have_link("‹ Prev")
|
127
|
-
end
|
128
|
-
|
129
|
-
it "third answer has download link for the attachments" do
|
130
|
-
click_on answer3.session_token, match: :first
|
131
|
-
expect(page).to have_content(translated(file_answer.attachments.first.title))
|
132
|
-
expect(page).to have_content(translated(file_answer.attachments.second.title))
|
133
|
-
end
|
134
|
-
|
135
|
-
context "when the file answer does not have a title for the attachment" do
|
136
|
-
let!(:file_answer) { create(:answer, questionnaire:, question: third, body: nil, user: answer3.user, session_token: answer3.session_token) }
|
137
|
-
|
138
|
-
before do
|
139
|
-
create(:attachment, :with_image, attached_to: file_answer, title: {}, description: {})
|
140
|
-
end
|
141
|
-
|
142
|
-
it "third answer has download link for the attachments" do
|
143
|
-
click_on answer3.session_token, match: :first
|
144
|
-
expect(page).to have_content("Download attachment")
|
145
|
-
end
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
@@ -1,105 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Decidim
|
4
|
-
module Forms
|
5
|
-
# This class serializes the answers given by a User for questionnaire so can be
|
6
|
-
# exported to CSV, JSON or other formats.
|
7
|
-
class UserAnswersSerializer < Decidim::Exporters::Serializer
|
8
|
-
include Decidim::TranslationsHelper
|
9
|
-
|
10
|
-
# Public: Initializes the serializer with a collection of Answers.
|
11
|
-
def initialize(answers)
|
12
|
-
@answers = answers
|
13
|
-
end
|
14
|
-
|
15
|
-
# Public: Exports a hash with the serialized data for the user answers.
|
16
|
-
def serialize
|
17
|
-
answers_hash = hash_for(@answers.first)
|
18
|
-
answers_hash.merge!(questions_hash)
|
19
|
-
|
20
|
-
@answers.each do |answer|
|
21
|
-
answers_hash[translated_question_key(answer.question.position, answer.question.body)] = normalize_body(answer)
|
22
|
-
end
|
23
|
-
|
24
|
-
answers_hash
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
attr_reader :answers
|
30
|
-
alias resource answers
|
31
|
-
|
32
|
-
def hash_for(answer)
|
33
|
-
{
|
34
|
-
answer_translated_attribute_name(:id) => answer&.session_token,
|
35
|
-
answer_translated_attribute_name(:created_at) => answer&.created_at,
|
36
|
-
answer_translated_attribute_name(:ip_hash) => answer&.ip_hash,
|
37
|
-
answer_translated_attribute_name(:user_status) => answer_translated_attribute_name(answer&.decidim_user_id.present? ? "registered" : "unregistered")
|
38
|
-
}
|
39
|
-
end
|
40
|
-
|
41
|
-
def questions_hash
|
42
|
-
questionnaire_id = @answers.first&.decidim_questionnaire_id
|
43
|
-
return {} unless questionnaire_id
|
44
|
-
|
45
|
-
questions = Decidim::Forms::Question.where(decidim_questionnaire_id: questionnaire_id).order(:position)
|
46
|
-
return {} if questions.none?
|
47
|
-
|
48
|
-
questions.each.inject({}) do |serialized, question|
|
49
|
-
serialized.update(
|
50
|
-
translated_question_key(question.position, question.body) => ""
|
51
|
-
)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def translated_question_key(idx, body)
|
56
|
-
"#{idx + 1}. #{translated_attribute(body)}"
|
57
|
-
end
|
58
|
-
|
59
|
-
def normalize_body(answer)
|
60
|
-
answer.body ||
|
61
|
-
normalize_attachments(answer) ||
|
62
|
-
normalize_choices(answer, answer.choices)
|
63
|
-
end
|
64
|
-
|
65
|
-
def normalize_attachments(answer)
|
66
|
-
return if answer.attachments.blank?
|
67
|
-
|
68
|
-
answer.attachments.map(&:url)
|
69
|
-
end
|
70
|
-
|
71
|
-
def normalize_choices(answer, choices)
|
72
|
-
if answer.question.matrix?
|
73
|
-
normalize_matrix_choices(answer, choices)
|
74
|
-
else
|
75
|
-
choices.map do |choice|
|
76
|
-
format_free_text_for choice
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def normalize_matrix_choices(answer, choices)
|
82
|
-
answer.question.matrix_rows.to_h do |matrix_row|
|
83
|
-
row_body = translated_attribute(matrix_row.body)
|
84
|
-
|
85
|
-
row_choices = answer.question.answer_options.map do |answer_option|
|
86
|
-
choice = choices.find_by(matrix_row:, answer_option:)
|
87
|
-
choice.try(:custom_body) || choice.try(:body)
|
88
|
-
end
|
89
|
-
|
90
|
-
[row_body, row_choices]
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def answer_translated_attribute_name(attribute)
|
95
|
-
I18n.t(attribute.to_sym, scope: "decidim.forms.user_answers_serializer")
|
96
|
-
end
|
97
|
-
|
98
|
-
def format_free_text_for(choice)
|
99
|
-
return choice.try(:body) if choice.try(:custom_body).blank?
|
100
|
-
|
101
|
-
"#{choice.try(:body)} (#{choice.try(:custom_body)})"
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
File without changes
|