effective_polls 0.7.2 → 0.8.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e6a8bdf3c390cdfb86549465e0b02dca81c9036e6dc62535c0b520be3bf56c91
4
- data.tar.gz: 19eef49b77145f3efca025591e2a790efcf0c88a1e98d0599edd4a874caebaf0
3
+ metadata.gz: 1893c50235d32c084e4edda3e02512dc4d3dda12fe78c0950912bd445b290d95
4
+ data.tar.gz: 68a4ec6af9aba679d144bceb777fbecc4669216369ad5f1bb25d1f4d9e6284af
5
5
  SHA512:
6
- metadata.gz: 7a20600e039346a802e89b17bf24457793d2efdc8d2848dfaec78a59be8be5366261027f4dc6c3c78fc178652acaf06644f7521c1398e07750e4d17bbe2ac66b
7
- data.tar.gz: 17d0bc1e39ca2ff8221f1eecc02ee5c897d2fd930d539cbc728a4112d699f8c959877edf118a0920b5472fcff8744bc23cb62ce0a232bcc2df2a2f4e1f1ab608
6
+ metadata.gz: 542819abd880bf6f6be5644a6d87339e2aef874883017bb354d19a23bcc7b12b89c24c9af9fc60fd0d8d66a2bd1ef4843cbb6e84adce314d4cd72f0fc610324f
7
+ data.tar.gz: aecfe705c0a2000edfa42932a9fc8a415086e6c9d95c6ebbf12080958808cf501fc40deb2db477251ee44c4c5d0da0935b1dc003ef231bac586fe20d96830c3d
@@ -56,7 +56,7 @@ module Effective
56
56
  when :vote
57
57
  params.require(:effective_ballot).permit(:current_step, ballot_responses_attributes: [
58
58
  :id, :poll_id, :poll_question_id,
59
- :date, :email, :number, :long_answer, :short_answer, :upload_file,
59
+ :date, :email, :number, :long_answer, :short_answer, :upload_file, :_destroy,
60
60
  :poll_question_option_ids, poll_question_option_ids: []
61
61
  ])
62
62
  when :submit
@@ -6,23 +6,40 @@ class Admin::EffectivePollQuestionsDatatable < Effective::Datatable
6
6
  col :created_at, visible: false
7
7
  col :id, visible: false
8
8
 
9
- col :poll
9
+ if attributes[:follow_up]
10
+ col :show_if_value_to_s, label: 'When answered with'
11
+ else
12
+ col :poll
13
+ end
10
14
 
11
- col :position do |poll_question|
15
+ col :position, visible: false do |poll_question|
12
16
  poll_question.position.to_i + 1
13
17
  end
14
18
 
15
19
  col :title
16
- col :body, as: :text
20
+ col :body, as: :text, visible: !attributes[:follow_up]
17
21
  col :required
18
22
 
19
23
  col :category, label: 'Type'
20
24
  col :poll_question_options, label: 'Options'
21
25
 
26
+ unless attributes[:follow_up]
27
+ col :follow_up_poll_questions, action: false, label: 'Follow up questions'
28
+ end
29
+
22
30
  actions_col
23
31
  end
24
32
 
25
33
  collection do
26
- Effective::PollQuestion.all.deep
34
+ scope = Effective::PollQuestion.all.deep
35
+
36
+ if attributes[:follow_up]
37
+ scope = scope.where(follow_up: true, poll_question_id: attributes[:poll_question_id])
38
+ else
39
+ scope = scope.where(poll_question_id: nil)
40
+ end
41
+
42
+ scope
27
43
  end
44
+
28
45
  end
@@ -5,7 +5,7 @@ class Admin::EffectivePollResultsDatatable < Effective::Datatable
5
5
  col :position, visible: false
6
6
  col :category, search: Effective::PollQuestion::CATEGORIES, visible: false
7
7
 
8
- col :question, search: poll.poll_questions.pluck(:title)
8
+ col :question, search: poll.poll_questions.pluck(:title).sort
9
9
  col :responses
10
10
  end
11
11
 
@@ -55,6 +55,16 @@ module EffectivePollsUser
55
55
  end
56
56
  end
57
57
 
