decidim-meetings 0.24.3 → 0.25.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/meetings/join_meeting_button/registration_confirm.erb +6 -0
  3. data/app/cells/decidim/meetings/join_meeting_button/show.erb +8 -6
  4. data/app/cells/decidim/meetings/meeting_m_cell.rb +4 -0
  5. data/app/cells/decidim/meetings/meeting_url/show.erb +7 -2
  6. data/app/cells/decidim/meetings/meeting_url_cell.rb +14 -2
  7. data/app/cells/decidim/meetings/meetings_map/show.erb +1 -1
  8. data/app/cells/decidim/meetings/online_meeting_cell.rb +30 -0
  9. data/app/cells/decidim/meetings/online_meeting_link/show.erb +44 -0
  10. data/app/cells/decidim/meetings/online_meeting_link_cell.rb +34 -0
  11. data/app/cells/decidim/meetings/public_participants_list/show.erb +16 -0
  12. data/app/cells/decidim/meetings/public_participants_list_cell.rb +31 -0
  13. data/app/cells/decidim/meetings/question_responses/show.erb +7 -0
  14. data/app/cells/decidim/meetings/question_responses_cell.rb +72 -0
  15. data/app/commands/decidim/meetings/admin/close_meeting.rb +3 -0
  16. data/app/commands/decidim/meetings/admin/copy_meeting.rb +0 -9
  17. data/app/commands/decidim/meetings/admin/create_meeting.rb +4 -20
  18. data/app/commands/decidim/meetings/admin/destroy_meeting.rb +2 -0
  19. data/app/commands/decidim/meetings/admin/publish_meeting.rb +72 -0
  20. data/app/commands/decidim/meetings/admin/unpublish_meeting.rb +45 -0
  21. data/app/commands/decidim/meetings/admin/update_meeting.rb +6 -3
  22. data/app/commands/decidim/meetings/admin/update_question_status.rb +57 -0
  23. data/app/commands/decidim/meetings/admin/update_questionnaire.rb +93 -0
  24. data/app/commands/decidim/meetings/close_meeting.rb +2 -0
  25. data/app/commands/decidim/meetings/create_answer.rb +52 -0
  26. data/app/commands/decidim/meetings/create_meeting.rb +3 -1
  27. data/app/commands/decidim/meetings/join_meeting.rb +2 -1
  28. data/app/commands/decidim/meetings/update_meeting.rb +2 -1
  29. data/app/controllers/concerns/decidim/meetings/admin/filterable.rb +55 -0
  30. data/app/controllers/concerns/decidim/meetings/polls_resources.rb +29 -0
  31. data/app/controllers/decidim/meetings/admin/application_controller.rb +6 -2
  32. data/app/controllers/decidim/meetings/admin/invites_controller.rb +0 -2
  33. data/app/controllers/decidim/meetings/admin/meeting_closes_controller.rb +1 -1
  34. data/app/controllers/decidim/meetings/admin/meetings_controller.rb +46 -1
  35. data/app/controllers/decidim/meetings/admin/meetings_poll_controller.rb +107 -0
  36. data/app/controllers/decidim/meetings/calendars_controller.rb +8 -0
  37. data/app/controllers/decidim/meetings/live_events_controller.rb +29 -0
  38. data/app/controllers/decidim/meetings/meeting_closes_controller.rb +1 -1
  39. data/app/controllers/decidim/meetings/polls/answers_controller.rb +37 -0
  40. data/app/controllers/decidim/meetings/polls/questions_controller.rb +45 -0
  41. data/app/events/decidim/meetings/create_meeting_event.rb +16 -0
  42. data/app/forms/decidim/meetings/admin/answer_option_form.rb +24 -0
  43. data/app/forms/decidim/meetings/admin/close_meeting_form.rb +3 -1
  44. data/app/forms/decidim/meetings/admin/meeting_form.rb +12 -1
  45. data/app/forms/decidim/meetings/admin/question_form.rb +44 -0
  46. data/app/forms/decidim/meetings/admin/questionnaire_form.rb +19 -0
  47. data/app/forms/decidim/meetings/answer_choice_form.rb +14 -0
  48. data/app/forms/decidim/meetings/answer_form.rb +65 -0
  49. data/app/forms/decidim/meetings/close_meeting_form.rb +4 -0
  50. data/app/forms/decidim/meetings/join_meeting_form.rb +1 -0
  51. data/app/forms/decidim/meetings/meeting_form.rb +9 -0
  52. data/app/helpers/decidim/meetings/admin/filterable_helper.rb +13 -0
  53. data/app/helpers/decidim/meetings/application_helper.rb +3 -2
  54. data/app/helpers/decidim/meetings/meetings_helper.rb +21 -0
  55. data/app/models/decidim/meetings/answer.rb +48 -0
  56. data/app/models/decidim/meetings/answer_choice.rb +15 -0
  57. data/app/models/decidim/meetings/answer_option.rb +24 -0
  58. data/app/models/decidim/meetings/meeting.rb +88 -42
  59. data/app/models/decidim/meetings/poll.rb +19 -0
  60. data/app/models/decidim/meetings/question.rb +54 -0
  61. data/app/models/decidim/meetings/questionnaire.rb +23 -0
  62. data/app/models/decidim/meetings/registration.rb +2 -0
  63. data/app/packs/entrypoints/decidim_meetings.js +6 -0
  64. data/app/packs/entrypoints/decidim_meetings_admin.js +5 -0
  65. data/app/{assets/images/decidim/gamification/badges/attended_meetings.svg → packs/images/decidim/gamification/badges/decidim_gamification_badges_attended_meetings.svg} +0 -0
  66. data/app/{assets/images/decidim/meetings/icon.svg → packs/images/decidim/meetings/decidim_meetings.svg} +0 -0
  67. data/app/packs/src/decidim/meetings/admin/agendas.js +159 -0
  68. data/app/{assets/javascripts/decidim/meetings/admin/destroy_meeting_alert.js.es6 → packs/src/decidim/meetings/admin/destroy_meeting_alert.js} +0 -0
  69. data/app/{assets/javascripts/decidim/meetings/admin/meetings_form.js.es6 → packs/src/decidim/meetings/admin/meetings_form.js} +7 -4
  70. data/app/{assets/javascripts/decidim/meetings/admin/registrations_form.js.es6 → packs/src/decidim/meetings/admin/registrations_form.js} +0 -0
  71. data/app/{assets/javascripts/decidim/meetings/admin/registrations_invite_form.es6 → packs/src/decidim/meetings/admin/registrations_invite_form.js} +3 -3
  72. data/app/packs/src/decidim/meetings/meetings_form.js +54 -0
  73. data/app/packs/src/decidim/meetings/meetings_polls.js +41 -0
  74. data/app/packs/src/decidim/meetings/poll.component.js +166 -0
  75. data/app/packs/stylesheets/decidim/meetings/_header.scss +52 -0
  76. data/app/packs/stylesheets/decidim/meetings/_main.scss +178 -0
  77. data/app/packs/stylesheets/decidim/meetings/_meetings.scss +2 -0
  78. data/app/permissions/decidim/meetings/admin/permissions.rb +16 -8
  79. data/app/permissions/decidim/meetings/permissions.rb +42 -17
  80. data/app/presenters/decidim/meetings/admin_log/meeting_presenter.rb +5 -1
  81. data/app/presenters/decidim/meetings/meeting_presenter.rb +22 -1
  82. data/app/queries/decidim/meetings/filtered_meetings.rb +1 -1
  83. data/app/queries/decidim/meetings/metrics/meetings_metric_manage.rb +1 -1
  84. data/app/queries/decidim/meetings/questionnaire_user_answers.rb +29 -0
  85. data/app/serializers/decidim/meetings/data_portability_invite_serializer.rb +2 -1
  86. data/app/serializers/decidim/meetings/data_portability_registration_serializer.rb +2 -1
  87. data/app/serializers/decidim/meetings/registration_serializer.rb +3 -12
  88. data/app/services/decidim/meetings/calendar/component_calendar.rb +2 -2
  89. data/app/services/decidim/meetings/calendar/meeting_calendar.rb +33 -0
  90. data/app/services/decidim/meetings/calendar_renderer.rb +2 -0
  91. data/app/services/decidim/meetings/meeting_iframe_embedder.rb +86 -0
  92. data/app/services/decidim/meetings/meeting_search.rb +2 -2
  93. data/app/views/decidim/meetings/admin/agenda/_form.html.erb +1 -1
  94. data/app/views/decidim/meetings/admin/invite_join_meeting_mailer/invite.html.erb +4 -4
  95. data/app/views/decidim/meetings/admin/invites/_form.html.erb +1 -1
  96. data/app/views/decidim/meetings/admin/meeting_closes/_form.html.erb +16 -1
  97. data/app/views/decidim/meetings/admin/meeting_copies/_form.html.erb +1 -1
  98. data/app/views/decidim/meetings/admin/meetings/_form.html.erb +16 -1
  99. data/app/views/decidim/meetings/admin/meetings/index.html.erb +39 -11
  100. data/app/views/decidim/meetings/admin/poll/_answer_option.html.erb +34 -0
  101. data/app/views/decidim/meetings/admin/poll/_answer_option_template.html.erb +7 -0
  102. data/app/views/decidim/meetings/admin/poll/_form.html.erb +63 -0
  103. data/app/views/decidim/meetings/admin/poll/_question.html.erb +96 -0
  104. data/app/views/decidim/meetings/admin/poll/edit.html.erb +9 -0
  105. data/app/views/decidim/meetings/admin/registrations/_form.html.erb +1 -1
  106. data/app/views/decidim/meetings/directory/meetings/index.html.erb +4 -4
  107. data/app/views/decidim/meetings/directory/meetings/index.js.erb +2 -0
  108. data/app/views/decidim/meetings/layouts/live_event.html.erb +50 -0
  109. data/app/views/decidim/meetings/live_events/show.html.erb +12 -0
  110. data/app/views/decidim/meetings/meeting_closes/_form.html.erb +8 -1
  111. data/app/views/decidim/meetings/meeting_closes/edit.html.erb +1 -1
  112. data/app/views/decidim/meetings/meetings/_calendar_modal.html.erb +26 -0
  113. data/app/views/decidim/meetings/meetings/_filters.html.erb +1 -1
  114. data/app/views/decidim/meetings/meetings/_form.html.erb +6 -1
  115. data/app/views/decidim/meetings/meetings/_meeting_minutes.html.erb +7 -18
  116. data/app/views/decidim/meetings/meetings/edit.html.erb +1 -1
  117. data/app/views/decidim/meetings/meetings/index.html.erb +4 -4
  118. data/app/views/decidim/meetings/meetings/new.html.erb +1 -1
  119. data/app/views/decidim/meetings/meetings/show.html.erb +12 -13
  120. data/app/views/decidim/meetings/polls/answers/_multiple_option.html.erb +13 -0
  121. data/app/views/decidim/meetings/polls/answers/_single_option.html.erb +19 -0
  122. data/app/views/decidim/meetings/polls/answers/create.js.erb +5 -0
  123. data/app/views/decidim/meetings/polls/questions/_closed_question.html.erb +7 -0
  124. data/app/views/decidim/meetings/polls/questions/_index.html.erb +5 -0
  125. data/app/views/decidim/meetings/polls/questions/_index_admin.html.erb +43 -0
  126. data/app/views/decidim/meetings/polls/questions/_published_question.html.erb +29 -0
  127. data/app/views/decidim/meetings/polls/questions/_question.html.erb +7 -0
  128. data/app/views/decidim/meetings/polls/questions/index.js.erb +5 -0
  129. data/app/views/decidim/meetings/polls/questions/index_admin.js.erb +5 -0
  130. data/app/views/decidim/meetings/registration_mailer/confirmation.html.erb +5 -2
  131. data/app/views/devise/mailer/join_meeting.html.erb +6 -6
  132. data/config/assets.rb +11 -0
  133. data/config/locales/ar.yml +3 -24
  134. data/config/locales/ca.yml +36 -27
  135. data/config/locales/cs.yml +72 -26
  136. data/config/locales/de.yml +22 -27
  137. data/config/locales/el.yml +3 -24
  138. data/config/locales/en.yml +112 -26
  139. data/config/locales/es-MX.yml +13 -27
  140. data/config/locales/es-PY.yml +13 -27
  141. data/config/locales/es.yml +13 -27
  142. data/config/locales/eu.yml +3 -24
  143. data/config/locales/fi-plain.yml +70 -26
  144. data/config/locales/fi.yml +70 -26
  145. data/config/locales/fr-CA.yml +19 -26
  146. data/config/locales/fr-LU.yml +534 -0
  147. data/config/locales/fr.yml +19 -26
  148. data/config/locales/gl.yml +46 -24
  149. data/config/locales/hu.yml +5 -24
  150. data/config/locales/id-ID.yml +3 -24
  151. data/config/locales/is-IS.yml +2 -22
  152. data/config/locales/it.yml +117 -23
  153. data/config/locales/ja.yml +148 -58
  154. data/config/locales/lb-LU.yml +1 -0
  155. data/config/locales/lv.yml +3 -24
  156. data/config/locales/nl.yml +70 -25
  157. data/config/locales/no.yml +5 -26
  158. data/config/locales/pl.yml +22 -33
  159. data/config/locales/pt-BR.yml +178 -23
  160. data/config/locales/pt.yml +3 -24
  161. data/config/locales/ro-RO.yml +38 -27
  162. data/config/locales/ru.yml +3 -24
  163. data/config/locales/sk.yml +3 -24
  164. data/config/locales/sv.yml +36 -27
  165. data/config/locales/tr-TR.yml +5 -27
  166. data/config/locales/uk.yml +3 -24
  167. data/config/locales/zh-CN.yml +3 -24
  168. data/db/migrate/20210217124802_add_registration_custom_content_to_meetings.rb +8 -0
  169. data/db/migrate/20210413050756_add_published_at_to_meetings.rb +7 -0
  170. data/db/migrate/20210413050917_update_published_at_to_existing_meetings.rb +20 -0
  171. data/db/migrate/20210430123416_add_public_participation_to_decidim_meetings_registrations.rb +7 -0
  172. data/db/migrate/20210506180226_merge_meetings_minutes_into_meetings_table.rb +48 -0
  173. data/db/migrate/20210512055802_create_decidim_meetings_polls.rb +10 -0
  174. data/db/migrate/20210512100333_drop_decidim_meetings_minutes_table.rb +77 -0
  175. data/db/migrate/20210518133236_merge_minutes_with_closing_report_in_meetings_table.rb +57 -0
  176. data/db/migrate/20210520084247_create_decidim_meetings_questionnaires.rb +11 -0
  177. data/db/migrate/20210520084253_create_decidim_meetings_questions.rb +15 -0
  178. data/db/migrate/20210520084321_create_decidim_meetings_answers.rb +13 -0
  179. data/db/migrate/20210520084330_create_decidim_meetings_answer_options.rb +10 -0
  180. data/db/migrate/20210520084337_create_decidim_meetings_answer_choices.rb +13 -0
  181. data/db/migrate/20210520134834_add_status_to_meetings_questions.rb +7 -0
  182. data/db/migrate/20210602040614_add_setting_embed_iframe_to_meetings.rb +7 -0
  183. data/lib/decidim/api/linked_resources_interface.rb +8 -1
  184. data/lib/decidim/api/meeting_type.rb +19 -4
  185. data/lib/decidim/api/meetings_type.rb +2 -2
  186. data/lib/decidim/meetings.rb +10 -0
  187. data/lib/decidim/meetings/admin_engine.rb +5 -1
  188. data/lib/decidim/meetings/api.rb +0 -1
  189. data/lib/decidim/meetings/component.rb +41 -6
  190. data/lib/decidim/meetings/data_portability_user_answers_serializer.rb +33 -0
  191. data/lib/decidim/meetings/engine.rb +10 -11
  192. data/lib/decidim/meetings/meeting_serializer.rb +1 -0
  193. data/lib/decidim/meetings/polls.rb +10 -0
  194. data/lib/decidim/meetings/test/factories.rb +85 -9
  195. data/lib/decidim/meetings/user_answers_serializer.rb +47 -0
  196. data/lib/decidim/meetings/version.rb +1 -1
  197. metadata +104 -114
  198. data/app/assets/config/decidim_meetings_manifest.js +0 -9
  199. data/app/assets/javascripts/decidim/meetings/admin/agendas.js.es6 +0 -158
  200. data/app/assets/javascripts/decidim/meetings/meetings_form.js.es6 +0 -54
  201. data/app/commands/decidim/meetings/admin/create_minutes.rb +0 -55
  202. data/app/commands/decidim/meetings/admin/update_minutes.rb +0 -63
  203. data/app/controllers/decidim/meetings/admin/minutes_controller.rb +0 -69
  204. data/app/forms/decidim/meetings/admin/minutes_form.rb +0 -20
  205. data/app/models/decidim/meetings/minutes.rb +0 -22
  206. data/app/presenters/decidim/meetings/admin_log/minutes_presenter.rb +0 -42
  207. data/app/views/decidim/meetings/admin/minutes/_form.html.erb +0 -23
  208. data/app/views/decidim/meetings/admin/minutes/edit.html.erb +0 -7
  209. data/app/views/decidim/meetings/admin/minutes/new.html.erb +0 -7
  210. data/app/views/decidim/meetings/meetings/_online_meeting_link.html.erb +0 -11
  211. data/config/locales/ja-JP.yml +0 -494
  212. data/lib/decidim/api/minutes_type.rb +0 -20
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ class Poll < Meetings::ApplicationRecord
6
+ has_one :questionnaire,
7
+ class_name: "Decidim::Meetings::Questionnaire",
8
+ dependent: :destroy,
9
+ inverse_of: :questionnaire_for,
10
+ as: :questionnaire_for
11
+
12
+ belongs_to :meeting, foreign_key: "decidim_meeting_id", class_name: "Decidim::Meetings::Meeting"
13
+
14
+ delegate :organization, to: :meeting
15
+
16
+ QUESTION_TYPES = %w(single_option multiple_option).freeze
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ # The data store for a Question in the Decidim::Meetings component.
6
+ class Question < Meetings::ApplicationRecord
7
+ include Decidim::TranslatableResource
8
+
9
+ QUESTION_TYPES = %w(single_option multiple_option).freeze
10
+
11
+ translatable_fields :body
12
+ enum status: { unpublished: 0, published: 1, closed: 2 }
13
+
14
+ belongs_to :questionnaire, class_name: "Decidim::Meetings::Questionnaire", foreign_key: "decidim_questionnaire_id"
15
+
16
+ has_many :answers,
17
+ class_name: "Decidim::Meetings::Answer",
18
+ foreign_key: "decidim_question_id",
19
+ dependent: :destroy,
20
+ inverse_of: :question
21
+
22
+ has_many :answer_options,
23
+ class_name: "Decidim::Meetings::AnswerOption",
24
+ foreign_key: "decidim_question_id",
25
+ dependent: :destroy,
26
+ inverse_of: :question
27
+
28
+ validates :question_type, inclusion: { in: QUESTION_TYPES }
29
+
30
+ scope :visible, -> { where(status: [:published, :closed]) }
31
+
32
+ def multiple_choice?
33
+ %w(single_option multiple_option).include?(question_type)
34
+ end
35
+
36
+ # Public: returns whether the questionnaire is answered by the user or not.
37
+ def answered_by?(user)
38
+ questionnaire.answers.where({ user: user, question: self }).any? if questionnaire.questions.present? && user.present?
39
+ end
40
+
41
+ def number_of_options
42
+ answer_options.size
43
+ end
44
+
45
+ def translated_body
46
+ Decidim::Forms::QuestionPresenter.new(self).translated_body
47
+ end
48
+
49
+ def answers_count
50
+ questionnaire.answers.where(question: self).count
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Meetings
5
+ # The data store for a Questionnaire in the Decidim::Meetings component.
6
+ class Questionnaire < Meetings::ApplicationRecord
7
+ belongs_to :questionnaire_for, polymorphic: true
8
+
9
+ has_many :questions, -> { order(:position) }, class_name: "Question", foreign_key: "decidim_questionnaire_id", dependent: :destroy
10
+ has_many :answers, class_name: "Answer", foreign_key: "decidim_questionnaire_id", dependent: :destroy
11
+
12
+ # Public: returns whether the questionnaire questions can be modified or not.
13
+ def questions_editable?
14
+ has_component = questionnaire_for.meeting.respond_to? :component
15
+ (has_component && !questionnaire_for.meeting.component.published?) || answers.empty?
16
+ end
17
+
18
+ def all_questions_unpublished?
19
+ questions.all?(&:unpublished?)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -16,6 +16,8 @@ module Decidim
16
16
 
