Aggiunge i tornei con hub, pagina pubblica e tabellone per semifinali e finali.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-05 15:05:57 +02:00
co-authored by Cursor
parent d52434fb2e
commit a1f4c24a43
124 changed files with 6979 additions and 91 deletions
@@ -0,0 +1,48 @@
<% state = tournament_public_board_state(match) %>
<% live_session = tournament_public_live_session(match) %>
<% recording = tournament_public_recording(match) %>
<% score = tournament_public_score_label(match) %>
<tr class="tournament-board__row tournament-board__row--<%= state %>">
<td>
<% if match.scheduled_at %>
<%= match.scheduled_at.in_time_zone.strftime("%H:%M") %>
<% else %>
<% end %>
</td>
<td><%= match.court.presence || "—" %></td>
<td>
<div class="tournament-stream-plan__matchup">
<%= tournament_team_chip(match.home_participant, name: match.home_display_name) %>
<span class="tournament-stream-plan__vs"></span>
<%= tournament_team_chip(match.away_participant, name: match.away_display_name) %>
</div>
<% if match.tournament_group || match.tournament_round %>
<p class="tournament-board__phase">
<%= [match.tournament_group&.name, match.tournament_round&.name].compact.join(" · ") %>
</p>
<% end %>
</td>
<td>
<% if score.present? %>
<strong><%= score %></strong>
<% else %>
<% end %>
</td>
<td>
<span class="tournament-live-status tournament-live-status--<%= state %>">
<span class="tournament-live-status__dot" aria-hidden="true"></span>
<%= t("tournaments.page.state.#{state}") %>
</span>
</td>
<td class="tournament-board__actions">
<% if live_session %>
<%= link_to t("tournaments.page.watch_live"), public_live_path(live_session), class: "tournament-board__cta tournament-board__cta--live" %>
<% elsif recording %>
<%= link_to t("tournaments.page.watch_replay"), public_replay_path(recording.stream_session_id), class: "tournament-board__cta" %>
<% else %>
<span class="muted"><%= t("tournaments.page.no_media") %></span>
<% end %>
</td>
</tr>
@@ -0,0 +1,28 @@
<% club = tournament.club %>
<% logo = tournament.effective_logo_url %>
<%= link_to public_tournament_page_path(tournament.slug), class: "team-directory-card tournament-directory-card" do %>
<div class="team-directory-card__media">
<% if logo.present? %>
<%= image_tag logo, alt: "", class: "team-directory-card__photo team-directory-card__photo--logo", width: 72, height: 72 %>
<% else %>
<div class="team-directory-card__photo team-directory-card__photo--fallback" aria-hidden="true">
<%= tournament.name.to_s.first %>
</div>
<% end %>
</div>
<div class="team-directory-card__body">
<p class="team-directory-card__club"><%= club.name %></p>
<h2 class="team-directory-card__name"><%= tournament.name %></h2>
<p class="team-directory-card__meta">
<%= tournament.sport_label %>
· <%= l(tournament.starts_on) %> <%= l(tournament.ends_on) %>
<% if tournament.venue.present? %> · <%= tournament.venue %><% end %>
</p>
<div class="team-directory-card__badges">
<% if tournament.archived? %>
<span class="badge badge-scheduled"><%= t("tournaments.status.archived") %></span>
<% end %>
<span class="tournament-directory-follow"><%= t("tournaments.directory.follow") %></span>
</div>
</div>
<% end %>
@@ -0,0 +1,62 @@
<section class="team-public-section" id="risultati" aria-labelledby="tournament-results-heading">
<h2 id="tournament-results-heading" class="section-heading visually-hidden"><%= t("tournaments.page.tabs.risultati") %></h2>
<p class="results-hint"><%= t("tournaments.page.board_hint") %></p>
<% if @matches.any? %>
<% tournament_matches_grouped_by_date(@matches).each do |date, matches| %>
<h3 class="tournament-board__day">
<%= date ? l(date, format: :long) : t("tournaments.hub.stream_plan_unscheduled") %>
</h3>
<div class="tournament-table-wrap">
<table class="data tournament-board">
<thead>
<tr>
<th><%= t("tournaments.hub.datetime") %></th>
<th><%= t("tournaments.hub.court") %></th>
<th><%= t("tournaments.hub.stream_plan_match") %></th>
<th><%= t("tournaments.page.score") %></th>
<th><%= t("tournaments.hub.stream_plan_live_col") %></th>
<th></th>
</tr>
</thead>
<tbody>
<% matches.each do |match| %>
<%= render "public/tournament_pages/board_row", match: match %>
<% end %>
</tbody>
</table>
</div>
<% end %>
<% else %>
<p class="muted"><%= t("tournaments.hub.no_matches") %></p>
<% end %>
</section>
<% if @recordings.any? %>
<section class="team-public-section">
<h2 class="section-heading section-heading--spaced"><%= t("tournaments.page.section_replays") %></h2>
<div class="replay-grid">
<% @recordings.each do |rec| %>
<% match = rec.stream_session.match %>
<%= link_to public_replay_path(rec.stream_session_id), class: "replay-card" do %>
<div class="replay-card__media">
<% if rec.thumbnail_url %>
<img src="<%= rec.thumbnail_url %>" alt="" class="replay-card__thumb" loading="lazy" width="320" height="180">
<% else %>
<div class="replay-card__thumb replay-card__thumb--placeholder" aria-hidden="true">
<span class="replay-card__placeholder-icon"></span>
</div>
<% end %>
<% if rec.duration_label.present? %>
<span class="replay-card__duration"><%= rec.duration_label %></span>
<% end %>
<span class="replay-card__play" aria-hidden="true"></span>
</div>
<div class="replay-card__body">
<strong class="replay-card__title"><%= rec.title.presence || match.matchup_label %></strong>
<p class="replay-card__meta"><%= match.matchup_label %></p>
</div>
<% end %>
<% end %>
</div>
</section>
<% end %>
@@ -0,0 +1,60 @@
<% visible_rounds = @rounds.select { |round| (@matches_by_round[round.id] || []).any? } %>
<section class="team-public-section" id="tabellone" aria-labelledby="tournament-bracket-heading">
<h2 id="tournament-bracket-heading" class="section-heading visually-hidden"><%= t("tournaments.page.tabs.tabellone") %></h2>
<% @groups.each do |group| %>
<h3 class="tournament-board__day"><%= group.name %><%= t("tournaments.hub.standings") %></h3>
<div class="tournament-table-wrap">
<table class="data">
<thead>
<tr>
<th></th>
<th><%= t("tournaments.hub.played") %></th>
<th><%= t("tournaments.hub.won") %></th>
<th><%= t("tournaments.hub.lost") %></th>
<th><%= t("tournaments.hub.points") %></th>
</tr>
</thead>
<tbody>
<% @standings_by_group[group].each_with_index do |row, idx| %>
<tr>
<td><%= idx + 1 %>. <%= tournament_team_chip(row.participant) %></td>
<td><%= row.played %></td>
<td><%= row.won %></td>
<td><%= row.lost %></td>
<td><strong><%= row.points %></strong></td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% end %>
<% visible_rounds.each do |round| %>
<h3 class="tournament-board__day"><%= round.name %></h3>
<div class="tournament-table-wrap">
<table class="data tournament-board">
<thead>
<tr>
<th><%= t("tournaments.hub.datetime") %></th>
<th><%= t("tournaments.hub.court") %></th>
<th><%= t("tournaments.hub.stream_plan_match") %></th>
<th><%= t("tournaments.page.score") %></th>
<th><%= t("tournaments.hub.stream_plan_live_col") %></th>
<th></th>
</tr>
</thead>
<tbody>
<% @matches_by_round[round.id].each do |match| %>
<%= render "public/tournament_pages/board_row", match: match %>
<% end %>
</tbody>
</table>
</div>
<% end %>
<% if @groups.none? && visible_rounds.none? %>
<p class="muted"><%= t("tournaments.page.bracket_empty") %></p>
<% end %>
</section>
@@ -0,0 +1,20 @@
<% content_for :title, t("tournaments.directory.meta_title") %>
<% content_for :meta_description, t("tournaments.directory.meta_description") %>
<% content_for :canonical_url, seo_absolute_url(public_tournament_pages_path) %>
<div class="wrap team-directory">
<h1><%= t("tournaments.directory.heading") %></h1>
<p class="results-hint"><%= t("tournaments.directory.hint") %></p>
<% if @tournaments.any? %>
<div class="team-directory-grid">
<% @tournaments.each do |tournament| %>
<%= render "public/tournament_pages/directory_card", tournament: tournament %>
<% end %>
</div>
<% else %>
<div class="empty-state empty-state--soft">
<p><strong><%= t("tournaments.directory.empty") %></strong></p>
</div>
<% end %>
</div>
@@ -0,0 +1,82 @@
<% content_for :title, t("tournaments.page.title", name: @tournament.name, club: @club.name) %>
<% content_for :robots, @tournament.published? ? "index, follow" : "noindex, nofollow" %>
<% content_for :canonical_url, seo_absolute_url(public_tournament_page_path(@tournament.slug)) if @tournament.published? %>
<div class="wrap team-public-page tournament-public">
<p class="results-hint" style="margin-bottom:8px">
<%= link_to t("tournaments.page.back_to_list"), public_tournament_pages_path, class: "back-link" %>
</p>
<header class="roster-hero card team-public-hero tournament-public-hero" style="--club-primary:<%= @tournament.effective_primary_color %>;--club-secondary:<%= @tournament.effective_secondary_color %>">
<% logo = @tournament.effective_logo_url %>
<% if logo.present? %>
<%= image_tag logo, alt: "", class: "tournament-public-hero__logo", width: 56, height: 56 %>
<% else %>
<div class="tournament-public-hero__logo tournament-public-hero__logo--fallback" aria-hidden="true">
<%= @tournament.name.to_s.first %>
</div>
<% end %>
<div class="roster-hero__intro">
<p class="roster-hero__club"><%= @club.name %></p>
<h1 class="roster-hero__title"><%= @tournament.name %></h1>
<p class="team-public-meta">
<%= @tournament.sport_label %>
· <%= l(@tournament.starts_on) %> <%= l(@tournament.ends_on) %>
<% if @tournament.venue.present? %> · <%= @tournament.venue %><% end %>
</p>
<% if @owner_preview %>
<p class="muted" style="margin:6px 0 0"><%= t("tournaments.page.draft_banner") %></p>
<% end %>
</div>
<% if @owner_manage %>
<%= link_to t("tournaments.index.open"), public_tournament_path(@tournament), class: "btn btn-secondary tournament-public-hero__manage" %>
<% end %>
</header>
<% if @live_sessions.any? %>
<section class="team-public-section" aria-labelledby="tournament-live-heading">
<h2 id="tournament-live-heading" class="section-heading"><%= t("tournaments.page.section_live") %></h2>
<div class="live-grid">
<% @live_sessions.each do |session| %>
<% match = session.match %>
<% on_air = @online_paths.include?(session.mediamtx_path_name) %>
<article class="live-card">
<%= live_match_card_heading(match, link_team: false) %>
<p class="meta">
<% if match.court_or_location.present? %><%= match.court_or_location %> · <% end %>
Match Live TV
</p>
<% if session.score_state %>
<p class="card-score">
<span class="card-sets"><%= live_score_sets_label(session.score_state, match) %></span>
<% if live_score_partials_label(session.score_state).present? %>
<span class="card-partials"><%= t("score.partials_prefix", value: live_score_partials_label(session.score_state)) %></span>
<% end %>
<span class="card-points"><%= session.score_state.home_points %> - <%= session.score_state.away_points %></span>
</p>
<% end %>
<div class="badges">
<% if on_air %>
<span class="badge badge-on-air"><%= t("live.index.badge_on_air") %></span>
<% elsif session.paused? %>
<span class="badge badge-connecting"><%= t("live.index.badge_paused") %></span>
<% else %>
<span class="badge badge-live"><%= t("live.index.badge_live") %></span>
<% end %>
</div>
<%= link_to t("tournaments.page.watch_live"), public_live_path(session), class: "btn-watch" %>
</article>
<% end %>
</div>
</section>
<% end %>
<nav class="tournament-public-tabs" aria-label="<%= t("tournaments.page.tabs_label") %>">
<% %w[risultati tabellone].each do |tab| %>
<%= link_to t("tournaments.page.tabs.#{tab}"), public_tournament_page_path(@tournament.slug, tab: tab),
class: "btn #{@tab == tab ? 'btn-primary' : 'btn-secondary'}" %>
<% end %>
</nav>
<%= render "public/tournament_pages/tab_#{@tab}" %>
</div>