diff --git a/app/helpers/containers_helper.rb b/app/helpers/containers_helper.rb index 73e33691..730f6731 100644 --- a/app/helpers/containers_helper.rb +++ b/app/helpers/containers_helper.rb @@ -15,17 +15,25 @@ def contest_description_action_links(container, description) end def render_eligibility_rules(description) - eligibility_plain = description.eligibility_rules.to_plain_text - if eligibility_plain.length > 60 - content_tag(:div) do - truncate(eligibility_plain, length: 60, omission: '') + - link_to(' ...more', '#', - data: { - action: 'click->modal#open', - url: eligibility_rules_container_contest_description_path(description.container, description), - modal_title: 'Eligibility Rules' - } - ) + eligibility_plain = description.eligibility_rules.to_plain_text.to_s.squish + preview_words = eligibility_plain.split + + if preview_words.length > 8 + preview_text = preview_words.first(8).join(' ') + + content_tag(:div, class: 'd-inline-flex align-items-start gap-1 border rounded px-1 py-0 bg-light small') do + safe_join([ + content_tag(:span, 'Rules:', class: 'fw-semibold text-muted'), + content_tag(:span, preview_text, class: 'text-muted'), + link_to('...more', '#', + data: { + action: 'click->modal#open', + url: eligibility_rules_container_contest_description_path(description.container, description), + modal_title: 'Eligibility Rules' + }, + aria: { label: 'View full eligibility rules' } + ) + ]) end else description.eligibility_rules diff --git a/app/helpers/contest_descriptions_helper.rb b/app/helpers/contest_descriptions_helper.rb index 5e53842d..bbf49b1c 100644 --- a/app/helpers/contest_descriptions_helper.rb +++ b/app/helpers/contest_descriptions_helper.rb @@ -1,13 +1,21 @@ module ContestDescriptionsHelper + def contest_description_entries_link(description) + active_instances = description.contest_instances.where(active: true) + return nil if active_instances.empty? + + first_active = active_instances.first + entry_count = first_active.entries.where(deleted: false).count + link_to("Active: #{pluralize(entry_count, 'entry')}", + container_contest_description_contest_instance_path(description.container, description, first_active), + class: 'btn btn-sm btn-primary small') + end + def contest_description_summary(description) total_instances = description.contest_instances.count active_instances = description.contest_instances.where(active: true) summary = '' if active_instances.any? - first_active = active_instances.first - entry_count = first_active.entries.where(deleted: false).count - summary += link_to("Active: #{pluralize(entry_count, 'entry')}", - container_contest_description_contest_instance_path(description.container, description, first_active), class: 'btn btn-sm btn-primary small') + summary += contest_description_entries_link(description).to_s summary += '
' end summary += "#{pluralize(total_instances, 'instance')}" diff --git a/app/views/containers/_contest_descriptions_table.html.erb b/app/views/containers/_contest_descriptions_table.html.erb index 2c8a6d84..8bc1f7ef 100644 --- a/app/views/containers/_contest_descriptions_table.html.erb +++ b/app/views/containers/_contest_descriptions_table.html.erb @@ -1,63 +1,53 @@ -<%# app/views/containers/_contest_descriptions_table.html.erb %> -
- - - - - - - - - - - - - - <% descriptions.each do |description| %> - - - - - - - - - <% end %> - -
NameStatusShort NameEligibilityNotesActiveActions
- - <%= description.name %> - - - <%= contest_description_summary(description) %> - - - <%= description.short_name %> - - - - <%# - <% if description.notes.present? %> - <%= link_to "view", container_contest_description_contest_instances_path(container, description), class: "btn btn-link btn-sm p-0", title: "View notes", aria: { label: "View notes for #{description.name}" } %> +<%# app/views/containers/_contest_descriptions_table.html.erb - Card list for scannability and full names %> +<% descriptions.each do |description| %> +
+
+
+
+
+
+ <%= link_to description.name, + container_contest_description_contest_instances_path(container, description), + class: 'text-decoration-none fw-semibold fs-5 lh-1' %> +
+ <% if (entries_link = contest_description_entries_link(description)).present? %> +
+ <%= entries_link %> +
<% end %> -
- <%= boolean_to_yes_no(description.active) %> - -
- <% contest_description_action_links(container, description).each do |action, config| %> - <%= link_to config[:path], - class: 'btn btn-link btn-sm p-0', - data: config[:data]&.merge('bs-toggle': 'tooltip'), - title: config[:title], - aria: { label: config[:title] } do %> - - <%= config[:title] %> - <% end %> - <% end %> +
+
+
+ Short name: <%= description.short_name %> + <%= pluralize(description.contest_instances.count, 'instance') %>
-
-
+
<%= render_eligibility_rules(description) %>
+ + +
+ Active: <%= boolean_to_yes_no(description.active) %> +
+ <% contest_description_action_links(container, description).each do |_action, config| %> + <%= link_to config[:path], + class: 'btn btn-link btn-sm p-0 d-inline-flex align-items-center justify-content-center', + style: 'width: 1.5rem; height: 1.5rem;', + data: config[:data]&.merge('bs-toggle': 'tooltip'), + title: config[:title], + aria: { label: config[:title] } do %> + + <%= config[:title] %> + <% end %> + <% end %> +
+ <% if description.notes.to_plain_text.squish.present? %> + <%= link_to "View notes", + container_contest_description_path(container, description), + class: "btn btn-link btn-sm p-0", + title: "View notes", + aria: { label: "View notes for #{description.name}" } %> + <% end %> +
+ + + +<% end %> diff --git a/spec/helpers/containers_helper_spec.rb b/spec/helpers/containers_helper_spec.rb index cff1112a..aaf6fafe 100644 --- a/spec/helpers/containers_helper_spec.rb +++ b/spec/helpers/containers_helper_spec.rb @@ -48,8 +48,8 @@ end describe "#render_eligibility_rules" do - context "when eligibility rules are longer than 60 characters" do - let(:long_text) { "These are very long eligibility rules that will definitely exceed the 60 character limit and therefore trigger the modal functionality." } + context "when eligibility rules have more than 6 words" do + let(:long_text) { "These rules are intentionally long so they trigger modal preview behavior." } let(:rich_text) { instance_double(ActionText::RichText, to_plain_text: long_text) } before do @@ -61,19 +61,21 @@ result = helper.render_eligibility_rules(description) parsed_result = Nokogiri::HTML.fragment(result.to_s) link = parsed_result.at_css('a') + preview_chip = parsed_result.at_css('div') # Check link attributes expect(link['data-action']).to eq('click->modal#open') expect(link['data-modal-title']).to eq('Eligibility Rules') expect(link['href']).to eq('#') - expect(link.text).to eq(' ...more') + expect(link.text).to eq('...more') - # Check truncated text - expect(parsed_result.text).to include(long_text[0..59]) + # Check word-based truncation and visual treatment + expect(parsed_result.text).to match(/Rules:\s*These rules are intentionally long so/) + expect(preview_chip['class']).to include('border') end end - context "when eligibility rules are shorter than 100 characters" do + context "when eligibility rules have 6 or fewer words" do let(:short_text) { "Short rules" } let(:rich_text) { instance_double(ActionText::RichText, to_plain_text: short_text) }