app/views/athletes/show.html.erb
<%
athlete_name = @athlete.name || t('common.nobody')
seconds_in_results = @athlete.stats.dig('results', 'seconds') || []
seconds_in_results_count = seconds_in_results.size
%>
<% head_info :title, athlete_name %>
<% head_info :description, t('.description', athlete_name:) %>
<div class="row">
<div class="col-6">
<h1 class="mt-2"><%= athlete_name %></h1>
</div>
<div class="ms-auto col-auto position-relative">
<%= image_tag user_image_path(@athlete.user), class: 'avatar', title: athlete_name %>
<a href="#" class="btn btn-outline-primary rounded-circle position-absolute top-0 end-0 me-2" role="button" data-bs-toggle="modal" data-bs-target="#barcodeModal">
<i class="fa-solid fa-qrcode" title="QR"></i>
</a>
</div>
</div>
<div class="accordion accordion-flush" id="accordionPersonalBest" data-controller="athlete">
<% if @athlete.club %>
<div class="accordion-item">
<h2 class="accordion-header" id="flush-club">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#flush-collapseClub" aria-expanded="false" aria-controls="flush-collapseClub">
<span class="badge text-bg-ligth bg-primary me-3"><i class="fa-solid fa-people-group"></i></span>
<%= @athlete.club.name %>
</button>
</h2>
<div id="flush-collapseClub" class="accordion-collapse collapse" aria-labelledby="flush-club" data-bs-parent="#accordionPersonalBest">
<div class="accordion-body">
<%= t '.show_club_html', link: link_to(@athlete.club.name, @athlete.club) %>
</div>
</div>
</div>
<% end %>
<% if @total_results.positive? %>
<div class="accordion-item">
<h2 class="accordion-header" id="flush-totalResults">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#flush-collapseTotalResults" aria-expanded="false" aria-controls="flush-collapseTotalResults">
<span class="badge text-bg-ligth bg-primary me-3"><%= @total_results %></span>
<%= t '.total_results' %>
</button>
</h2>
<div id="flush-collapseTotalResults" class="accordion-collapse collapse" aria-labelledby="flush-totalResults" data-bs-parent="#accordionPersonalBest">
<div class="accordion-body">
<div class="d-flex flex-wrap justify-content-around">
<div id="chart-results" class="col-12 col-md-10 col-xl-8"></div>
</div>
</div>
</div>
</div>
<% end %>
<% if @total_vol.positive? %>
<div class="accordion-item">
<h2 class="accordion-header" id="flush-totalVolunteering">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#flush-collapseTotalVolunteering" aria-expanded="false" aria-controls="flush-collapseTotalVolunteering">
<span class="badge text-bg-ligth bg-primary me-3"><%= @total_vol %></span>
<%= t '.total_volunteering' %>
</button>
</h2>
<div id="flush-collapseTotalVolunteering" class="accordion-collapse collapse" aria-labelledby="flush-totalVolunteering" data-bs-parent="#accordionPersonalBest">
<div class="accordion-body">
<ul class="list-group list-group-flush">
<li class="list-group-item">
<div class="hstack">
<div class="acc-li-s"><%= t '.h_index' %></div>
<div class="mx-3">
<abbr title="<%= t '.h_index_explanation' %>" class="initialism">
<%= @volunteering.group(:role).reorder(count_all: :desc).count.values.map.with_index.take_while { |count, idx| count > idx }.size %>
</abbr>
</div>
</div>
</li>
<li class="list-group-item">
<div class="hstack">
<div class="acc-li-s"><%= t '.volunteering_coefficient' %></div>
<div class="mx-3">
<abbr title="<%= t '.volunteering_coefficient_explanation' %>" class="initialism">
<% if @total_results.zero? %>
<i class="fa-solid fa-infinity"></i>
<% else %>
<%= (@total_vol * 10.0 / @total_results).round(1) %>
<% end %>
</abbr>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
<% end %>
<% if @total_trophies.positive? %>
<div class="accordion-item">
<h2 class="accordion-header" id="flush-totalTrophies">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#flush-collapseTotalTrophies" aria-expanded="false" aria-controls="flush-collapseTotalTrophies">
<span class="badge text-bg-ligth bg-primary me-3"><%= @total_trophies %></span>
<%= t '.total_trophies' %>
</button>
</h2>
<div id="flush-collapseTotalTrophies" class="accordion-collapse collapse" aria-labelledby="flush-totalTrophies" data-bs-parent="#accordionPersonalBest">
<div class="accordion-body">
<div class="d-flex justify-content-start flex-wrap">
<% if @total_results.positive? && seconds_in_results_count < 60 %>
<%= render 'minute_bingo', seconds_in_results_count: %>
<% end %>
<% @athlete.trophies.includes(badge: { image_attachment: :blob }).order('date ASC NULLS FIRST').each do |trophy| %>
<div class="p-1">
<%
trophy_link = link_to badge_path(trophy.badge) do
image_tag trophy.badge.image.variant(:thumb), class: 'trophy', title: trophy.badge.name
end
%>
<% if trophy.badge.record_kind? %>
<% trophy.info['data'].each do |record_data| %>
<%= trophy_link %>
<% end %>
<% else %>
<%= trophy_link %>
<% end %>
</div>
<% end %>
</div>
</div>
</div>
</div>
<% end %>
<% if @total_results.positive? %>
<div class="accordion-item">
<h2 class="accordion-header" id="flush-totalEvents">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#flush-collapseTotalEvents" aria-expanded="false" aria-controls="flush-collapseTotalEvents">
<span class="badge text-bg-ligth bg-primary me-3"><%= @total_events %></span>
<%= t '.total_events' %>
</button>
</h2>
<div id="flush-collapseTotalEvents" class="accordion-collapse collapse" aria-labelledby="flush-totalEvents" data-bs-parent="#accordionPersonalBest">
<div class="accordion-body">
<div class="d-flex flex-wrap justify-content-around">
<div id="chart-events-count" class="col-12 col-md-6 col-xl-4"></div>
<div id="chart-events-whiskers" class="col-12 col-md-6 col-xl-4"></div>
</div>
</div>
</div>
</div>
<div class="accordion-item">
<% pb_result = @pb_by_time.first %>
<h2 class="accordion-header" id="flush-personalBestAbsolute">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#flush-collapsePersonalBestAbsolute" aria-expanded="false" aria-controls="flush-collapsePersonalBestAbsolute">
<span class="badge text-bg-ligth bg-primary me-3"><%= pb_result ? human_result_time(pb_result.total_time) : '?' %></span>
<%= t '.personal_best' %>
</button>
</h2>
<div id="flush-collapsePersonalBestAbsolute" class="accordion-collapse collapse" aria-labelledby="flush-personalBestAbsolute" data-bs-parent="#accordionPersonalBest">
<div class="accordion-body">
<table class="table">
<thead>
<tr>
<th><%= t '.table.pb' %></th>
<th>Δ</th>
<th><%= t '.table.date' %></th>
<th><%= t '.table.event' %></th>
</tr>
</thead>
<tbody>
<% @pb_by_time.includes(activity: :event).to_a.push(nil).each_cons(2) do |pb, pb_prev| %>
<tr>
<td><%= human_result_time pb.total_time %></td>
<td>
<% if pb_prev %>
<% delta = (pb_prev.total_time - pb.total_time).to_i %>
<%= format('%d:%02d', delta / 60, delta % 60) %>
<% end %>
</td>
<td><%= link_to l(pb.activity.date), pb.activity %></td>
<td><%= link_to pb.activity.event_name, event_path(pb.activity.event.code_name) %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="flush-bestPositionAbsolute">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#flush-collapseBestPositionAbsolute" aria-expanded="false" aria-controls="flush-collapseBestPositionAbsolute">
<span class="badge text-bg-ligth bg-primary me-3"><%= @best_position %></span>
<%= t '.best_position' %>
</button>
</h2>
<div id="flush-collapseBestPositionAbsolute" class="accordion-collapse collapse" aria-labelledby="flush-bestPositionAbsolute" data-bs-parent="#accordionPersonalBest">
<div class="accordion-body">
<ul class="list-group list-group-flush">
<% @pb_by_position.each do |pb| %>
<li class="list-group-item">
<div class="hstack">
<div class="acc-li-s">
<%= link_to l(pb.activity.date), pb.activity %>
</div>
<div class="mx-3">
<%= human_result_time pb.total_time %>
</div>
<div>
<%= link_to pb.activity.event_name, event_path(pb.activity.event.code_name) %>
</div>
</div>
</li>
<% end %>
</ul>
</div>
</div>
</div>
<% end %>
</div>
<% if @total_results.zero? %>
<p><%= t '.no_results' %></p>
<% else %>
<h2 class="mt-3"><%= t '.results' %></h2>
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th><%= t '.table.date' %></th>
<th><%= t '.table.time' %></th>
<th class="hidden-on-phone"><%= t '.table.pace' %></th>
<th class="hidden-on-phone"><%= t '.table.place' %></th>
<th><%= t '.table.event' %></th>
</tr>
</thead>
<tbody class="table-group-divider">
<%= render partial: 'result', collection: @athlete.results.published.includes(activity: :event).order('activity.date DESC') %>
</tbody>
</table>
<%= render '/expand_button' if @total_results > 15 %>
<% end %>
<h2><%= t '.volunteering' %></h2>
<% if @total_vol.zero? %>
<p><%= t '.no_volunteering' %></p>
<% else %>
<div class="accordion accordion-flush" id="accordionRoles">
<% @volunteering.pluck(:role).uniq.each do |role| %>
<% volunteering_in_role = @volunteering.includes(activity: :event).where(role:) %>
<div class="accordion-item">
<h2 id="flush-heading-<%= role %>" class="accordion-header">
<button class="accordion-button collapsed position-relative" type="button" data-bs-toggle="collapse" data-bs-target="#flush-collapse-<%= role %>" aria-expanded="false" aria-controls="flush-collapse-<%= role %>">
<span class="badge text-bg-ligth bg-primary me-3"><%= volunteering_in_role.size %></span>
<%= human_volunteer_role role %>
</button>
</h2>
<div id="flush-collapse-<%= role %>" class="accordion-collapse collapse" aria-labelledby="flush-heading-<%= role %>" data-bs-parent="#accordionRoles">
<div class="accordion-body">
<ul class="list-group list-group-flush">
<% volunteering_in_role.each do |vol| %>
<li class="list-group-item">
<div class="hstack">
<div class="acc-li-s">
<%= link_to l(vol.activity.date), vol.activity %>
</div>
<div class="ms-3">
<%= link_to vol.activity.event_name, event_path(vol.activity.event.code_name) %>
</div>
</div>
</li>
<% end %>
</ul>
</div>
</div>
</div>
<% end %>
</div>
<% end %>
<!-- Modal barcode -->
<div class="modal fade" id="barcodeModal" tabindex="-1" aria-labelledby="barcodeModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="barcodeModalLabel">A<%= @athlete.code %></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body d-flex justify-content-center">
<div><%== @barcode %></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><%= t '.close' %></button>
</div>
</div>
</div>
</div>
<!-- Modal minute bingo -->
<div class="modal fade" id="minuteBingoModal" tabindex="-1" aria-labelledby="minuteBingoModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="minuteBingoModalLabel"><%= t '.mission_minute_bingo' %></h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<table class="mx-auto">
<% 6.times.each do |row| %>
<tr>
<% 10.times.each do |col| %>
<% x = row * 10 + col %>
<td class="text-<%= seconds_in_results.include?(x) ? 'primary' : 'light' %>"><%= format '%02d', x %></td>
<% end %>
</tr>
<% end %>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><%= t '.close' %></button>
</div>
</div>
</div>
</div>