Adapt 'show' view for open questions without options

- Prevent creating options for open questions
- Skip rendering the options table when none exist
This commit is contained in:
taitus
2025-08-06 11:54:30 +02:00
parent d3f32978c8
commit b3f8ba819b
6 changed files with 75 additions and 54 deletions

View File

@@ -101,7 +101,7 @@ module Abilities
end end
can [:read, :order_options], Poll::Question::Option can [:read, :order_options], Poll::Question::Option
can [:create, :update, :destroy], Poll::Question::Option do |option| can [:create, :update, :destroy], Poll::Question::Option do |option|
can?(:update, option.question) can?(:update, option.question) && option.question.accepts_options?
end end
can :read, Poll::Question::Option::Video can :read, Poll::Question::Option::Video
can [:create, :update, :destroy], Poll::Question::Option::Video do |video| can [:create, :update, :destroy], Poll::Question::Option::Video do |video|

View File

@@ -61,6 +61,10 @@ class Poll::Question < ApplicationRecord
votation_type.nil? || votation_type.unique? votation_type.nil? || votation_type.unique?
end end
def accepts_options?
votation_type.nil? || votation_type.accepts_options?
end
def max_votes def max_votes
if multiple? if multiple?
votation_type.max_votes votation_type.max_votes

View File

@@ -9,6 +9,10 @@ class VotationType < ApplicationRecord
validates :questionable_type, inclusion: { in: ->(*) { QUESTIONABLE_TYPES }} validates :questionable_type, inclusion: { in: ->(*) { QUESTIONABLE_TYPES }}
validates :max_votes, presence: true, if: :max_votes_required? validates :max_votes, presence: true, if: :max_votes_required?
def accepts_options?
!open?
end
private private
def max_votes_required? def max_votes_required?

View File

@@ -46,57 +46,59 @@
</div> </div>
</div> </div>
<div class="clear"> <% if @question.accepts_options? %>
<% if can?(:create, Poll::Question::Option.new(question: @question)) %> <div class="clear">
<%= link_to t("admin.questions.show.add_answer"), new_admin_question_option_path(@question), <% if can?(:create, Poll::Question::Option.new(question: @question)) %>
class: "button float-right" %> <%= link_to t("admin.questions.show.add_answer"), new_admin_question_option_path(@question),
<% else %> class: "button float-right" %>
<div class="callout warning"> <% else %>
<strong><%= t("admin.questions.no_edit") %></strong> <div class="callout warning">
</div> <strong><%= t("admin.questions.no_edit") %></strong>
<% end %> </div>
</div>
<table class="margin-top">
<caption><%= t("admin.questions.show.valid_answers") %></caption>
<thead>
<tr>
<th><%= t("admin.questions.show.answers.title") %></th>
<th scope="col" class="medium-7"><%= t("admin.questions.show.answers.description") %></th>
<th scope="col" class="text-center"><%= t("admin.questions.show.answers.images") %></th>
<th scope="col" class="text-center"><%= t("admin.questions.show.answers.documents") %></th>
<th scope="col" class="text-center"><%= t("admin.questions.show.answers.videos") %></th>
<th><%= t("admin.actions.actions") %></th>
</tr>
</thead>
<tbody class="sortable" data-js-url="<%= admin_question_options_order_options_path(@question.id) %>">
<% @question.question_options.each do |option| %>
<tr id="<%= dom_id(option) %>" class="poll_question_option" data-option-id="<%= option.id %>">
<td class="align-top"><%= option.title %></td>
<td class="align-top break"><%= wysiwyg(option.description) %></td>
<td class="align-top text-center">
(<%= option.images.count %>)
<br>
<%= link_to t("admin.questions.show.answers.images_list"),
admin_option_images_path(option) %>
</td>
<td class="align-top text-center">
(<%= option.documents.count rescue 0 %>)
<br>
<%= link_to t("admin.questions.show.answers.documents_list"),
admin_option_documents_path(option) %>
</td>
<td class="align-top text-center">
(<%= option.videos.count %>)
<br>
<%= link_to t("admin.questions.show.answers.video_list"),
admin_option_videos_path(option) %>
</td>
<td>
<%= render Admin::Poll::Questions::Options::TableActionsComponent.new(option) %>
</td>
</tr>
<% end %> <% end %>
</tbody> </div>
</table>
<table class="margin-top">
<caption><%= t("admin.questions.show.valid_answers") %></caption>
<thead>
<tr>
<th><%= t("admin.questions.show.answers.title") %></th>
<th scope="col" class="medium-7"><%= t("admin.questions.show.answers.description") %></th>
<th scope="col" class="text-center"><%= t("admin.questions.show.answers.images") %></th>
<th scope="col" class="text-center"><%= t("admin.questions.show.answers.documents") %></th>
<th scope="col" class="text-center"><%= t("admin.questions.show.answers.videos") %></th>
<th><%= t("admin.actions.actions") %></th>
</tr>
</thead>
<tbody class="sortable" data-js-url="<%= admin_question_options_order_options_path(@question.id) %>">
<% @question.question_options.each do |option| %>
<tr id="<%= dom_id(option) %>" class="poll_question_option" data-option-id="<%= option.id %>">
<td class="align-top"><%= option.title %></td>
<td class="align-top break"><%= wysiwyg(option.description) %></td>
<td class="align-top text-center">
(<%= option.images.count %>)
<br>
<%= link_to t("admin.questions.show.answers.images_list"),
admin_option_images_path(option) %>
</td>
<td class="align-top text-center">
(<%= option.documents.count rescue 0 %>)
<br>
<%= link_to t("admin.questions.show.answers.documents_list"),
admin_option_documents_path(option) %>
</td>
<td class="align-top text-center">
(<%= option.videos.count %>)
<br>
<%= link_to t("admin.questions.show.answers.video_list"),
admin_option_videos_path(option) %>
</td>
<td>
<%= render Admin::Poll::Questions::Options::TableActionsComponent.new(option) %>
</td>
</tr>
<% end %>
</tbody>
</table>
<% end %>

