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
|
@@ -6,7 +6,7 @@ module Decidim
|
|
|
6
6
|
class Question < Forms::ApplicationRecord
|
|
7
7
|
include Decidim::TranslatableResource
|
|
8
8
|
|
|
9
|
-
QUESTION_TYPES = %w(
|
|
9
|
+
QUESTION_TYPES = %w(short_response long_response single_option multiple_option sorting files matrix_single matrix_multiple).freeze
|
|
10
10
|
SEPARATOR_TYPE = "separator"
|
|
11
11
|
TITLE_AND_DESCRIPTION_TYPE = "title_and_description"
|
|
12
12
|
TYPES = (QUESTION_TYPES + [SEPARATOR_TYPE, TITLE_AND_DESCRIPTION_TYPE]).freeze
|
|
@@ -21,8 +21,8 @@ module Decidim
|
|
|
21
21
|
dependent: :destroy,
|
|
22
22
|
inverse_of: :question
|
|
23
23
|
|
|
24
|
-
has_many :
|
|
25
|
-
class_name: "
|
|
24
|
+
has_many :response_options,
|
|
25
|
+
class_name: "ResponseOption",
|
|
26
26
|
foreign_key: "decidim_question_id",
|
|
27
27
|
dependent: :destroy,
|
|
28
28
|
inverse_of: :question
|
|
@@ -34,21 +34,22 @@ module Decidim
|
|
|
34
34
|
dependent: :destroy,
|
|
35
35
|
inverse_of: :question
|
|
36
36
|
|
|
37
|
-
# Conditions to display other questions based on the value of this question's
|
|
37
|
+
# Conditions to display other questions based on the value of this question's response
|
|
38
38
|
has_many :display_conditions_for_other_questions,
|
|
39
39
|
class_name: "DisplayCondition",
|
|
40
40
|
foreign_key: "decidim_condition_question_id",
|
|
41
41
|
dependent: :destroy,
|
|
42
|
-
inverse_of: :question
|
|
42
|
+
inverse_of: :question,
|
|
43
|
+
counter_cache: :display_conditions_for_other_questions_count
|
|
43
44
|
|
|
44
|
-
# Questions which have display conditions based on the value of this question's
|
|
45
|
+
# Questions which have display conditions based on the value of this question's response
|
|
45
46
|
has_many :conditioned_questions,
|
|
46
47
|
through: :display_conditions_for_other_questions,
|
|
47
48
|
foreign_key: "decidim_condition_question_id",
|
|
48
49
|
class_name: "Question"
|
|
49
50
|
|
|
50
|
-
has_many :
|
|
51
|
-
class_name: "
|
|
51
|
+
has_many :responses,
|
|
52
|
+
class_name: "Response",
|
|
52
53
|
foreign_key: "decidim_question_id",
|
|
53
54
|
dependent: :destroy,
|
|
54
55
|
inverse_of: :question
|
|
@@ -58,8 +59,8 @@ module Decidim
|
|
|
58
59
|
scope :not_separator, -> { where.not(question_type: SEPARATOR_TYPE) }
|
|
59
60
|
scope :not_title_and_description, -> { where.not(question_type: TITLE_AND_DESCRIPTION_TYPE) }
|
|
60
61
|
|
|
61
|
-
scope :with_body, -> { where(question_type: %w(
|
|
62
|
-
scope :with_choices, -> { where.not(question_type: %w(
|
|
62
|
+
scope :with_body, -> { where(question_type: %w(short_response long_response)) }
|
|
63
|
+
scope :with_choices, -> { where.not(question_type: %w(short_response long_response)) }
|
|
63
64
|
|
|
64
65
|
scope :conditioned, -> { includes(:display_conditions).where.not(decidim_forms_display_conditions: { id: nil }) }
|
|
65
66
|
scope :not_conditioned, -> { includes(:display_conditions).where(decidim_forms_display_conditions: { id: nil }) }
|
|
@@ -81,7 +82,7 @@ module Decidim
|
|
|
81
82
|
end
|
|
82
83
|
|
|
83
84
|
def number_of_options
|
|
84
|
-
|
|
85
|
+
response_options.size
|
|
85
86
|
end
|
|
86
87
|
|
|
87
88
|
def translated_body
|
|
@@ -100,8 +101,8 @@ module Decidim
|
|
|
100
101
|
question_type.to_s == "files"
|
|
101
102
|
end
|
|
102
103
|
|
|
103
|
-
def
|
|
104
|
-
questionnaire.
|
|
104
|
+
def responses_count
|
|
105
|
+
questionnaire.responses.where(question: self).count
|
|
105
106
|
end
|
|
106
107
|
|
|
107
108
|
def self.log_presenter_class_for(_log)
|
|
@@ -9,7 +9,7 @@ module Decidim
|
|
|
9
9
|
translatable_fields :body
|
|
10
10
|
belongs_to :question, class_name: "Question", foreign_key: "decidim_question_id", counter_cache: :matrix_rows_count
|
|
11
11
|
|
|
12
|
-
delegate :
|
|
12
|
+
delegate :response_options, :mandatory, :max_choices, to: :question
|
|
13
13
|
|
|
14
14
|
scope :by_position, -> { order(:position) }
|
|
15
15
|
default_scope { by_position }
|
|
@@ -13,22 +13,27 @@ module Decidim
|
|
|
13
13
|
belongs_to :questionnaire_for, polymorphic: true
|
|
14
14
|
|
|
15
15
|
has_many :questions, -> { order(:position) }, class_name: "Question", foreign_key: "decidim_questionnaire_id", dependent: :destroy
|
|
16
|
-
has_many :
|
|
16
|
+
has_many :responses, class_name: "Response", foreign_key: "decidim_questionnaire_id", dependent: :destroy
|
|
17
17
|
|
|
18
18
|
after_initialize :set_default_salt
|
|
19
19
|
|
|
20
20
|
attr_accessor :questionnaire_template_id
|
|
21
|
+
attr_reader :override_edit
|
|
21
22
|
|
|
22
23
|
# Public: returns whether the questionnaire questions can be modified or not.
|
|
23
24
|
def questions_editable?
|
|
24
25
|
has_component = questionnaire_for.respond_to? :component
|
|
25
|
-
(has_component && !questionnaire_for.component.published?) ||
|
|
26
|
+
(has_component && !questionnaire_for.component.published?) || override_edit.presence || responses.empty?
|
|
26
27
|
end
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
def override_edit!
|
|
30
|
+
@override_edit = true
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Public: returns whether the questionnaire is responded by the user or not.
|
|
34
|
+
def responded_by?(user)
|
|
30
35
|
query = user.is_a?(String) ? { session_token: user } : { user: }
|
|
31
|
-
|
|
36
|
+
responses.where(query).any? if questions.present?
|
|
32
37
|
end
|
|
33
38
|
|
|
34
39
|
def pristine?
|
|
@@ -43,14 +48,9 @@ module Decidim
|
|
|
43
48
|
Decidim::Forms::QuestionnaireParticipants.new(self).count_participants
|
|
44
49
|
end
|
|
45
50
|
|
|
46
|
-
# Public: Returns only the actual question types (excludes separators and title_and_description)
|
|
47
|
-
def question_types
|
|
48
|
-
questions.not_separator.not_title_and_description
|
|
49
|
-
end
|
|
50
|
-
|
|
51
51
|
private
|
|
52
52
|
|
|
53
|
-
# salt is used to generate secure hash in anonymous
|
|
53
|
+
# salt is used to generate secure hash in anonymous responses
|
|
54
54
|
def set_default_salt
|
|
55
55
|
return unless defined?(salt)
|
|
56
56
|
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module Decidim
|
|
4
4
|
module Forms
|
|
5
|
-
# The data store for an
|
|
6
|
-
class
|
|
5
|
+
# The data store for an Response in the Decidim::Forms
|
|
6
|
+
class Response < Forms::ApplicationRecord
|
|
7
7
|
include Decidim::DownloadYourData
|
|
8
8
|
include Decidim::NewsletterParticipant
|
|
9
9
|
include Decidim::HasAttachments
|
|
@@ -13,10 +13,10 @@ module Decidim
|
|
|
13
13
|
belongs_to :question, class_name: "Question", foreign_key: "decidim_question_id"
|
|
14
14
|
|
|
15
15
|
has_many :choices,
|
|
16
|
-
class_name: "
|
|
17
|
-
foreign_key: "
|
|
16
|
+
class_name: "ResponseChoice",
|
|
17
|
+
foreign_key: "decidim_response_id",
|
|
18
18
|
dependent: :destroy,
|
|
19
|
-
inverse_of: :
|
|
19
|
+
inverse_of: :response
|
|
20
20
|
|
|
21
21
|
validate :user_questionnaire_same_organization
|
|
22
22
|
validate :question_belongs_to_questionnaire
|
|
@@ -29,7 +29,7 @@ module Decidim
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def self.export_serializer
|
|
32
|
-
Decidim::Forms::
|
|
32
|
+
Decidim::Forms::DownloadYourDataUserResponsesSerializer
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def self.newsletter_participant_ids(component)
|
|
@@ -37,10 +37,10 @@ module Decidim
|
|
|
37
37
|
questionnaires = Decidim::Forms::Questionnaire.includes(:questionnaire_for)
|
|
38
38
|
.where(questionnaire_for_type: Decidim::Surveys::Survey.name, questionnaire_for_id: surveys.pluck(:id))
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
responses = Decidim::Forms::Response.joins(:questionnaire)
|
|
41
|
+
.where(questionnaire: questionnaires)
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
responses.pluck(:decidim_user_id).flatten.compact.uniq
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
def organization
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Forms
|
|
5
|
+
class ResponseChoice < Forms::ApplicationRecord
|
|
6
|
+
belongs_to :response,
|
|
7
|
+
class_name: "Response",
|
|
8
|
+
foreign_key: "decidim_response_id"
|
|
9
|
+
|
|
10
|
+
belongs_to :response_option,
|
|
11
|
+
class_name: "ResponseOption",
|
|
12
|
+
foreign_key: "decidim_response_option_id"
|
|
13
|
+
|
|
14
|
+
belongs_to :matrix_row,
|
|
15
|
+
class_name: "QuestionMatrixRow",
|
|
16
|
+
foreign_key: "decidim_question_matrix_row_id",
|
|
17
|
+
optional: true
|
|
18
|
+
|
|
19
|
+
validates :matrix_row, presence: true, if: -> { response.question.matrix? }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -2,23 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
module Decidim
|
|
4
4
|
module Forms
|
|
5
|
-
class
|
|
5
|
+
class ResponseOption < Forms::ApplicationRecord
|
|
6
6
|
include Decidim::TranslatableResource
|
|
7
7
|
|
|
8
8
|
default_scope { order(arel_table[:id].asc) }
|
|
9
9
|
|
|
10
10
|
translatable_fields :body
|
|
11
11
|
|
|
12
|
-
belongs_to :question, class_name: "Question", foreign_key: "decidim_question_id", counter_cache: :
|
|
12
|
+
belongs_to :question, class_name: "Question", foreign_key: "decidim_question_id", counter_cache: :response_options_count
|
|
13
13
|
|
|
14
14
|
has_many :display_conditions,
|
|
15
15
|
class_name: "DisplayCondition",
|
|
16
|
-
foreign_key: "
|
|
16
|
+
foreign_key: "decidim_response_option_id",
|
|
17
17
|
dependent: :nullify,
|
|
18
|
-
inverse_of: :
|
|
18
|
+
inverse_of: :response_option
|
|
19
19
|
|
|
20
20
|
def translated_body
|
|
21
|
-
Decidim::Forms::
|
|
21
|
+
Decidim::Forms::ResponseOptionPresenter.new(self).translated_body
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
end
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import "src/decidim/forms/admin/collapsible_questions"
|
|
2
2
|
|
|
3
3
|
import createEditableForm from "src/decidim/forms/admin/forms"
|
|
4
|
-
import
|
|
4
|
+
import createPublicableQuestionResponsesButtons from "src/decidim/forms/admin/publish_responses_buttons"
|
|
5
5
|
|
|
6
6
|
window.Decidim.createEditableForm = createEditableForm
|
|
7
7
|
|
|
8
|
-
document.addEventListener("
|
|
9
|
-
document.querySelectorAll("[data-publish-question-
|
|
8
|
+
document.addEventListener("turbo:load", () => {
|
|
9
|
+
document.querySelectorAll("[data-publish-question-response-action]").forEach((el) => createPublicableQuestionResponsesButtons(el));
|
|
10
10
|
});
|
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
(() => {
|
|
1
|
+
document.addEventListener("turbo:load", () => {
|
|
2
2
|
const getButtons = document.querySelectorAll("button.question--collapse");
|
|
3
3
|
|
|
4
4
|
setTimeout(() => {
|
|
5
|
-
|
|
5
|
+
getButtons.forEach((button) => {
|
|
6
6
|
if (button.classList.contains("question-error")) {
|
|
7
|
-
button.click()
|
|
7
|
+
button.click();
|
|
8
8
|
}
|
|
9
|
-
})
|
|
10
|
-
}, 100)
|
|
9
|
+
});
|
|
10
|
+
}, 100);
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
document.querySelector("button.collapse-all")?.addEventListener("click", () => {
|
|
13
|
+
document.querySelectorAll("[id$=field] button.question--collapse[aria-expanded='true']").
|
|
14
|
+
forEach((button) => button.click());
|
|
14
15
|
});
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
document.querySelector("button.expand-all")?.addEventListener("click", () => {
|
|
18
|
+
document.querySelectorAll("[id$=field] button.question--collapse[aria-expanded='false']").
|
|
19
|
+
forEach((button) => button.click());
|
|
18
20
|
});
|
|
19
|
-
})
|
|
21
|
+
});
|
|
@@ -7,36 +7,35 @@ import AutoSelectOptionsFromUrl from "src/decidim/forms/admin/auto_select_option
|
|
|
7
7
|
import createLiveTextUpdateComponent from "src/decidim/forms/admin/live_text_update.component"
|
|
8
8
|
import AutoButtonsByPositionComponent from "src/decidim/admin/auto_buttons_by_position.component"
|
|
9
9
|
import AutoLabelByPositionComponent from "src/decidim/admin/auto_label_by_position.component"
|
|
10
|
+
import createSortList from "src/decidim/admin/sort_list.component"
|
|
10
11
|
import createDynamicFields from "src/decidim/admin/dynamic_fields.component"
|
|
11
12
|
import createFieldDependentInputs from "src/decidim/admin/field_dependent_inputs.component"
|
|
12
13
|
import initLanguageChangeSelect from "src/decidim/admin/choose_language"
|
|
13
|
-
import sortable from "html5sortable/dist/html5sortable.es"
|
|
14
14
|
|
|
15
15
|
export default function createEditableForm() {
|
|
16
16
|
const wrapperSelector = ".questionnaire-questions";
|
|
17
17
|
const fieldSelector = ".questionnaire-question";
|
|
18
18
|
const questionTypeSelector = "select[name$=\\[question_type\\]]";
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
19
|
+
const responseOptionFieldSelector = ".questionnaire-question-response-option";
|
|
20
|
+
const responseOptionsWrapperSelector = ".questionnaire-question-response-options";
|
|
21
|
+
const responseOptionRemoveFieldButtonSelector = ".remove-response-option";
|
|
22
22
|
const matrixRowFieldSelector = ".questionnaire-question-matrix-row";
|
|
23
23
|
const matrixRowsWrapperSelector = ".questionnaire-question-matrix-rows";
|
|
24
24
|
const matrixRowRemoveFieldButtonSelector = ".remove-matrix-row";
|
|
25
25
|
const addMatrixRowButtonSelector = ".add-matrix-row";
|
|
26
26
|
const maxChoicesWrapperSelector = ".questionnaire-question-max-choices";
|
|
27
|
-
const responseOptionFreeTextSelector = ".questionnaire-question-answer-option-free-text";
|
|
28
27
|
|
|
29
28
|
const displayConditionFieldSelector = ".questionnaire-question-display-condition";
|
|
30
29
|
const displayConditionsWrapperSelector = ".questionnaire-question-display-conditions";
|
|
31
30
|
const displayConditionRemoveFieldButtonSelector = ".remove-display-condition";
|
|
32
31
|
|
|
33
32
|
const displayConditionQuestionSelector = "select[name$=\\[decidim_condition_question_id\\]]";
|
|
34
|
-
const
|
|
33
|
+
const displayConditionResponseOptionSelector = "select[name$=\\[decidim_response_option_id\\]]";
|
|
35
34
|
const displayConditionTypeSelector = "select[name$=\\[condition_type\\]]";
|
|
36
35
|
const deletedInputSelector = "input[name$=\\[deleted\\]]";
|
|
37
36
|
|
|
38
37
|
const displayConditionValueWrapperSelector = ".questionnaire-question-display-condition-value";
|
|
39
|
-
const
|
|
38
|
+
const displayconditionResponseOptionWrapperSelector = ".questionnaire-question-display-condition-response-option";
|
|
40
39
|
|
|
41
40
|
const addDisplayConditionButtonSelector = ".add-display-condition";
|
|
42
41
|
|
|
@@ -75,41 +74,41 @@ export default function createEditableForm() {
|
|
|
75
74
|
const MULTIPLE_CHOICE_VALUES = ["single_option", "multiple_option", "sorting", "matrix_single", "matrix_multiple"];
|
|
76
75
|
const MATRIX_VALUES = ["matrix_single", "matrix_multiple"];
|
|
77
76
|
|
|
78
|
-
const
|
|
77
|
+
const createAutoMaxChoicesByNumberOfResponseOptions = (fieldId) => {
|
|
79
78
|
return new AutoSelectOptionsByTotalItemsComponent({
|
|
80
79
|
wrapperSelector: fieldSelector,
|
|
81
80
|
selectSelector: `${maxChoicesWrapperSelector} select`,
|
|
82
|
-
listSelector: `#${fieldId} ${
|
|
81
|
+
listSelector: `#${fieldId} ${responseOptionsWrapperSelector} .questionnaire-question-response-option:not(.hidden)`
|
|
83
82
|
})
|
|
84
83
|
};
|
|
85
84
|
|
|
86
|
-
const
|
|
85
|
+
const createAutoButtonsByMinItemsForResponseOptions = (fieldId) => {
|
|
87
86
|
return new AutoButtonsByMinItemsComponent({
|
|
88
87
|
wrapperSelector: fieldSelector,
|
|
89
|
-
listSelector: `#${fieldId} ${
|
|
88
|
+
listSelector: `#${fieldId} ${responseOptionsWrapperSelector} .questionnaire-question-response-option:not(.hidden)`,
|
|
90
89
|
minItems: 2,
|
|
91
|
-
hideOnMinItemsOrLessSelector:
|
|
90
|
+
hideOnMinItemsOrLessSelector: responseOptionRemoveFieldButtonSelector
|
|
92
91
|
})
|
|
93
92
|
};
|
|
94
93
|
|
|
95
94
|
const createAutoSelectOptionsFromUrl = ($field) => {
|
|
96
95
|
return new AutoSelectOptionsFromUrl({
|
|
97
96
|
source: $field.find(displayConditionQuestionSelector),
|
|
98
|
-
select: $field.find(
|
|
97
|
+
select: $field.find(displayConditionResponseOptionSelector),
|
|
99
98
|
sourceToParams: ($element) => { return { id: $element.val() } }
|
|
100
99
|
})
|
|
101
100
|
};
|
|
102
101
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
102
|
+
const createSortableList = () => {
|
|
103
|
+
createSortList(".questionnaire-questions-list:not(.published)", {
|
|
104
|
+
handle: ".question-divider",
|
|
105
|
+
placeholder: '<div style="border-style: dashed; border-color: #000"></div>',
|
|
106
|
+
forcePlaceholderSize: true,
|
|
107
|
+
onSortUpdate: () => {
|
|
108
108
|
autoLabelByPosition.run();
|
|
109
109
|
autoButtonsByPosition.run();
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
}
|
|
110
|
+
}
|
|
111
|
+
});
|
|
113
112
|
};
|
|
114
113
|
|
|
115
114
|
const createDynamicQuestionTitle = (fieldId) => {
|
|
@@ -139,18 +138,18 @@ export default function createEditableForm() {
|
|
|
139
138
|
}
|
|
140
139
|
};
|
|
141
140
|
|
|
142
|
-
const
|
|
143
|
-
const autoButtons =
|
|
144
|
-
const autoSelectOptions =
|
|
141
|
+
const createDynamicFieldsForResponseOptions = (fieldId) => {
|
|
142
|
+
const autoButtons = createAutoButtonsByMinItemsForResponseOptions(fieldId);
|
|
143
|
+
const autoSelectOptions = createAutoMaxChoicesByNumberOfResponseOptions(fieldId);
|
|
145
144
|
|
|
146
145
|
return createDynamicFields({
|
|
147
|
-
placeholderId: "questionnaire-question-
|
|
148
|
-
wrapperSelector: `#${fieldId} ${
|
|
149
|
-
containerSelector: ".questionnaire-question-
|
|
150
|
-
fieldSelector:
|
|
151
|
-
addFieldButtonSelector: ".add-
|
|
152
|
-
fieldTemplateSelector: ".decidim-
|
|
153
|
-
removeFieldButtonSelector:
|
|
146
|
+
placeholderId: "questionnaire-question-response-option-id",
|
|
147
|
+
wrapperSelector: `#${fieldId} ${responseOptionsWrapperSelector}`,
|
|
148
|
+
containerSelector: ".questionnaire-question-response-options-list",
|
|
149
|
+
fieldSelector: responseOptionFieldSelector,
|
|
150
|
+
addFieldButtonSelector: ".add-response-option",
|
|
151
|
+
fieldTemplateSelector: ".decidim-response-option-template",
|
|
152
|
+
removeFieldButtonSelector: responseOptionRemoveFieldButtonSelector,
|
|
154
153
|
onAddField: () => {
|
|
155
154
|
autoButtons.run();
|
|
156
155
|
autoSelectOptions.run();
|
|
@@ -162,7 +161,7 @@ export default function createEditableForm() {
|
|
|
162
161
|
});
|
|
163
162
|
};
|
|
164
163
|
|
|
165
|
-
const
|
|
164
|
+
const dynamicFieldsForResponseOptions = {};
|
|
166
165
|
|
|
167
166
|
const createDynamicFieldsForMatrixRows = (fieldId) => {
|
|
168
167
|
return createDynamicFields({
|
|
@@ -201,7 +200,7 @@ export default function createEditableForm() {
|
|
|
201
200
|
|
|
202
201
|
const isMultiple = isMultipleChoiceOption(selectedQuestionType);
|
|
203
202
|
|
|
204
|
-
let conditionTypes = ["
|
|
203
|
+
let conditionTypes = ["responded", "not_responded"];
|
|
205
204
|
|
|
206
205
|
if (isMultiple) {
|
|
207
206
|
conditionTypes.push("equal");
|
|
@@ -237,7 +236,7 @@ export default function createEditableForm() {
|
|
|
237
236
|
const onDisplayConditionTypeChange = ($field) => {
|
|
238
237
|
const value = $field.find(displayConditionTypeSelector).val();
|
|
239
238
|
const $valueWrapper = $field.find(displayConditionValueWrapperSelector);
|
|
240
|
-
const $
|
|
239
|
+
const $responseOptionWrapper = $field.find(displayconditionResponseOptionWrapperSelector);
|
|
241
240
|
|
|
242
241
|
const $questionSelector = $field.find(displayConditionQuestionSelector);
|
|
243
242
|
const selectedQuestionType = getSelectedQuestionType($questionSelector[0]);
|
|
@@ -252,10 +251,10 @@ export default function createEditableForm() {
|
|
|
252
251
|
}
|
|
253
252
|
|
|
254
253
|
if (isMultiple && (value === "not_equal" || value === "equal")) {
|
|
255
|
-
$
|
|
254
|
+
$responseOptionWrapper.show();
|
|
256
255
|
}
|
|
257
256
|
else {
|
|
258
|
-
$
|
|
257
|
+
$responseOptionWrapper.hide();
|
|
259
258
|
}
|
|
260
259
|
};
|
|
261
260
|
|
|
@@ -303,8 +302,8 @@ export default function createEditableForm() {
|
|
|
303
302
|
createFieldDependentInputs({
|
|
304
303
|
controllerField: $fieldQuestionTypeSelect,
|
|
305
304
|
wrapperSelector: fieldSelector,
|
|
306
|
-
dependentFieldsSelector:
|
|
307
|
-
dependentInputSelector: `${
|
|
305
|
+
dependentFieldsSelector: responseOptionsWrapperSelector,
|
|
306
|
+
dependentInputSelector: `${responseOptionFieldSelector} input`,
|
|
308
307
|
enablingCondition: ($field) => {
|
|
309
308
|
return isMultipleChoiceOption($field.val());
|
|
310
309
|
}
|
|
@@ -330,23 +329,20 @@ export default function createEditableForm() {
|
|
|
330
329
|
}
|
|
331
330
|
});
|
|
332
331
|
|
|
333
|
-
|
|
332
|
+
dynamicFieldsForResponseOptions[fieldId] = createDynamicFieldsForResponseOptions(fieldId);
|
|
334
333
|
dynamicFieldsForMatrixRows[fieldId] = createDynamicFieldsForMatrixRows(fieldId);
|
|
335
334
|
dynamicFieldsForDisplayConditions[fieldId] = createDynamicFieldsForDisplayConditions(fieldId);
|
|
336
335
|
|
|
337
|
-
const
|
|
336
|
+
const dynamicFieldsResponseOptions = dynamicFieldsForResponseOptions[fieldId];
|
|
338
337
|
const dynamicFieldsMatrixRows = dynamicFieldsForMatrixRows[fieldId];
|
|
339
338
|
|
|
340
339
|
const onQuestionTypeChange = () => {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
if (isMultipleChoiceOption(questionType)) {
|
|
345
|
-
const nOptions = $fieldQuestionTypeSelect.parents(fieldSelector).find(answerOptionFieldSelector).length;
|
|
340
|
+
if (isMultipleChoiceOption($fieldQuestionTypeSelect.val())) {
|
|
341
|
+
const nOptions = $fieldQuestionTypeSelect.parents(fieldSelector).find(responseOptionFieldSelector).length;
|
|
346
342
|
|
|
347
343
|
if (nOptions === 0) {
|
|
348
|
-
|
|
349
|
-
|
|
344
|
+
dynamicFieldsResponseOptions._addField();
|
|
345
|
+
dynamicFieldsResponseOptions._addField();
|
|
350
346
|
}
|
|
351
347
|
}
|
|
352
348
|
|
|
@@ -358,12 +354,6 @@ export default function createEditableForm() {
|
|
|
358
354
|
dynamicFieldsMatrixRows._addField();
|
|
359
355
|
}
|
|
360
356
|
}
|
|
361
|
-
|
|
362
|
-
if (questionType === "sorting") {
|
|
363
|
-
$currentField.find(responseOptionFreeTextSelector).addClass("hidden");
|
|
364
|
-
} else {
|
|
365
|
-
$currentField.find(responseOptionFreeTextSelector).removeClass("hidden");
|
|
366
|
-
}
|
|
367
357
|
};
|
|
368
358
|
|
|
369
359
|
$fieldQuestionTypeSelect.on("change", onQuestionTypeChange);
|
|
@@ -396,22 +386,13 @@ export default function createEditableForm() {
|
|
|
396
386
|
moveDownFieldButtonSelector: ".move-down-question",
|
|
397
387
|
onAddField: ($field) => {
|
|
398
388
|
setupInitialQuestionAttributes($field);
|
|
399
|
-
|
|
389
|
+
createSortableList();
|
|
400
390
|
|
|
401
391
|
autoLabelByPosition.run();
|
|
402
392
|
autoButtonsByPosition.run();
|
|
403
393
|
|
|
404
394
|
initLanguageChangeSelect($field.find("select.language-change").toArray());
|
|
405
395
|
|
|
406
|
-
const sortableContainer = document.querySelector(".questionnaire-questions-list[data-draggable-table]");
|
|
407
|
-
if (sortableContainer) {
|
|
408
|
-
sortable(sortableContainer, {
|
|
409
|
-
forcePlaceholderSize: true,
|
|
410
|
-
items: ".questionnaire-question",
|
|
411
|
-
handle: sortableContainer.dataset.draggableHandle || ".card-divider"
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
|
-
|
|
415
396
|
// instead of initialize specific stuff, we send an event, with the DOM fragment we wanna update/refresh/bind
|
|
416
397
|
document.dispatchEvent(new CustomEvent("ajax:loaded", { detail: $field[0] }));
|
|
417
398
|
},
|
|
@@ -419,8 +400,8 @@ export default function createEditableForm() {
|
|
|
419
400
|
autoLabelByPosition.run();
|
|
420
401
|
autoButtonsByPosition.run();
|
|
421
402
|
|
|
422
|
-
$field.find(
|
|
423
|
-
|
|
403
|
+
$field.find(responseOptionRemoveFieldButtonSelector).each((idx, el) => {
|
|
404
|
+
dynamicFieldsForResponseOptions[$field.attr("id")]._removeField(el);
|
|
424
405
|
});
|
|
425
406
|
|
|
426
407
|
$field.find(matrixRowRemoveFieldButtonSelector).each((idx, el) => {
|
|
@@ -441,7 +422,7 @@ export default function createEditableForm() {
|
|
|
441
422
|
}
|
|
442
423
|
});
|
|
443
424
|
|
|
444
|
-
|
|
425
|
+
createSortableList();
|
|
445
426
|
|
|
446
427
|
$(fieldSelector).each((idx, el) => {
|
|
447
428
|
const $target = $(el);
|
data/app/packs/src/decidim/forms/admin/{publish_answers_buttons.js → publish_responses_buttons.js}
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Initializes event listeners for a button to toggle the publication status of question
|
|
2
|
+
* Initializes event listeners for a button to toggle the publication status of question responses.
|
|
3
3
|
*
|
|
4
|
-
* The button's `data-publish-question-
|
|
4
|
+
* The button's `data-publish-question-response-action` attribute determines whether the action is
|
|
5
5
|
* to "publish" or "unpublish." Based on this attribute, the function makes an AJAX request using
|
|
6
6
|
* Rails' AJAX helper and updates the button's state and associated labels.
|
|
7
7
|
*
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* @returns {void}
|
|
11
11
|
*
|
|
12
12
|
* Button element must have the following data attributes:
|
|
13
|
-
* - `data-publish-question-
|
|
14
|
-
* - `data-publish-question-
|
|
13
|
+
* - `data-publish-question-response-action`: Specifies the current action ("publish" or "unpublish").
|
|
14
|
+
* - `data-publish-question-response-question-url`: Specifies the endpoint URL for the AJAX request.
|
|
15
15
|
*
|
|
16
16
|
* The DOM structure must include:
|
|
17
17
|
* - A parent element with the class `toggle__switch-toggle`.
|
|
@@ -28,16 +28,16 @@
|
|
|
28
28
|
* <span class="label alert hidden">Unpublished</span>
|
|
29
29
|
* <span>
|
|
30
30
|
* <input
|
|
31
|
-
* data-publish-question-
|
|
32
|
-
* data-publish-question-
|
|
31
|
+
* data-publish-question-response-action="unpublish"
|
|
32
|
+
* data-publish-question-response-question-url="/path/to/publish/or/unpublish">
|
|
33
33
|
* </span>
|
|
34
34
|
* </label>
|
|
35
35
|
* ```
|
|
36
36
|
*/
|
|
37
|
-
export default function
|
|
37
|
+
export default function createPublicableQuestionResponsesButtons(button) {
|
|
38
38
|
button.addEventListener("click", () => {
|
|
39
|
-
const action = button.dataset.
|
|
40
|
-
const url = button.dataset.
|
|
39
|
+
const action = button.dataset.publishQuestionResponseAction;
|
|
40
|
+
const url = button.dataset.publishQuestionResponseQuestionUrl;
|
|
41
41
|
const buttonText = button.closest(".toggle__switch-toggle").querySelector(".toggle__switch-trigger-text");
|
|
42
42
|
const publishedLabel = buttonText.querySelector(".label.success");
|
|
43
43
|
const unpublishedLabel = buttonText.querySelector(".label.alert");
|
|
@@ -48,7 +48,7 @@ export default function createPublicableQuestionAnswersButtons(button) {
|
|
|
48
48
|
url: url,
|
|
49
49
|
type: "PUT",
|
|
50
50
|
success: function() {
|
|
51
|
-
button.setAttribute("data-publish-question-
|
|
51
|
+
button.setAttribute("data-publish-question-response-action", "unpublish")
|
|
52
52
|
publishedLabel.classList.remove("hidden");
|
|
53
53
|
unpublishedLabel.classList.add("hidden");
|
|
54
54
|
}
|
|
@@ -59,14 +59,14 @@ export default function createPublicableQuestionAnswersButtons(button) {
|
|
|
59
59
|
url: url,
|
|
60
60
|
type: "DELETE",
|
|
61
61
|
success: function() {
|
|
62
|
-
button.setAttribute("data-publish-question-
|
|
62
|
+
button.setAttribute("data-publish-question-response-action", "publish")
|
|
63
63
|
unpublishedLabel.classList.remove("hidden");
|
|
64
64
|
publishedLabel.classList.add("hidden");
|
|
65
65
|
}
|
|
66
66
|
});
|
|
67
67
|
break;
|
|
68
68
|
default:
|
|
69
|
-
console.log(`Publish questions
|
|
69
|
+
console.log(`Publish questions responses: Unknown action ${action}`);
|
|
70
70
|
}
|
|
71
71
|
});
|
|
72
72
|
}
|