decidim-forms 0.30.3 → 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 +8 -29
- 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 +21 -11
- data/app/views/decidim/forms/admin/questionnaires/_questions_form.html.erb +13 -41
- 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 +11 -1
- data/app/views/decidim/forms/admin/questionnaires/_title_and_description.html.erb +12 -2
- 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 +2 -22
- data/config/locales/bg.yml +10 -36
- data/config/locales/ca-IT.yml +80 -74
- data/config/locales/ca.yml +80 -74
- data/config/locales/cs.yml +79 -76
- data/config/locales/de.yml +79 -72
- data/config/locales/el.yml +10 -36
- data/config/locales/en.yml +90 -84
- data/config/locales/es-MX.yml +82 -76
- data/config/locales/es-PY.yml +82 -76
- data/config/locales/es.yml +80 -74
- data/config/locales/eu.yml +78 -72
- data/config/locales/fi-plain.yml +78 -72
- data/config/locales/fi.yml +76 -70
- data/config/locales/fr-CA.yml +77 -68
- data/config/locales/fr.yml +77 -68
- data/config/locales/ga-IE.yml +4 -7
- data/config/locales/gl.yml +4 -22
- data/config/locales/hu.yml +4 -22
- data/config/locales/id-ID.yml +2 -22
- data/config/locales/it.yml +8 -40
- data/config/locales/ja.yml +87 -81
- data/config/locales/lb.yml +8 -36
- data/config/locales/lt.yml +10 -36
- data/config/locales/lv.yml +4 -22
- data/config/locales/nl.yml +10 -36
- data/config/locales/no.yml +10 -36
- data/config/locales/pl.yml +10 -36
- data/config/locales/pt-BR.yml +10 -36
- data/config/locales/pt.yml +8 -36
- data/config/locales/ro-RO.yml +29 -51
- data/config/locales/ru.yml +2 -9
- data/config/locales/sk.yml +2 -22
- data/config/locales/sl.yml +0 -5
- data/config/locales/sv.yml +89 -55
- data/config/locales/tr-TR.yml +8 -36
- data/config/locales/val-ES.yml +2 -0
- data/config/locales/zh-CN.yml +8 -36
- data/config/locales/zh-TW.yml +10 -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 +43 -140
- 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
|
@@ -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(".
|
|
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.
|
|
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
|
-
.
|
|
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 relative before:content-[attr(data-
|
|
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
|
|
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.
|
|
33
|
+
I18n.t(registered? ? "registered" : "unregistered", scope: "decidim.forms.user_responses_serializer")
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
def
|
|
37
|
-
siblings.map { |
|
|
36
|
+
def responses
|
|
37
|
+
siblings.map { |response| QuestionnaireResponsePresenter.new(response:) }
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
def
|
|
41
|
-
short = siblings.where(decidim_forms_questions: { question_type: %w(
|
|
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(
|
|
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(
|
|
49
|
-
.where("
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
|
@@ -4,15 +4,15 @@ module Decidim
|
|
|
4
4
|
module Forms
|
|
5
5
|
module Admin
|
|
6
6
|
#
|
|
7
|
-
# Presenter for questionnaire
|
|
7
|
+
# Presenter for questionnaire response
|
|
8
8
|
#
|
|
9
|
-
class
|
|
9
|
+
class QuestionnaireResponsePresenter < SimpleDelegator
|
|
10
10
|
delegate :content_tag, :safe_join, :link_to, :number_to_human_size, to: :view_context
|
|
11
11
|
|
|
12
12
|
include Decidim::TranslatableAttributes
|
|
13
13
|
|
|
14
|
-
def
|
|
15
|
-
__getobj__.fetch(:
|
|
14
|
+
def response
|
|
15
|
+
__getobj__.fetch(:response)
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def view_context
|
|
@@ -20,22 +20,22 @@ module Decidim
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def question
|
|
23
|
-
translated_attribute(
|
|
23
|
+
translated_attribute(response.question.body)
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def body
|
|
27
|
-
return
|
|
28
|
-
return attachments if
|
|
29
|
-
return "-" if
|
|
27
|
+
return response.body if response.body.present?
|
|
28
|
+
return attachments if response.attachments.any?
|
|
29
|
+
return "-" if response.choices.empty?
|
|
30
30
|
|
|
31
|
-
choices =
|
|
31
|
+
choices = response.choices.map do |choice|
|
|
32
32
|
{
|
|
33
|
-
|
|
33
|
+
response_option_body: choice.try(:response_option).try(:translated_body),
|
|
34
34
|
choice_body: body_or_custom_body(choice)
|
|
35
35
|
}
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
return choice(choices.first) if
|
|
38
|
+
return choice(choices.first) if response.question.question_type == "single_option"
|
|
39
39
|
|
|
40
40
|
content_tag(:ul) do
|
|
41
41
|
safe_join(choices.map { |c| choice(c) })
|
|
@@ -44,7 +44,7 @@ module Decidim
|
|
|
44
44
|
|
|
45
45
|
def attachments
|
|
46
46
|
content_tag(:ul) do
|
|
47
|
-
safe_join(
|
|
47
|
+
safe_join(response.attachments.map { |a| pretty_attachment(a) })
|
|
48
48
|
end
|
|
49
49
|
end
|
|
50
50
|
|
|
@@ -57,7 +57,7 @@ module Decidim
|
|
|
57
57
|
link_to(attachment.url, target: "_blank", rel: "noopener noreferrer") do
|
|
58
58
|
content_tag(:span) do
|
|
59
59
|
translated_attribute(attachment.title).presence ||
|
|
60
|
-
I18n.t("download_attachment", scope: "decidim.forms.
|
|
60
|
+
I18n.t("download_attachment", scope: "decidim.forms.questionnaire_response_presenter")
|
|
61
61
|
end + " " + content_tag(:small) do
|
|
62
62
|
"#{attachment.file_type} #{number_to_human_size(attachment.file_size)}"
|
|
63
63
|
end
|
|
@@ -73,9 +73,9 @@ module Decidim
|
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
def render_body_for(choice_hash)
|
|
76
|
-
return choice_hash[:
|
|
76
|
+
return choice_hash[:response_option_body] if choice_hash[:choice_body].blank?
|
|
77
77
|
|
|
78
|
-
"#{choice_hash[:
|
|
78
|
+
"#{choice_hash[:response_option_body]} (#{choice_hash[:choice_body]})"
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
def body_or_custom_body(choice)
|
|
@@ -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.
|
|
19
|
-
# i18n-tasks-use t("decidim.forms.admin_log.question.
|
|
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 "
|
|
22
|
+
when "publish_responses", "unpublish_responses"
|
|
23
23
|
"decidim.forms.admin_log.question.#{action}"
|
|
24
24
|
else
|
|
25
25
|
super
|
data/app/presenters/decidim/forms/{answer_option_presenter.rb → response_option_presenter.rb}
RENAMED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
module Decidim
|
|
4
4
|
module Forms
|
|
5
5
|
#
|
|
6
|
-
# Decorator for
|
|
6
|
+
# Decorator for response_options
|
|
7
7
|
#
|
|
8
|
-
class
|
|
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
|
|
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
|
-
|
|
26
|
+
responses.select(:session_token, :decidim_user_id, :ip_hash).first
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
# Finds the participant's
|
|
30
|
-
def
|
|
31
|
-
|
|
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
|
|
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
|
|
22
|
-
# Because exporters only have access to the
|
|
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
|
-
|
|
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 (
|
|
35
|
-
|
|
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
|
|
8
|
+
<button class="button button__xs button__transparent-secondary small alert remove-display-condition button--title">
|
|
9
9
|
<%= icon("delete-bin-line") %>
|
|
10
|
-
|
|
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:
|
|
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-
|
|
44
|
+
<div class="row column questionnaire-question-display-condition-response-option">
|
|
45
45
|
<%=
|
|
46
46
|
form.select(
|
|
47
|
-
:
|
|
48
|
-
options_from_collection_for_select(display_condition.
|
|
49
|
-
{ prompt: t(".
|
|
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.
|
|
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:
|
|
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:
|
|
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
|
|
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
|
-
|
|
10
|
+
<span class="hidden md:block"><%= t(".remove") %></span>
|
|
11
11
|
</button>
|
|
12
12
|
<% end %>
|
|
13
13
|
</h2>
|
|
@@ -1,7 +1,7 @@
|
|
|
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-
|
|
4
|
+
<div class="form__wrapper" data-controller="accordion">
|
|
5
5
|
<div class="card-divider">
|
|
6
6
|
<h2 class="card-title flex items-center">
|
|
7
7
|
<span>
|
|
@@ -23,16 +23,26 @@
|
|
|
23
23
|
</button>
|
|
24
24
|
|
|
25
25
|
<% if editable %>
|
|
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
|
+
|
|
26
36
|
<button class="button button__xs button__transparent-secondary small alert remove-question button--title">
|
|
27
37
|
<%= icon("delete-bin-line") %>
|
|
28
|
-
|
|
38
|
+
<span class="hidden md:block"><%= t(".remove") %></span>
|
|
29
39
|
</button>
|
|
30
40
|
<% end %>
|
|
31
41
|
</span>
|
|
32
42
|
</h2>
|
|
33
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
|
|
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-
|
|
117
|
-
<div class="questionnaire-question-
|
|
118
|
-
<% question.
|
|
119
|
-
<%= fields_for "questions[questions][#{question.to_param}][
|
|
120
|
-
<%= render "decidim/forms/admin/questionnaires/
|
|
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
|
|
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
|
|
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>
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
<div class="questionnaire-questions form__wrapper">
|
|
2
|
-
<div class="row column text-
|
|
3
|
-
<button type="button" class="button button__sm
|
|
4
|
-
|
|
2
|
+
<div class="row column text-right my-4">
|
|
3
|
+
<button type="button" class="button button__sm button__text-secondary collapse-all"><%= t(".collapse") %></button>
|
|
4
|
+
|
|
|
5
|
+
<button type="button" class="button button__sm button__text-secondary expand-all"><%= t(".expand") %></button>
|
|
5
6
|
</div>
|
|
6
7
|
|
|
7
8
|
<% if questionnaire.questions_editable? %>
|
|
8
|
-
<% if questionnaire.
|
|
9
|
+
<% if questionnaire.responses.any? && !questionnaire.published? %>
|
|
9
10
|
<%= cell("decidim/announcement", t(".unpublished_warning"), callout_class: "warning" ) %>
|
|
10
11
|
<% end %>
|
|
11
12
|
|
|
@@ -15,7 +16,7 @@
|
|
|
15
16
|
form: question_form,
|
|
16
17
|
id: tabs_id_for_question(blank_question),
|
|
17
18
|
editable: questionnaire.questions_editable?,
|
|
18
|
-
|
|
19
|
+
response_option_template_selector: "#response-option-template-dummy",
|
|
19
20
|
display_condition_template_selector: "#display-condition-template-dummy",
|
|
20
21
|
matrix_row_template_selector: "#matrix-row-template-dummy" %>
|
|
21
22
|
</script>
|
|
@@ -31,15 +32,15 @@
|
|
|
31
32
|
id: tabs_id_for_question(blank_question),
|
|
32
33
|
editable: questionnaire.questions_editable? %>
|
|
33
34
|
</script>
|
|
34
|
-
<%= render "decidim/forms/admin/questionnaires/
|
|
35
|
+
<%= render "decidim/forms/admin/questionnaires/response_option_template", form: question_form, editable: questionnaire.questions_editable?, template_id: "response-option-template-dummy" %>
|
|
35
36
|
<%= render "decidim/forms/admin/questionnaires/display_condition_template", form: question_form, editable: questionnaire.questions_editable?, template_id: "display-condition-template-dummy" %>
|
|
36
37
|
<%= render "decidim/forms/admin/questionnaires/matrix_row_template", form: question_form, editable: questionnaire.questions_editable?, template_id: "matrix-row-template-dummy" %>
|
|
37
38
|
<% end %>
|
|
38
39
|
<% else %>
|
|
39
|
-
<%= cell("decidim/announcement", t(".
|
|
40
|
+
<%= cell("decidim/announcement", t(".already_responded_warning"), callout_class: "warning" ) %>
|
|
40
41
|
<% end %>
|
|
41
42
|
|
|
42
|
-
<div class="questionnaire-questions-list flex flex-col py-6 gap-6 last:pb-0"
|
|
43
|
+
<div class="questionnaire-questions-list flex flex-col py-6 gap-6 last:pb-0">
|
|
43
44
|
<% @form.questions.each_with_index do |question, index| %>
|
|
44
45
|
<%= fields_for "questions[questions][]", question do |question_form| %>
|
|
45
46
|
<% if question.separator? %>
|
|
@@ -58,10 +59,10 @@
|
|
|
58
59
|
id: tabs_id_for_question(question),
|
|
59
60
|
editable: questionnaire.questions_editable?,
|
|
60
61
|
display_condition_template_selector: "#display-condition-template-#{index}",
|
|
61
|
-
|
|
62
|
+
response_option_template_selector: "#response-option-template-#{index}",
|
|
62
63
|
matrix_row_template_selector: "#matrix-row-template-#{index}" %>
|
|
63
64
|
<%= render "decidim/forms/admin/questionnaires/display_condition_template", form: question_form, editable: questionnaire.questions_editable?, template_id: "display-condition-template-#{index}" %>
|
|
64
|
-
<%= render "decidim/forms/admin/questionnaires/
|
|
65
|
+
<%= render "decidim/forms/admin/questionnaires/response_option_template", form: question_form, editable: questionnaire.questions_editable?, template_id: "response-option-template-#{index}" %>
|
|
65
66
|
<%= render "decidim/forms/admin/questionnaires/matrix_row_template", form: question_form, editable: questionnaire.questions_editable?, template_id: "matrix-row-template-#{index}" %>
|
|
66
67
|
<% end %>
|
|
67
68
|
<% end %>
|
|
@@ -72,38 +73,9 @@
|
|
|
72
73
|
<%= append_javascript_pack_tag "decidim_forms_admin" %>
|
|
73
74
|
|
|
74
75
|
<% if questionnaire.questions_editable? %>
|
|
75
|
-
|
|
76
|
-
document.addEventListener("
|
|
76
|
+
<script>
|
|
77
|
+
document.addEventListener("turbo:load", function () {
|
|
77
78
|
window.Decidim.createEditableForm();
|
|
78
|
-
|
|
79
|
-
// Function to initialize the sortable functionality
|
|
80
|
-
function initializeSortable() {
|
|
81
|
-
const container = document.querySelector("#questionnaire-questions-list");
|
|
82
|
-
const questionCards = container?.querySelectorAll(".card.questionnaire-question");
|
|
83
|
-
|
|
84
|
-
if (!container || !questionCards?.length) return;
|
|
85
|
-
|
|
86
|
-
questionCards.forEach(card => {
|
|
87
|
-
card.setAttribute("draggable", "true");
|
|
88
|
-
card.setAttribute("role", "option");
|
|
89
|
-
card.setAttribute("aria-grabbed", "false");
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
window.Decidim?.createSortableList?.("#questionnaire-questions-list");
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Initialize on load and when new options such as questions etc are added
|
|
96
|
-
initializeSortable();
|
|
97
|
-
|
|
98
|
-
const observer = new MutationObserver(() => {
|
|
99
|
-
clearTimeout(observer.timer);
|
|
100
|
-
observer.timer = setTimeout(initializeSortable, 500);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
const container = document.querySelector("#questionnaire-questions-list");
|
|
104
|
-
if (container) {
|
|
105
|
-
observer.observe(container, { childList: true, subtree: true });
|
|
106
|
-
}
|
|
107
79
|
});
|
|
108
80
|
</script>
|
|
109
81
|
<% end %>
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
<%
|
|
1
|
+
<% response_option = form.object %>
|
|
2
2
|
|
|
3
|
-
<div class="card questionnaire-question-
|
|
3
|
+
<div class="card questionnaire-question-response-option m-4">
|
|
4
4
|
<div class="card-divider">
|
|
5
5
|
<h2 class="card-title">
|
|
6
|
-
<span><%= t(".
|
|
6
|
+
<span><%= t(".response_option") %></span>
|
|
7
7
|
<% if editable %>
|
|
8
|
-
<button class="button
|
|
8
|
+
<button class="button button__xs button__transparent-secondary small alert remove-response-option button--title mb-2">
|
|
9
9
|
<%= icon("delete-bin-line") %>
|
|
10
|
-
|
|
10
|
+
<span class="hidden md:block"><%= t(".remove") %></span>
|
|
11
11
|
</button>
|
|
12
12
|
<% end %>
|
|
13
13
|
</h2>
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
form.translated(
|
|
20
20
|
:text_field,
|
|
21
21
|
:body,
|
|
22
|
-
tabs_id:
|
|
22
|
+
tabs_id: tabs_id_for_question_response_option(question, response_option),
|
|
23
23
|
label: t(".statement"),
|
|
24
24
|
disabled: !editable
|
|
25
25
|
)
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
</div>
|
|
38
38
|
</div>
|
|
39
39
|
|
|
40
|
-
<% if
|
|
40
|
+
<% if response_option.persisted? %>
|
|
41
41
|
<%= form.hidden_field :id, disabled: !editable %>
|
|
42
42
|
<% end %>
|
|
43
43
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<% question = form.object %>
|
|
2
|
+
|
|
3
|
+
<script type="text/template" class="decidim-response-option-template decidim-template" id="<%= template_id %>">
|
|
4
|
+
<%= fields_for "questions[questions][#{question.to_param}][response_options][]", blank_response_option do |response_option_form| %>
|
|
5
|
+
<%= render "decidim/forms/admin/questionnaires/response_option", form: response_option_form, question:, editable: %>
|
|
6
|
+
<% end %>
|
|
7
|
+
</script>
|