58
+ if self.class.try(:effective_mentorships_user?)
59
+ Effective::MentorshipCycle.sorted.each do |cycle|
60
+ scopes += [
61
+ ["All grouped participants in #{cycle}", "mentorships_with_groups_id_#{cycle.id}"],
62
+ ["All grouped mentors in #{cycle}", "mentorships_mentors_with_groups_id_#{cycle.id}"],
63
+ ["All grouped mentees in #{cycle}", "mentorships_mentees_with_groups_id_#{cycle.id}"],
64
+ ]
65
+ end
66
+ end
67
+
58
68
  scopes
59
69
  end
60
70
 
@@ -72,6 +82,12 @@ module EffectivePollsUser
72
82
  return collection.with_committee(Effective::Committee.find_by_id(id))
73
83
  when :with_role
74
84
  return collection.with_role(id)
85
+ when :mentorships_with_groups
86
+ return collection.mentorships_with_groups(Effective::MentorshipCycle.find_by_id(id))
87
+ when :mentorships_mentors_with_groups
88
+ return collection.mentorships_mentors_with_groups(Effective::MentorshipCycle.find_by_id(id))
89
+ when :mentorships_mentees_with_groups
90
+ return collection.mentorships_mentees_with_groups(Effective::MentorshipCycle.find_by_id(id))
75
91
  end
76
92
 
77
93
  # Otherwise we don't know what this scope is
@@ -10,7 +10,7 @@ module Effective
10
10
  belongs_to :poll
11
11
 
12
12
  has_many :ballot_responses, dependent: :destroy
13
- accepts_nested_attributes_for :ballot_responses
13
+ accepts_nested_attributes_for :ballot_responses, allow_destroy: true
14
14
 
15
15
  acts_as_tokened
16
16
 
@@ -2,9 +2,14 @@ module Effective
2
2
  class PollQuestion < ActiveRecord::Base
3
3
  belongs_to :poll
4
4
 
5
+ belongs_to :poll_question, optional: true # Present when I'm a follow up question
6
+ belongs_to :poll_question_option, optional: true # Might be present when I'm a follow up question
7
+
5
8
  has_many :poll_question_options, -> { order(:position) }, inverse_of: :poll_question, dependent: :delete_all
6
9
  accepts_nested_attributes_for :poll_question_options, reject_if: :all_blank, allow_destroy: true
7
10
 
11
+ has_many :follow_up_poll_questions, -> { order(:position) }, class_name: 'Effective::PollQuestion', foreign_key: :poll_question_id, dependent: :destroy
12
+
8
13
  has_rich_text :body
9
14
  log_changes(to: :poll) if respond_to?(:log_changes)
10
15
 
@@ -34,6 +39,8 @@ module Effective
34
39
  'Select up to 5'
35
40
  ]
36
41
 
42
+ UNSUPPORTED_FOLLOW_UP_QUESTION_CATEGORIES = ['Upload File']
43
+
37
44
  effective_resource do
38
45
  title :string
39
46
  category :string
@@ -41,21 +48,37 @@ module Effective
41
48
 
42
49
  position :integer
43
50
 
51
+ follow_up :boolean
52
+ follow_up_value :string
53
+
44
54
  timestamps
45
55
  end
46
56
 
47
- before_validation(if: -> { poll.present? }) do
48
- self.position ||= (poll.poll_questions.map { |obj| obj.position }.compact.max || -1) + 1
57
+ before_validation(if: -> { poll_question.present? }) do
58
+ assign_attributes(poll: poll_question.poll)
59
+ end
60
+
61
+ # Set position
62
+ before_validation do
63
+ source = poll_question_option.try(:follow_up_poll_questions) || poll.try(:poll_questions) || []
64
+ self.position = (source.map { |obj| obj.position }.compact.max || -1) + 1
49
65
  end
50
66
 
51
67
  scope :deep, -> { with_rich_text_body_and_embeds.includes(:poll_question_options) }
52
68
  scope :sorted, -> { order(:position) }
53
69
 
70
+ scope :top_level, -> { where(follow_up: false) }
71
+ scope :follow_up, -> { where(follow_up: true) }
72
+
54
73
  validates :title, presence: true
55
74
  validates :category, presence: true, inclusion: { in: CATEGORIES }
