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,95 @@
module Tournaments
class ProposeKnockout
def self.call(tournament)
new(tournament).call
end
def initialize(tournament)
@tournament = tournament
end
def call
Tournaments::Entitlements.new(@tournament.club).assert_writable!
first_round = @tournament.rounds.order(:position).first
return [] unless first_round
pairs = pair_qualified
slots = first_round.matches.order(:scheduled_at, :created_at).to_a
slots = create_slots!(first_round, [pairs.size, 1].max) if slots.empty?
updated = []
slots.each_with_index do |match, idx|
home, away = pairs[idx]
next if home.blank? && away.blank?
match.update!(home_participant: home, away_participant: away)
updated << match
end
updated.concat(seed_later_rounds!(slots))
updated
end
private
def pair_qualified
groups = @tournament.groups.order(:position).to_a
ranked = groups.map { |group| Tournaments::Standings.call(group).map(&:participant) }
return ranked.flatten.each_slice(2).to_a if groups.size < 2
pairs = []
first = ranked[0] || []
second = ranked[1] || []
pairs << [first[0], second[1]] if first[0] || second[1]
pairs << [second[0], first[1]] if second[0] || first[1]
leftover = (first.drop(2) + second.drop(2) + ranked.drop(2).flatten)
pairs.concat(leftover.each_slice(2).to_a)
pairs
end
def seed_later_rounds!(first_round_matches)
prev = first_round_matches
created = []
@tournament.rounds.order(:position).offset(1).each do |round|
needed = [prev.size / 2, 1].max
slots = round.matches.order(:scheduled_at, :created_at).to_a
last_at = prev.map(&:scheduled_at).compact.max
start = last_at&.+(1.hour)
slots = create_slots!(round, needed, start: start) if slots.empty?
slots.each_with_index do |match, idx|
home_src = prev[idx * 2]
away_src = prev[idx * 2 + 1]
attrs = {}
if home_src && match.home_source_match_id.blank?
attrs[:home_source_kind] = "winner_match"
attrs[:home_source_match_id] = home_src.id
end
if away_src && match.away_source_match_id.blank?
attrs[:away_source_kind] = "winner_match"
attrs[:away_source_match_id] = away_src.id
end
match.update!(attrs) if attrs.any?
created << match
end
prev = slots
end
created
end
def create_slots!(round, n, start: nil)
count = [n, 1].max
day = @tournament.ends_on
start ||= Time.zone.local(day.year, day.month, day.day, 18, 0, 0)
Array.new(count) do |i|
Tournaments::ScheduleMatch.call(
tournament: @tournament,
attrs: {
tournament_round_id: round.id,
court: @tournament.court_list.first,
scheduled_at: start + i.hours
}
)
end
end
end
end