View File

@@ -20,6 +20,8 @@ describe Abilities::Administrator do
let(:future_poll) { create(:poll, :future) } let(:future_poll) { create(:poll, :future) }
let(:current_poll_question) { create(:poll_question) } let(:current_poll_question) { create(:poll_question) }
let(:future_poll_question) { create(:poll_question, poll: future_poll) } let(:future_poll_question) { create(:poll_question, poll: future_poll) }
let(:future_poll_question_open) { create(:poll_question_open, poll: future_poll) }
let(:future_poll_question_option_open) { future_poll_question_open.question_options.new }
let(:current_poll_question_option) { create(:poll_question_option) } let(:current_poll_question_option) { create(:poll_question_option) }
let(:future_poll_question_option) { create(:poll_question_option, poll: future_poll) } let(:future_poll_question_option) { create(:poll_question_option, poll: future_poll) }
let(:current_poll_option_video) { create(:poll_option_video, option: current_poll_question_option) } let(:current_poll_option_video) { create(:poll_option_video, option: current_poll_question_option) }
@@ -143,6 +145,9 @@ describe Abilities::Administrator do
it { should_not be_able_to(:create, current_poll_question_option) } it { should_not be_able_to(:create, current_poll_question_option) }
it { should_not be_able_to(:update, current_poll_question_option) } it { should_not be_able_to(:update, current_poll_question_option) }
it { should_not be_able_to(:destroy, current_poll_question_option) } it { should_not be_able_to(:destroy, current_poll_question_option) }
it { should_not be_able_to(:create, future_poll_question_option_open) }
it { should_not be_able_to(:update, future_poll_question_option_open) }
it { should_not be_able_to(:destroy, future_poll_question_option_open) }
it { should be_able_to(:create, future_poll_option_video) } it { should be_able_to(:create, future_poll_option_video) }
it { should be_able_to(:update, future_poll_option_video) } it { should be_able_to(:update, future_poll_option_video) }

View File

@@ -89,6 +89,8 @@ describe "Admin poll questions", :admin do
expect(page).to have_content "Question with unique answer" expect(page).to have_content "Question with unique answer"
expect(page).to have_content "Unique answer" expect(page).to have_content "Unique answer"
expect(page).not_to have_content "Maximum number of votes" expect(page).not_to have_content "Maximum number of votes"
expect(page).to have_link "Add answer"
expect(page).to have_table "Valid answers"
end end
scenario "Multiple" do scenario "Multiple" do
@@ -106,6 +108,8 @@ describe "Admin poll questions", :admin do
expect(page).to have_content "Question with multiple answers" expect(page).to have_content "Question with multiple answers"
expect(page).to have_content "Multiple answers" expect(page).to have_content "Multiple answers"
expect(page).to have_text "Maximum number of votes 6", normalize_ws: true expect(page).to have_text "Maximum number of votes 6", normalize_ws: true
expect(page).to have_link "Add answer"
expect(page).to have_table "Valid answers"
end end
scenario "Open-ended" do scenario "Open-ended" do
@@ -122,6 +126,8 @@ describe "Admin poll questions", :admin do
expect(page).to have_content "What do you want?" expect(page).to have_content "What do you want?"
expect(page).to have_content "Open-ended" expect(page).to have_content "Open-ended"
expect(page).not_to have_content "Maximum number of votes" expect(page).not_to have_content "Maximum number of votes"
expect(page).not_to have_link "Add answer"
expect(page).not_to have_table "Valid answers"
end end
end end
end end