Completa multi-sport su web, API score_action e controlli Android board-aware.

Allinea i form società/squadra al catalogo sport, espone score_action per basket/timed e corregge bug namespace Sports e Result nel period engine; l'app nativa gestisce overlay e punteggio per board con wizard regole esteso.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-09 21:48:29 +02:00
parent c0ede48091
commit ad9d67ddb6
25 changed files with 513 additions and 116 deletions

View File

@@ -2,7 +2,7 @@ module Api
module V1
class SportsController < BaseController
def index
render json: Sports::Catalog.as_api_list
render json: ::Sports::Catalog.as_api_list
end
end
end

View File

@@ -39,6 +39,20 @@ module Api
render json: session_json(@session, detail: true)
end
def score_action
action = params[:score_action].presence
raise ArgumentError, "Azione mancante" if action.blank?
result = Scoring::ApplyAction.new(session: @session, action: action).call
render json: session_json(@session, detail: true).merge(
set_won: result.set_won,
match_won: result.match_won,
winner: result.winner&.to_s
)
rescue ArgumentError => e
render json: { error: e.message }, status: :unprocessable_entity
end
def events
events = @session.stream_events.recent.limit(100)
render json: events.map { |e| event_json(e) }

View File

@@ -21,7 +21,7 @@ module Api
}, status: :unprocessable_entity
end
sport_key = Sports::Catalog.normalize_key(team_params[:sport_key] || team_params[:sport] || "pallavolo")
sport_key = ::Sports::Catalog.normalize_key(team_params[:sport_key] || team_params[:sport] || "pallavolo")
club = Club.create!(name: team_params[:name], sport: sport_key,
primary_color: "#e53935", secondary_color: "#ffffff")
ClubMembership.create!(user: current_user, club: club, role: "owner")

View File

@@ -190,6 +190,7 @@ module Public
)
p[:primary_color] = normalize_hex_color(p[:primary_color], "#e53935")
p[:secondary_color] = normalize_hex_color(p[:secondary_color], "#ffffff")
p[:sport] = Sports::Catalog.normalize_key(p[:sport]) if p[:sport].present?
p
end

View File

@@ -165,7 +165,12 @@ module Public
end
def team_params
p = params.require(:team).permit(:name, :sport, :description, :logo_url, :primary_color, :secondary_color)
p = params.require(:team).permit(:name, :sport, :sport_key, :description, :logo_url, :primary_color, :secondary_color)
if p[:sport].present? && p[:sport_key].blank?
p[:sport_key] = p.delete(:sport)
elsif p[:sport_key].present?
p.delete(:sport)
end
p[:primary_color] = p[:primary_color].presence
p[:secondary_color] = p[:secondary_color].presence
if p[:primary_color].present?

View File

@@ -16,4 +16,9 @@ module ApplicationHelper
value = date_or_time.respond_to?(:in_time_zone) ? date_or_time.in_time_zone : date_or_time
I18n.with_locale(:it) { I18n.l(value, format: format) }
end
def sport_catalog_options(selected = nil)
options = Sports::Catalog.as_api_list.map { |entry| [entry[:label], entry[:key]] }
options_for_select(options, Sports::Catalog.normalize_key(selected))
end
end

View File

@@ -1,6 +1,8 @@
module Scoring
module Engines
module PeriodEngineMixin
Result = Scoring::ApplyAction::Result
private
def period_rules

View File

@@ -8,7 +8,7 @@
<%= label_tag "club[name]", "Nome società" %>
<%= text_field_tag "club[name]", @club.name, required: true %>
<%= label_tag "club[sport]", "Sport principale" %>
<%= select_tag "club[sport]", options_for_select([["Pallavolo", "volleyball"], ["Calcio", "football"], ["Basket", "basketball"]], @club.sport) %>
<%= select_tag "club[sport]", sport_catalog_options(@club.sport) %>
<%= render "shared/branding_fields", record: @club, legend: "Logo e colori societari" %>
<%= render "shared/billing_profile_fields", record: @club %>
<%= submit_tag "Salva", class: "btn btn-primary" %>

View File

@@ -11,7 +11,7 @@
<%= label_tag "club[name]", "Nome società / club" %>
<%= text_field_tag "club[name]", params.dig(:club, :name), required: true, placeholder: "es. Crazy Volley Rozzano" %>
<%= label_tag "club[sport]", "Sport principale" %>
<%= select_tag "club[sport]", options_for_select([["Pallavolo", "volleyball"], ["Calcio", "football"], ["Basket", "basketball"]], params.dig(:club, :sport) || "volleyball") %>
<%= select_tag "club[sport]", sport_catalog_options(params.dig(:club, :sport) || "pallavolo") %>
<%= render "shared/branding_fields", record: Club.new(primary_color: "#e53935", secondary_color: "#ffffff"), legend: "Logo e colori societari" %>
<%= render "shared/billing_profile_fields", record: Club.new(billing_country: "IT") %>

View File

@@ -9,7 +9,7 @@
<%= label_tag "team[name]", "Nome squadra" %>
<%= text_field_tag "team[name]", @team.name, required: true %>
<%= label_tag "team[sport]", "Sport" %>
<%= select_tag "team[sport]", options_for_select([["Pallavolo", "volleyball"], ["Calcio", "football"], ["Basket", "basketball"]], @team.sport) %>
<%= select_tag "team[sport]", sport_catalog_options(@team.sport_key) %>
<%= label_tag "team[description]", "Descrizione squadra" %>
<%= text_area_tag "team[description]", @team.description, rows: 5, placeholder: "Presentazione della squadra, palmarès, obiettivi della stagione…" %>

View File

@@ -9,7 +9,7 @@
<%= label_tag "team[name]", "Nome squadra" %>
<%= text_field_tag "team[name]", nil, required: true, placeholder: "es. Under 13, Serie C" %>
<%= label_tag "team[sport]", "Sport" %>
<%= select_tag "team[sport]", options_for_select([["Pallavolo", "volleyball"], ["Calcio", "football"], ["Basket", "basketball"]], @club.sport) %>
<%= select_tag "team[sport]", sport_catalog_options(@club.sport) %>
<%= render "shared/branding_fields", record: Team.new(club: @club), show_inherit_hint: true, legend: "Override branding (opzionale)" %>
<%= submit_tag "Aggiungi squadra", class: "btn btn-primary" %>
<% end %>