17
17
  before_validation :generate_code, on: :create
18
18
 
19
+ scope :public_participant, -> { where(decidim_user_group_id: nil, public_participation: true) }
20
+
19
21
  def self.user_collection(user)
20
22
  where(decidim_user_id: user.id)
21
23
  end
@@ -0,0 +1,6 @@
1
+ import "src/decidim/meetings/meetings_form"
2
+ import "src/decidim/meetings/meetings_polls"
3
+ import "src/decidim/forms/forms"
4
+
5
+ // Images
6
+ require.context("../images", true)
@@ -0,0 +1,5 @@
1
+ import "src/decidim/meetings/admin/agendas"
2
+ import "src/decidim/meetings/admin/destroy_meeting_alert"
3
+ import "src/decidim/meetings/admin/meetings_form"
4
+ import "src/decidim/meetings/admin/registrations_form"
5
+ import "src/decidim/meetings/admin/registrations_invite_form"
@@ -0,0 +1,159 @@
1
+ import AutoButtonsByPositionComponent from "src/decidim/admin/auto_buttons_by_position.component"
2
+ import AutoLabelByPositionComponent from "src/decidim/admin/auto_label_by_position.component"
3
+ import createSortList from "src/decidim/admin/sort_list.component"
4
+ import createDynamicFields from "src/decidim/admin/dynamic_fields.component"
5
+ import createQuillEditor from "src/decidim/editor"
6
+
7
+ const wrapperSelector = ".meeting-agenda-items";
8
+ const fieldSelector = ".meeting-agenda-item";
9
+ const childsWrapperSelector = ".meeting-agenda-item-childs";
10
+ const childFieldSelector = ".meeting-agenda-item-child";
11
+
12
+ const autoLabelByPosition = new AutoLabelByPositionComponent({
13
+ listSelector: ".meeting-agenda-item:not(.hidden)",
14
+ labelSelector: ".card-title span:first",
15
+ onPositionComputed: (el, idx) => {
16
+ $(el).find("input[name$=\\[position\\]]").val(idx);
17
+ }
18
+ });
19
+
20
+ const autoButtonsByPosition = new AutoButtonsByPositionComponent({
21
+ listSelector: ".meeting-agenda-item:not(.hidden)",
22
+ hideOnFirstSelector: ".move-up-agenda-item",
23
+ hideOnLastSelector: ".move-down-agenda-item"
24
+ });
25
+
26
+ const createSortableList = () => {
27
+ createSortList(".meeting-agenda-items-list:not(.published)", {
28
+ handle: ".agenda-item-divider",
29
+ placeholder: '<div style="border-style: dashed; border-color: #000"></div>',
30
+ forcePlaceholderSize: true,
31
+ onSortUpdate: () => { autoLabelByPosition.run() }
32
+ });
33
+ };
34
+
35
+ const createSortableListChild = () => {
36
+ createSortList(".meeting-agenda-item-childs-list:not(.published)", {
37
+ handle: ".agenda-item-child-divider",
38
+ placeholder: '<div style="border-style: dashed; border-color: #000"></div>',
39
+ forcePlaceholderSize: true,
40
+ onSortUpdate: () => { autoLabelByPosition.run() }
41
+ });
42
+ };
43
+
44
+ const autoLabelByPositionChild = new AutoLabelByPositionComponent({
45
+ listSelector: ".meeting-agenda-item-child:not(.hidden)",
46
+ labelSelector: ".card-title span:first",
47
+ onPositionComputed: (el, idx) => {
48
+ $(el).find("input[name$=\\[position\\]]").val(idx);
49
+ }
50
+ });
51
+
52
+ const autoButtonsByPositionChild = new AutoButtonsByPositionComponent({
53
+ listSelector: ".meeting-agenda-item-child:not(.hidden)",
54
+ hideOnFirstSelector: ".move-up-agenda-item-child",
55
+ hideOnLastSelector: ".move-down-agenda-item-child"
56
+ });
57
+
58
+ const createDynamicFieldsForAgendaItemChilds = (fieldId) => {
59
+ return createDynamicFields({
60
+ placeholderId: "meeting-agenda-item-child-id",
61
+ wrapperSelector: `#${fieldId} ${childsWrapperSelector}`,
62
+ containerSelector: ".meeting-agenda-item-childs-list",
63
+ fieldSelector: childFieldSelector,
64
+ addFieldButtonSelector: ".add-agenda-item-child",
65
+ removeFieldButtonSelector: ".remove-agenda-item-child",
66
+ moveUpFieldButtonSelector: ".move-up-agenda-item-child",
67
+ moveDownFieldButtonSelector: ".move-down-agenda-item-child",
68
+
69
+ onAddField: ($field) => {
70
+ createSortableListChild();
71
+
72
+ $field.find(".editor-container").each((idx, el) => {
73
+ createQuillEditor(el);
74
+ });
75
+
76
+ autoLabelByPositionChild.run();
77
+ autoButtonsByPositionChild.run();
78
+ },
79
+ onRemoveField: () => {
80
+ autoLabelByPositionChild.run();
81
+ autoButtonsByPositionChild.run();
82
+ },
83
+ onMoveUpField: () => {
84
+ autoLabelByPositionChild.run();
85
+ autoButtonsByPositionChild.run();
86
+ },
87
+ onMoveDownField: () => {
88
+ autoLabelByPositionChild.run();
89
+ autoButtonsByPositionChild.run();
90
+ }
91
+ });
92
+ };
93
+
94
+ const dynamicFieldsForAgendaItemChilds = {};
95
+
96
+ const setupInitialAgendaItemChildAttributes = ($target) => {
97
+ const fieldId = $target.attr("id");
98
+
99
+ dynamicFieldsForAgendaItemChilds[fieldId] = createDynamicFieldsForAgendaItemChilds(fieldId);
100
+
101
+ }
102
+
103
+ const hideDeletedAgendaItem = ($target) => {
104
+ const inputDeleted = $target.find("input[name$=\\[deleted\\]]").val();
105
+
106
+ if (inputDeleted === "true") {
107
+ $target.addClass("hidden");
108
+ $target.hide();
109
+ }
110
+ };
111
+
112
+ createDynamicFields({
113
+ placeholderId: "meeting-agenda-item-id",
114
+ wrapperSelector: wrapperSelector,
115
+ containerSelector: ".meeting-agenda-items-list",
116
+ fieldSelector: fieldSelector,
117
+ addFieldButtonSelector: ".add-agenda-item",
118
+ removeFieldButtonSelector: ".remove-agenda-item",
119
+ moveUpFieldButtonSelector: ".move-up-agenda-item",
120
+ moveDownFieldButtonSelector: ".move-down-agenda-item",
121
+ onAddField: ($field) => {
122
+ // createDynamicFieldsForAgendaItemChilds($field);
123
+ setupInitialAgendaItemChildAttributes($field);
124
+ createSortableList();
125
+
126
+ $field.find(".editor-container").each((idx, el) => {
127
+ createQuillEditor(el);
128
+ });
129
+
130
+ autoLabelByPosition.run();
131
+ autoButtonsByPosition.run();
132
+ },
133
+ onRemoveField: () => {
134
+ autoLabelByPosition.run();
135
+ autoButtonsByPosition.run();
136
+ },
137
+ onMoveUpField: () => {
138
+ autoLabelByPosition.run();
139
+ autoButtonsByPosition.run();
140
+ },
141
+ onMoveDownField: () => {
142
+ autoLabelByPosition.run();
143
+ autoButtonsByPosition.run();
144
+ }
145
+ });
146
+
147
+ createSortableList();
148
+
149
+ $(fieldSelector).each((idx, el) => {
150
+ const $target = $(el);
151
+
152
+ hideDeletedAgendaItem($target);
153
+ setupInitialAgendaItemChildAttributes($target);
154
+ });
155
+
156
+ autoLabelByPosition.run();
157
+ autoButtonsByPosition.run();
158
+ autoLabelByPositionChild.run();
159
+ autoButtonsByPositionChild.run();
@@ -1,7 +1,10 @@
1
- ((exports) => {
2
- const { AutoLabelByPositionComponent, AutoButtonsByPositionComponent, createDynamicFields, createSortList } = exports.DecidimAdmin;
3
- const { attachGeocoding } = window.Decidim;
1
+ import AutoButtonsByPositionComponent from "src/decidim/admin/auto_buttons_by_position.component"
2
+ import AutoLabelByPositionComponent from "src/decidim/admin/auto_label_by_position.component"
3
+ import createSortList from "src/decidim/admin/sort_list.component"
4
+ import createDynamicFields from "src/decidim/admin/dynamic_fields.component"
5
+ import attachGeocoding from "src/decidim/geocoding/attach_input"
4
6
 
7
+ $(() => {
5
8
  const wrapperSelector = ".meeting-services";
6
9
  const fieldSelector = ".meeting-service";
7
10
 
@@ -144,4 +147,4 @@
144
147
  toggleDependsOnSelect($meetingTypeOfMeeting, $meetingOnlineFields, "online");
145
148
  toggleDependsOnSelect($meetingTypeOfMeeting, $meetingInPersonFields, "in_person");
146
149
  }
147
- })(window);
150
+ })
@@ -1,6 +1,6 @@
1
- ((exports) => {
2
- const { createFieldDependentInputs } = exports.DecidimAdmin;
1
+ import createFieldDependentInputs from "src/decidim/admin/field_dependent_inputs.component"
3
2
 
3
+ $(() => {
4
4
  const $attendeeType = $('[name="meeting_registration_invite[existing_user]"');
5
5
 
6
6
  createFieldDependentInputs({
@@ -22,4 +22,4 @@
22
22
  return $("#meeting_registration_invite_existing_user_true").is(":checked")
23
23
  }
24
24
  });
25
- })(window);
25
+ })
@@ -0,0 +1,54 @@
1
+ import attachGeocoding from "src/decidim/geocoding/attach_input"
2
+
3
+ $(() => {
4
+ // Adds the latitude/longitude inputs after the geocoding is done
5
+ const $meetingAddress = $("#meeting_address");
6
+ if ($meetingAddress.length > 0) {
7
+ attachGeocoding($meetingAddress);
8
+ }
9
+
10
+ const $form = $(".meetings_form");
11
+ if ($form.length > 0) {
12
+ const $meetingTypeOfMeeting = $form.find("#meeting_type_of_meeting");
13
+ const $meetingOnlineFields = $form.find(".field[data-meeting-type='online']");
14
+ const $meetingInPersonFields = $form.find(".field[data-meeting-type='in_person']");
15
+
16
+ const toggleDependsOnSelect = ($target, $showDiv, type) => {
17
+ const value = $target.val();
18
+ if (value === "hybrid") {
19
+ $showDiv.show();
20
+ } else {
21
+ $showDiv.hide();
22
+ if (value === type) {
23
+ $showDiv.show();
24
+ }
25
+ }
26
+ };
27
+
28
+ $meetingTypeOfMeeting.on("change", (ev) => {
29
+ const $target = $(ev.target);
30
+ toggleDependsOnSelect($target, $meetingOnlineFields, "online");
31
+ toggleDependsOnSelect($target, $meetingInPersonFields, "in_person");
32
+ });
33
+
34
+ toggleDependsOnSelect($meetingTypeOfMeeting, $meetingOnlineFields, "online");
35
+ toggleDependsOnSelect($meetingTypeOfMeeting, $meetingInPersonFields, "in_person");
36
+
37
+
38
+ const $meetingRegistrationType = $form.find("#meeting_registration_type");
39
+ const $meetingRegistrationTerms = $form.find("#meeting_registration_terms");
40
+ const $meetingRegistrationUrl = $form.find("#meeting_registration_url");
41
+ const $meetingAvailableSlots = $form.find("#meeting_available_slots");
42
+
43
+ $meetingRegistrationType.on("change", (ev) => {
44
+ const $target = $(ev.target);
45
+ toggleDependsOnSelect($target, $meetingAvailableSlots, "on_this_platform");
46
+ toggleDependsOnSelect($target, $meetingRegistrationTerms, "on_this_platform");
47
+ toggleDependsOnSelect($target, $meetingRegistrationUrl, "on_different_platform");
48
+ });
49
+
50
+ toggleDependsOnSelect($meetingRegistrationType, $meetingAvailableSlots, "on_this_platform");
51
+ toggleDependsOnSelect($meetingRegistrationType, $meetingRegistrationTerms, "on_this_platform");
52
+ toggleDependsOnSelect($meetingRegistrationType, $meetingRegistrationUrl, "on_different_platform");
53
+ }
54
+ });
@@ -0,0 +1,41 @@
1
+ import MeetingsPollComponent from "src/decidim/meetings/poll.component"
2
+ const OPEN_CLASS = "is-open";
3
+
4
+ $(() => {
5
+ // Mount polls component for users
6
+ const $container = $("[data-decidim-meetings-poll]");
7
+ const $counter = $("#visible-questions-count");
8
+ const poll = new MeetingsPollComponent($container, $container.data("decidim-meetings-poll"), $counter);
9
+
10
+ $(".meeting-polls__action-list").on("click", (event) => {
11
+ event.preventDefault();
12
+
13
+ if (poll.isMounted()) {
14
+ $(event.target).removeClass(OPEN_CLASS);
15
+ $container.removeClass(OPEN_CLASS);
16
+ poll.unmountComponent();
17
+ } else {
18
+ $(event.target).addClass(OPEN_CLASS);
19
+ $container.addClass(OPEN_CLASS);
20
+ poll.mountComponent();
21
+ }
22
+ });
23
+
24
+ // Mount polls component for admins
25
+ const $adminContainer = $("[data-decidim-admin-meetings-poll]");
26
+ const adminPoll = new MeetingsPollComponent($adminContainer, $adminContainer.data("decidim-admin-meetings-poll"));
27
+
28
+ $(".meeting-polls__action-administrate").on("click", (event) => {
29
+ event.preventDefault();
30
+
31
+ if (adminPoll.isMounted()) {
32
+ $(event.target).removeClass(OPEN_CLASS);
33
+ $adminContainer.removeClass(OPEN_CLASS);
34
+ adminPoll.unmountComponent();
35
+ } else {
36
+ $(event.target).addClass(OPEN_CLASS);
37
+ $adminContainer.addClass(OPEN_CLASS);
38
+ adminPoll.mountComponent();
39
+ }
40
+ });
41
+ });
@@ -0,0 +1,166 @@
1
+ /* eslint id-length: ["error", { "exceptions": ["$"] }] */
2
+
3
+ /**
4
+ * A plain Javascript component that handles questions from polls in meetings:
5
+ * - fetches them via Ajax
6
+ * - enables a polling to automatically update them
7
+ *
8
+ * @class
9
+ * @augments Component
10
+ */
11
+
12
+ // This is necessary for testing purposes
13
+ const $ = window.$;
14
+
15
+ // Default question states
16
+ const OPEN = "open";
17
+ const CLOSED = "closed";
18
+
19
+ export default class PollComponent {
20
+ constructor($element, config, $counter = null) {
21
+ this.$element = $element;
22
+ this.$counter = $counter;
23
+ this.questionsUrl = config.questionsUrl;
24
+ this.pollingInterval = config.pollingInterval || 5000;
25
+ this.mounted = false;
26
+ this.questions = {};
27
+ }
28
+
29
+ /**
30
+ * Returns if the component is mounted or not
31
+ * @public
32
+ * @returns {Void} - Returns nothing
33
+ */
34
+ isMounted() {
35
+ return this.mounted;
36
+ }
37
+
38
+ /**
39
+ * Handles the logic for mounting the component
40
+ * @public
41
+ * @returns {Void} - Returns nothing
42
+ */
43
+ mountComponent() {
44
+ if (this.$element.length > 0 && !this.mounted) {
45
+ this.mounted = true;
46
+ this._fetchQuestions();
47
+ }
48
+ }
49
+
50
+ unmountComponent() {
51
+ if (this.mounted) {
52
+ this.mounted = false;
53
+ this._stopPolling();
54
+ this.$element.html("");
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Performs the ajax call that updates the list of questions
60
+ * Before, stores the current questions states to apply them after the ajax call has
61
+ * been completed
62
+ * @private
63
+ * @returns {Void} - Returns nothing
64
+ */
65
+ _fetchQuestions() {
66
+ // Store current questions state (open / closed) before overwritting them with the Ajax call
67
+ // response.
68
+ this._storeQuestionState(this.$element);
69
+
70
+ $.ajax({
71
+ url: this.questionsUrl,
72
+ method: "GET",
73
+ contentType: "application/javascript"
74
+ }).done(() => {
75
+ this._updateCounter();
76
+ this._setQuestionsState(this.$element);
77
+ this._pollQuestions();
78
+ });
79
+ }
80
+
81
+ /**
82
+ * Iterates over all existing questions and stores the state in an internal attribute.
83
+ * @private
84
+ * @param {jQuery} $parent - The HTML content for the questionnaire.
85
+ * @returns {Void} - Returns nothing
86
+ */
87
+ _storeQuestionState($parent) {
88
+ $("[data-question]", $parent).each((_i, el) => {
89
+ const $el = $(el);
90
+ const questionId = $el.data("question");
91
+ if ($el[0].open === true) {
92
+ this.questions[questionId] = OPEN;
93
+ } else {
94
+ this.questions[questionId] = CLOSED;
95
+ }
96
+ });
97
+ }
98
+
99
+ /**
100
+ * Initializes the states of all the questions.
101
+ * @private
102
+ * @param {jQuery} $parent - The HTML container for the questionnaire.
103
+ * @returns {Void} - Returns nothing
104
+ */
105
+ _setQuestionsState($parent) {
106
+ $("[data-question]", $parent).each((_i, el) => {
107
+ this._setQuestionState($(el));
108
+ });
109
+ }
110
+
111
+ /**
112
+ * Initializes the state of a single question with two types of modifications:
113
+ * - sets the is-new class if the question is new (doesn't exist in the internal list)
114
+ * - sets the state to open if it was open in the internal list
115
+ * @private
116
+ * @param {jQuery} $el - The HTML container for the questionnaire.
117
+ * @returns {Void} - Returns nothing
118
+ */
119
+ _setQuestionState($el) {
120
+ const questionId = $el.data("question");
121
+ // Current question state
122
+ const state = this.questions[questionId];
123
+ // New questions have a special class
124
+ if (!state) {
125
+ $el.addClass("is-new");
126
+ } else if (state === OPEN) {
127
+ $el.prop(OPEN, true);
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Sets a timeout to poll new questions.
133
+ * @private
134
+ * @returns {Void} - Returns nothing
135
+ */
136
+ _pollQuestions() {
137
+ this._stopPolling();
138
+
139
+ this.pollTimeout = setTimeout(() => {
140
+ this._fetchQuestions();
141
+ }, this.pollingInterval);
142
+ }
143
+
144
+ /**
145
+ * Stops polling for new questions.
146
+ * @private
147
+ * @returns {Void} - Returns nothing
148
+ */
149
+ _stopPolling() {
150
+ if (this.pollTimeout) {
151
+ clearTimeout(this.pollTimeout);
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Updates the counter with the number of questions returned in the Ajax call
157
+ * @private
158
+ * @returns {Void} - Returns nothing
159
+ */
160
+ _updateCounter() {
161
+ if (this.$counter) {
162
+ const questionsCount = this.$element.find("details").length;
163
+ this.$counter.html(`(${questionsCount})`);
164
+ }
165
+ }
166
+ }