56
75
  validates :position, presence: true
57
76
  validates :poll_question_options, presence: true, if: -> { poll_question_option? }
58
77
 
78
+ validates :poll_question, presence: true, if: -> { follow_up? }
79
+ validates :poll_question_option, presence: true, if: -> { follow_up? && poll_question.try(:poll_question_option?) }
80
+ validates :follow_up_value, presence: true, if: -> { follow_up? && !poll_question.try(:poll_question_option?) }
81
+
59
82
  # Create choose_one? and select_all_that_apply? methods for each category
60
83
  CATEGORIES.each do |category|
61
84
  define_method(category.parameterize.underscore + '?') { self.category == category }
@@ -65,6 +88,28 @@ module Effective
65
88
  title.presence || model_name.human
66
89
  end
67
90
 
91
+ def show_if_attribute
92
+ return :poll_question_option_ids if poll_question_option?
93
+
94
+ case category
95
+ when 'Date' then :date
96
+ when 'Email' then :email
97
+ when 'Number' then :number
98
+ when 'Long Answer' then :long_answer
99
+ when 'Short Answer' then :short_answer
100
+ when 'Upload File' then :upload_file
101
+ else :unknown
102
+ end
103
+ end
104
+
105
+ def show_if_value
106
+ poll_question.try(:poll_question_option?) ? poll_question_option_id : follow_up_value
107
+ end
108
+
109
+ def show_if_value_to_s
110
+ (poll_question.try(:poll_question_option?) ? poll_question_option : follow_up_value).to_s
111
+ end
112
+
68
113
  def poll_question_option?
69
114
  WITH_OPTIONS_CATEGORIES.include?(category)
70
115
  end
@@ -4,6 +4,21 @@
4
4
  - else
5
5
  = f.select :poll_id, Effective::Poll.all
6
6
 
7
+ -# Follow up
8
+ - if inline_datatable? && (original = f.object.poll_question).present?
9
+ = f.hidden_field :follow_up, value: true
10
+ = f.hidden_field :poll_question_id
11
+
12
+ .mb-3.card
13
+ .card-body
14
+ %h5 Follow up Question
15
+ %p.text-muted This question will only appear when they answer the above question with the following value:
16
+
17
+ - if original.poll_question_option?
18
+ = f.select :poll_question_option_id, original.poll_question_options, label: 'Answer required to display this question'
19
+ - else
20
+ = f.text_field :follow_up_value, label: 'Answer required to display this question', required: true, hint: 'Enter the exact answer value that should trigger this question to appear'
21
+
7
22
  = f.text_field :title, label: 'Question Title'
8
23
 
9
24
  - if defined?(EffectiveArticleEditor)
@@ -17,11 +32,8 @@
17
32
  = f.show_if :category, 'Choose one' do
18
33
  .mt-3.card
19
34
  .card-body
20
- %h5 Options
21
- %p Display the following options:
22
-
23
- = f.has_many :poll_question_options, build: true do |fa|
24
- = fa.text_field :title, label: false
35
+ %h5 Choose one
36
+ %p Display radio buttons to select one option
25
37
 
26
38
  = f.show_if :category, 'Select all that apply' do
27
39
  .card
@@ -105,3 +117,16 @@
105
117
  = fa.text_field :title, label: false
106
118
 
107
119
  = effective_submit(f)
120
+
121
+ - unless poll_question.follow_up?
122
+ %h2 Follow up questions
123
+
124
+ - if Effective::PollQuestion::UNSUPPORTED_FOLLOW_UP_QUESTION_CATEGORIES.include?(poll_question.category)
125
+ %p.text-muted
126
+ %em Follow up questions are not supported for the #{poll_question.category} question category
127
+ - elsif poll_question.new_record?
128
+ %p.text-muted
129
+ %em Please save this question to add follow up questions
130
+ - else
131
+ %p Display follow up question(s) based on the answer to this question:
132
+ = render_inline_datatable Admin::EffectivePollQuestionsDatatable.new(follow_up: true, poll_question: poll_question)
@@ -1 +1,2 @@
1
- = f.file_field :upload_file, label: false, required: poll_question.required?
1
+ - # Always required false here because we'll handle the logic in the ballot_response model
2
+ = f.file_field :upload_file, label: false, required: false
@@ -6,9 +6,17 @@
6
6
  %th Response
