Corregge scadenza token regia oltre le 8h, tabellone HTML sulla pagina live, pulsanti pausa/ripresa in regia, link admin e broadcast ActionCable diretto. App Android: overlay branding, sync score da regia via WebSocket con reconnect e poll. Co-authored-by: Cursor <cursoragent@cursor.com>
124 lines
3.8 KiB
Ruby
124 lines
3.8 KiB
Ruby
module Api
|
|
module V1
|
|
class MatchesController < BaseController
|
|
include Api::UrlHelper
|
|
include BrandingAttachments
|
|
|
|
before_action :set_team, only: %i[index create]
|
|
before_action :set_match, only: %i[show update destroy]
|
|
|
|
def index
|
|
matches = @team.matches
|
|
.includes(:team, :stream_sessions)
|
|
.order(Arel.sql("scheduled_at ASC NULLS LAST"), created_at: :desc)
|
|
.select(&:coach_hub_visible?)
|
|
render json: matches.map { |m| match_json(m) }
|
|
end
|
|
|
|
def create
|
|
match = @team.matches.create!(match_params)
|
|
attach_opponent_logo(match)
|
|
render json: match_json(match), status: :created
|
|
end
|
|
|
|
def show
|
|
render json: match_json(@match, detail: true)
|
|
end
|
|
|
|
def update
|
|
attrs = match_params.to_h
|
|
normalize_scoring_rules!(attrs)
|
|
normalize_opponent_color!(attrs)
|
|
@match.update!(attrs)
|
|
attach_opponent_logo(@match)
|
|
render json: match_json(@match)
|
|
end
|
|
|
|
def destroy
|
|
active = active_session_for(@match)
|
|
if active&.resumable?
|
|
return render json: {
|
|
error: "Chiudi la diretta prima di eliminare questa partita"
|
|
}, status: :unprocessable_entity
|
|
end
|
|
|
|
@match.destroy!
|
|
head :no_content
|
|
end
|
|
|
|
private
|
|
|
|
def set_team
|
|
@team = current_user.streamable_teams.find { |t| t.id == params[:team_id] }
|
|
raise ActiveRecord::RecordNotFound unless @team
|
|
end
|
|
|
|
def set_match
|
|
team_ids = current_user.streamable_teams.map(&:id)
|
|
@match = Match.includes(:team).where(team_id: team_ids).find(params[:id])
|
|
end
|
|
|
|
def match_params
|
|
params.require(:match).permit(
|
|
:opponent_name, :location, :scheduled_at, :sport, :sets_to_win,
|
|
:category, :opponent_primary_color,
|
|
scoring_rules: %i[points_per_set points_deciding_set min_point_lead]
|
|
)
|
|
end
|
|
|
|
def normalize_scoring_rules!(attrs)
|
|
return unless attrs.key?("scoring_rules")
|
|
|
|
rules = attrs["scoring_rules"]
|
|
attrs["scoring_rules"] = {} if rules.blank?
|
|
end
|
|
|
|
def normalize_opponent_color!(attrs)
|
|
return unless attrs.key?("opponent_primary_color")
|
|
|
|
attrs["opponent_primary_color"] = normalize_hex_color(
|
|
attrs["opponent_primary_color"],
|
|
Match::DEFAULT_OPPONENT_COLOR
|
|
)
|
|
end
|
|
|
|
def attach_opponent_logo(match)
|
|
file = params[:opponent_logo_file]
|
|
match.opponent_logo_file.attach(file) if file.present?
|
|
end
|
|
|
|
def match_json(match, detail: false)
|
|
active = active_session_for(match)
|
|
team = match.team
|
|
{
|
|
id: match.id,
|
|
team_id: match.team_id,
|
|
team_name: team.name,
|
|
opponent_name: match.opponent_name,
|
|
location: match.location,
|
|
scheduled_at: match.scheduled_at,
|
|
sport: match.sport,
|
|
sets_to_win: match.sets_to_win,
|
|
scoring_rules: match.scoring_rules.presence,
|
|
category: match.category,
|
|
home_primary_color: team.effective_primary_color,
|
|
home_secondary_color: team.effective_secondary_color,
|
|
home_logo_url: api_absolute_url(team.effective_logo_url),
|
|
opponent_primary_color: match.effective_opponent_primary_color,
|
|
opponent_logo_url: api_absolute_url(match.opponent_logo_url),
|
|
active_session_id: active&.id,
|
|
active_session_status: active&.status,
|
|
stream_completed: match.stream_completed?,
|
|
coach_hub_visible: match.coach_hub_visible?
|
|
}
|
|
end
|
|
|
|
def active_session_for(match)
|
|
match.stream_sessions
|
|
.order(created_at: :desc)
|
|
.find { |s| !%w[ended error].include?(s.status) }
|
|
end
|
|
end
|
|
end
|
|
end
|