module Public class TournamentsController < WebBaseController before_action :require_login! before_action :set_club, only: %i[index new create] before_action :set_tournament, except: %i[index new create] before_action :require_owner! before_action :require_writable!, only: %i[update generate_group_matches propose_knockout] def index @tournaments = @club.tournaments.order(starts_on: :desc) @can_create = Tournaments::Entitlements.new(@club).premium_full? end def new assert_full! return if performed? @tournament = @club.tournaments.build( sport_key: Sports::Catalog.normalize_key(@club.sport), starts_on: Date.current, ends_on: Date.current + 1, format_kind: "mixed", courts: ["Campo 1", "Campo 2"], knockout_size: 4 ) end def create @tournament = Tournaments::Create.call(club: @club, attrs: tournament_params) redirect_to public_tournament_path(@tournament), notice: t("flash.tournaments.created") rescue Tournaments::EntitlementError => e redirect_to public_club_billing_path(@club), alert: e.message rescue ActiveRecord::RecordInvalid => e @tournament = e.record flash.now[:alert] = e.record.errors.full_messages.join(", ") render :new, status: :unprocessable_entity end def show if params[:tab] == "delega" redirect_to public_tournament_path(@tournament, tab: "dirette") return end load_hub! end def update attrs = tournament_params file = attrs.delete(:logo_file) @tournament.assign_attributes(attrs) @tournament.logo_file.attach(file) if file.present? @tournament.save! redirect_to public_tournament_path(@tournament, tab: hub_tab || params[:tab]), notice: t("flash.tournaments.updated") rescue ActiveRecord::RecordInvalid => e load_hub! flash.now[:alert] = e.record.errors.full_messages.join(", ") render :show, status: :unprocessable_entity end def publish assert_full! return if performed? @tournament.update!(status: "published") redirect_to public_tournament_path(@tournament), notice: t("flash.tournaments.published") end def unpublish assert_full! return if performed? @tournament.update!(status: "draft") redirect_to public_tournament_path(@tournament), notice: t("flash.tournaments.unpublished") end def archive assert_full! return if performed? @tournament.update!(status: "archived") redirect_to public_tournament_path(@tournament), notice: t("flash.tournaments.archived") end def destroy Tournaments::Destroy.call(tournament: @tournament) redirect_to public_club_tournaments_path(@club), notice: t("flash.tournaments.deleted") rescue Tournaments::Destroy::LiveBroadcastError => e redirect_to public_tournament_path(@tournament), alert: e.message end def generate_group_matches created = Tournaments::GenerateGroupMatches.call(tournament: @tournament) redirect_to public_tournament_path(@tournament, tab: "calendario"), notice: t("flash.tournaments.group_matches_created", count: created.size) rescue Tournaments::EntitlementError, ActiveRecord::RecordInvalid => e redirect_to public_tournament_path(@tournament, tab: "calendario"), alert: e.message end def propose_knockout updated = Tournaments::ProposeKnockout.call(@tournament) redirect_to public_tournament_path(@tournament, tab: "tabellone"), notice: t("flash.tournaments.knockout_proposed", count: updated.size) rescue Tournaments::EntitlementError, ActiveRecord::RecordInvalid => e redirect_to public_tournament_path(@tournament, tab: "tabellone"), alert: e.message end private def set_club @club = Club.find(params[:club_id] || params[:id]) end def set_tournament @tournament = Tournament.find(params[:id]) @club = @tournament.club end def require_owner! require_club_owner!(@club) end def assert_full! Tournaments::Entitlements.new(@club).assert_writable! rescue Tournaments::EntitlementError => e redirect_to public_club_billing_path(@club), alert: e.message end def require_writable! assert_full! return if performed? return if @tournament.writable? redirect_to public_tournament_path(@tournament), alert: t("flash.tournaments.archived_locked") end def load_hub! @tab = hub_tab || "squadre" @participants = @tournament.participants.with_attached_logo_file.includes(:group, :source_team).order(:position, :name) @groups = @tournament.groups.order(:position) @rounds = @tournament.rounds.order(:position) @matches = @tournament.matches.includes( { home_participant: { logo_file_attachment: :blob } }, { away_participant: { logo_file_attachment: :blob } }, :tournament_group, :tournament_round, :stream_sessions, { broadcast_assignments: :user } ).order(:scheduled_at) @invitations = @tournament.broadcast_invitations.order(created_at: :desc) @pending_invites_by_match_id = pending_invites_by_match_id @standings_by_group = @groups.index_with { |group| Tournaments::Standings.call(group) } @overlap_warnings = overlap_warnings @writable = @tournament.writable? && Tournaments::Entitlements.new(@club).premium_full? end def hub_tab raw = params[:tab].to_s raw = "dirette" if raw == "delega" raw.presence_in(%w[squadre struttura calendario dirette tabellone]) end def pending_invites_by_match_id pending = @tournament.broadcast_invitations.pending.to_a map = Hash.new { |h, k| h[k] = [] } @matches.each do |match| pending.each do |invitation| map[match.id] << invitation if invitation.covers_match?(match) end end map end def overlap_warnings limit = @tournament.concurrent_limit return [] unless limit @matches.group_by { |m| m.scheduled_at&.strftime("%Y-%m-%d %H:%M") }.filter_map do |slot, list| next if slot.blank? || list.size <= limit t("tournaments.hub.overlap_warning", slot: slot, count: list.size, limit: limit) end end def tournament_params params.require(:tournament).permit( :name, :sport_key, :venue, :starts_on, :ends_on, :format_kind, :description, :knockout_size, :courts, :logo_file ) end end end