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.
- 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 +15 -15
- 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 +18 -16
- 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 -11
- 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 +47 -66
- 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_response_presenter.rb +89 -0
- 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 +25 -15
- data/app/views/decidim/forms/admin/questionnaires/_questions_form.html.erb +14 -21
- data/app/views/decidim/forms/admin/questionnaires/_response_option.html.erb +45 -0
- data/app/views/decidim/forms/admin/questionnaires/_response_option_template.html.erb +7 -0
- data/app/views/decidim/forms/admin/questionnaires/_separator.html.erb +15 -5
- data/app/views/decidim/forms/admin/questionnaires/_title_and_description.html.erb +15 -5
- 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 +24 -22
- 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 +82 -76
- data/config/locales/ca.yml +82 -76
- data/config/locales/cs.yml +79 -76
- data/config/locales/de.yml +80 -74
- 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 +83 -77
- data/config/locales/fi-plain.yml +78 -72
- data/config/locales/fi.yml +76 -70
- data/config/locales/fr-CA.yml +72 -70
- data/config/locales/fr.yml +72 -70
- 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 -120
- data/config/locales/pt.yml +8 -36
- data/config/locales/ro-RO.yml +30 -52
- data/config/locales/ru.yml +2 -9
- data/config/locales/sk.yml +3 -183
- data/config/locales/sl.yml +0 -5
- data/config/locales/sv.yml +77 -70
- 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 +35 -43
- 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 +178 -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 -90
- 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 -6
- 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 -46
- 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/presenters/decidim/forms/admin/questionnaire_answer_presenter.rb +0 -112
- data/app/queries/decidim/forms/questionnaire_user_answers.rb +0 -32
- data/app/views/decidim/forms/admin/questionnaires/_answer_option.html.erb +0 -45
- 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 -60
- 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/test/shared_examples/manage_questionnaires/draggable_behavior.rb +0 -47
- data/lib/decidim/forms/user_answers_serializer.rb +0 -105
- /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.
|
|
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$=\\[
|
|
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
|
-
|
|
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
|
-
|
|
66
|
-
return !this.
|
|
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.
|
|
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.
|
|
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 "
|
|
103
|
-
fulfilled = this.
|
|
102
|
+
case "responded":
|
|
103
|
+
fulfilled = this.checkRespondedCondition(value);
|
|
104
104
|
break;
|
|
105
|
-
case "
|
|
106
|
-
fulfilled = this.
|
|
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
|
-
|
|
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(".
|
|
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;
|
|
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
|
|
@@ -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.
|
|
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,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-
|
|
5
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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>
|