7
7
 
8
8
  %tbody
9
- - ballot.poll.poll_questions.each_with_index do |poll_question, index|
9
+ - ballot.poll.poll_questions.top_level.deep.each do |poll_question|
10
10
  - ballot_response = ballot.ballot_response(poll_question)
11
11
 
12
12
  %tr
13
13
  %td= poll_question
14
14
  %td= render(ballot_response)
15
+
16
+ - poll_question.follow_up_poll_questions.deep.each do |follow_up_poll_question|
17
+ - ballot_response = ballot.ballot_response(follow_up_poll_question)
18
+
19
+ - if ballot_response.persisted?
20
+ %tr
21
+ %td= follow_up_poll_question
22
+ %td= render(ballot_response)
@@ -5,10 +5,21 @@
5
5
  = effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
6
6
  = f.hidden_field :current_step
7
7
 
8
- - resource.poll.poll_questions.deep.all.each_with_index do |poll_question, index|
8
+ - resource.poll.poll_questions.top_level.deep.each do |poll_question|
9
9
  - ballot_response = resource.ballot_response(poll_question)
10
10
 
11
11
  = f.fields_for :ballot_responses, ballot_response do |fbr|
12
12
  = render('/effective/ballot_responses/fields', f: fbr, poll_question: poll_question)
13
13
 
14
+ - poll_question.follow_up_poll_questions.each do |follow_up|
15
+ - ballot_response = resource.ballot_response(follow_up)
16
+
17
+ = fbr.show_if(poll_question.show_if_attribute, follow_up.show_if_value) do
18
+ = f.fields_for :ballot_responses, ballot_response do |fbr|
19
+ = render('/effective/ballot_responses/fields', f: fbr, poll_question: follow_up)
20
+
21
+ = fbr.hide_if(poll_question.show_if_attribute, follow_up.show_if_value) do
22
+ = f.fields_for :ballot_responses, ballot_response do |fbr|
23
+ = fbr.hidden_field :_destroy, value: true
24
+
14
25
  = f.submit 'Save and Continue', center: true
@@ -33,13 +33,25 @@
33
33
  %tbody
34
34
  - ballots = poll.ballots
35
35
 
36
- - poll.poll_questions.each_with_index do |poll_question, index|
36
+ - poll.poll_questions.top_level.each_with_index do |poll_question, index|
37
37
  - ballot_responses = poll.poll_results(poll_question: poll_question)
38
38
 
39
39
  %tr
40
40
  %td
41
- #{poll_question.position + 1}. #{poll_question}
41
+ #{index + 1}. #{poll_question}
42
42
  %br
43
43
  %small.text-muted= poll_question.category
44
44
 
45
45
  %td= render('effective/poll_results/poll_result', poll_question: poll_question, ballot_responses: ballot_responses)
46
+
47
+ - poll_question.follow_up_poll_questions.each_with_index do |follow_up_poll_question, index|
48
+ - ballot_responses = poll.poll_results(poll_question: follow_up_poll_question)
49
+
50
+ %tr
51
+ %td
52
+ .ml-4
53
+ #{('a'.ord + index).chr}. #{follow_up_poll_question}
54
+ %br
55
+ %small.text-muted= follow_up_poll_question.category
56
+
57
+ %td= render('effective/poll_results/poll_result', poll_question: follow_up_poll_question, ballot_responses: ballot_responses)
@@ -49,6 +49,12 @@ class CreateEffectivePolls < ActiveRecord::Migration[6.0]
49
49
 
50
50
  t.integer :position
51
51
 
52
+ t.boolean :follow_up, default: false
53
+ t.string :follow_up_value
54
+
55
+ t.integer :poll_question_id
56
+ t.integer :poll_question_option_id
57
+
52
58
  t.datetime :updated_at
53
59
  t.datetime :created_at
54
60
  end
@@ -1,3 +1,3 @@
1
1
  module EffectivePolls
2
- VERSION = '0.7.2'
2
+ VERSION = '0.8.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_polls
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.2
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-23 00:00:00.000000000 Z
11
+ date: 2025-04-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails