Introduce architettura multi-sport con catalogo, engine scoring e overlay.

Catalogo sport in YAML, board implicito, engine per volley/basket/timed/racket/timer/generic, regia e app Android allineate.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-09 20:11:21 +02:00
parent 53f9a6a884
commit e727069d43
56 changed files with 1783 additions and 223 deletions

View File

@@ -0,0 +1,30 @@
<% data = @score.data || {} %>
<p class="regia-sets" id="sets-line"><%= live_score_period_label(@match, @score) %></p>
<p class="regia-score-line" id="score-line"><%= live_score_points_label(@match, @score) %></p>
<p class="regia-partials" id="partials-line" hidden></p>
<p class="regia-clock-line" id="clock-line">Cronometro: <%= format_clock_secs(data["clock_secs"]) %></p>
<div class="regia-team-row">
<div>
<div class="regia-team-name"><%= @team.name %></div>
<div class="regia-points" id="home-points"><%= data["home_score"] || @score.home_points %></div>
<button type="button" class="regia-btn regia-btn--point" data-action="home_point" style="width:100%;margin-top:4px">+1</button>
<button type="button" class="regia-btn regia-btn--point" data-action="home_point_2" style="width:100%;margin-top:4px">+2</button>
<button type="button" class="regia-btn regia-btn--point" data-action="home_point_3" style="width:100%;margin-top:4px">+3</button>
<button type="button" class="regia-btn regia-btn--minus" data-action="home_undo" style="width:100%;margin-top:4px"></button>
</div>
<div class="regia-center-hint" id="center-hint"><%= format_clock_secs(data["clock_secs"]) %></div>
<div>
<div class="regia-team-name"><%= @match.opponent_name %></div>
<div class="regia-points" id="away-points"><%= data["away_score"] || @score.away_points %></div>
<button type="button" class="regia-btn regia-btn--point" data-action="away_point" style="width:100%;margin-top:4px">+1</button>
<button type="button" class="regia-btn regia-btn--point" data-action="away_point_2" style="width:100%;margin-top:4px">+2</button>
<button type="button" class="regia-btn regia-btn--point" data-action="away_point_3" style="width:100%;margin-top:4px">+3</button>
<button type="button" class="regia-btn regia-btn--minus" data-action="away_undo" style="width:100%;margin-top:4px"></button>
</div>
</div>
<div class="regia-clock-actions">
<button type="button" class="regia-btn regia-btn--outline" data-action="clock_toggle"><%= data["clock_running"] ? "Pausa" : "Avvia" %> cronometro</button>
<button type="button" class="regia-btn regia-btn--outline" data-action="clock_reset">Reset</button>
<button type="button" class="regia-btn regia-btn--yellow" data-action="advance_period">Prossimo quarto</button>
</div>

View File

@@ -0,0 +1,19 @@
<p class="regia-sets" id="sets-line">Punteggio libero</p>
<p class="regia-score-line" id="score-line"><%= live_score_points_label(@match, @score) %></p>
<p class="regia-partials" id="partials-line" hidden></p>
<div class="regia-team-row">
<div>
<div class="regia-team-name"><%= @team.name %></div>
<div class="regia-points" id="home-points"><%= @score.home_points %></div>
<button type="button" class="regia-btn regia-btn--point" data-action="home_point" style="width:100%;margin-top:8px">+1</button>
<button type="button" class="regia-btn regia-btn--minus" data-action="home_undo" style="width:100%;margin-top:6px"></button>
</div>
<div class="regia-center-hint" id="center-hint">—</div>
<div>
<div class="regia-team-name"><%= @match.opponent_name %></div>
<div class="regia-points" id="away-points"><%= @score.away_points %></div>
<button type="button" class="regia-btn regia-btn--point" data-action="away_point" style="width:100%;margin-top:8px">+1</button>
<button type="button" class="regia-btn regia-btn--minus" data-action="away_undo" style="width:100%;margin-top:6px"></button>
</div>
</div>

View File

@@ -0,0 +1 @@
<%= render "public/regia/volley" %>

View File

@@ -0,0 +1,26 @@
<% data = @score.data || {} %>
<p class="regia-sets" id="sets-line"><%= live_score_period_label(@match, @score) %></p>
<p class="regia-score-line" id="score-line"><%= live_score_points_label(@match, @score) %></p>
<p class="regia-partials" id="partials-line" hidden></p>
<p class="regia-clock-line" id="clock-line">Cronometro: <%= format_clock_secs(data["clock_secs"]) %></p>
<div class="regia-team-row">
<div>
<div class="regia-team-name"><%= @team.name %></div>
<div class="regia-points" id="home-points"><%= data["home_score"] || @score.home_points %></div>
<button type="button" class="regia-btn regia-btn--point" data-action="home_point" style="width:100%;margin-top:8px">+1</button>
<button type="button" class="regia-btn regia-btn--minus" data-action="home_undo" style="width:100%;margin-top:6px"></button>
</div>
<div class="regia-center-hint" id="center-hint"><%= format_clock_secs(data["clock_secs"]) %></div>
<div>
<div class="regia-team-name"><%= @match.opponent_name %></div>
<div class="regia-points" id="away-points"><%= data["away_score"] || @score.away_points %></div>
<button type="button" class="regia-btn regia-btn--point" data-action="away_point" style="width:100%;margin-top:8px">+1</button>
<button type="button" class="regia-btn regia-btn--minus" data-action="away_undo" style="width:100%;margin-top:6px"></button>
</div>
</div>
<div class="regia-clock-actions">
<button type="button" class="regia-btn regia-btn--outline" data-action="clock_toggle"><%= data["clock_running"] ? "Pausa" : "Avvia" %> cronometro</button>
<button type="button" class="regia-btn regia-btn--outline" data-action="clock_reset">Reset</button>
<button type="button" class="regia-btn regia-btn--yellow" data-action="advance_period">Prossimo tempo</button>
</div>

View File

@@ -0,0 +1,10 @@
<% data = @score.data || {} %>
<p class="regia-sets" id="sets-line">Cronometro</p>
<p class="regia-score-line" id="score-line" hidden></p>
<p class="regia-partials" id="partials-line" hidden></p>
<p class="regia-clock-line" id="clock-line"><%= format_clock_secs(data["clock_secs"], count_up: true) %></p>
<div class="regia-clock-actions" style="margin-top:16px">
<button type="button" class="regia-btn regia-btn--outline" data-action="clock_toggle"><%= data["clock_running"] ? "Pausa" : "Avvia" %></button>
<button type="button" class="regia-btn regia-btn--outline" data-action="clock_reset">Reset</button>
</div>

View File

@@ -0,0 +1,24 @@
<p class="regia-sets" id="sets-line"><%= live_score_sets_label(@score) || "—" %></p>
<p class="regia-score-line" id="score-line"><%= live_score_points_label(@match, @score) %></p>
<p class="regia-partials" id="partials-line"<%= " hidden" unless live_score_partials_label(@score).present? %>>
Parziali: <%= live_score_partials_label(@score) %>
</p>
<div class="regia-team-row">
<div>
<div class="regia-team-name"><%= @team.name %></div>
<div class="regia-points" id="home-points"><%= @score.home_points %></div>
<button type="button" class="regia-btn regia-btn--point" data-action="home_point" style="width:100%;margin-top:8px">+1</button>
<button type="button" class="regia-btn regia-btn--minus" data-action="home_undo" style="width:100%;margin-top:6px"></button>
</div>
<div class="regia-center-hint" id="center-hint">
<%= Scoring::Rules.from_match(@match).points_target(@score.current_set) %> pt
</div>
<div>
<div class="regia-team-name"><%= @match.opponent_name %></div>
<div class="regia-points" id="away-points"><%= @score.away_points %></div>
<button type="button" class="regia-btn regia-btn--point" data-action="away_point" style="width:100%;margin-top:8px">+1</button>
<button type="button" class="regia-btn regia-btn--minus" data-action="away_undo" style="width:100%;margin-top:6px"></button>
</div>
</div>
<button type="button" class="regia-btn regia-btn--yellow" data-action="close_set" style="margin-top:12px">Chiudi set</button>

View File

@@ -9,6 +9,7 @@
<div class="regia-page" id="regia-app"
data-token="<%= j params[:token] %>"
data-board="<%= @match.effective_board_type %>"
data-status-url="<%= j public_regia_status_path(params[:token]) %>"
data-score-url="<%= j public_regia_score_path(params[:token]) %>"
data-pause-url="<%= j public_regia_pause_path(params[:token]) %>"
@@ -20,13 +21,13 @@
data-away-name="<%= j @match.opponent_name %>"
data-share-url="<%= j @share_url %>"
data-share-title="<%= j @share_title %>"
data-points-target="<%= Scoring::Rules.from_match(@match).points_target(@score.current_set) %>"
data-points-target="<%= @match.effective_board_type.in?(%w[volley racket]) ? Scoring::Rules.from_match(@match).points_target(@score.current_set) : 0 %>"
data-stream-closed="<%= @stream_closed %>"
data-initial-paused="<%= @session.paused? %>">
<header class="regia-header">
<h1><%= @team.name %> vs <%= @match.opponent_name %></h1>
<p>Regia · Set <%= @score.current_set %></p>
<p>Regia · <%= regia_header_subtitle(@match, @score) %></p>
<span id="regia-badge" class="regia-badge <%= @stream_closed ? 'regia-badge--ended' : (@session.paused? ? 'regia-badge--wait' : (@on_air ? 'regia-badge--live' : 'regia-badge--wait')) %>">
<%= @stream_closed ? "Terminata" : (@session.paused? ? "In pausa" : (@on_air ? "In onda" : "In attesa")) %>
</span>
@@ -51,30 +52,7 @@
</div>
<section class="regia-scoreboard">
<p class="regia-sets" id="sets-line"><%= live_score_sets_label(@score) || "—" %></p>
<p class="regia-score-line" id="score-line"><%= live_score_points_label(@match, @score) %></p>
<p class="regia-partials" id="partials-line"<%= " hidden" unless live_score_partials_label(@score).present? %>>
Parziali: <%= live_score_partials_label(@score) %>
</p>
<div class="regia-team-row">
<div>
<div class="regia-team-name"><%= @team.name %></div>
<div class="regia-points" id="home-points"><%= @score.home_points %></div>
<button type="button" class="regia-btn regia-btn--point" data-action="home_point" style="width:100%;margin-top:8px">+1</button>
<button type="button" class="regia-btn regia-btn--minus" data-action="home_undo" style="width:100%;margin-top:6px"></button>
</div>
<div style="text-align:center;color:var(--regia-muted);font-size:0.72rem;padding-top:24px">
<%= Scoring::Rules.from_match(@match).points_target(@score.current_set) %> pt
</div>
<div>
<div class="regia-team-name"><%= @match.opponent_name %></div>
<div class="regia-points" id="away-points"><%= @score.away_points %></div>
<button type="button" class="regia-btn regia-btn--point" data-action="away_point" style="width:100%;margin-top:8px">+1</button>
<button type="button" class="regia-btn regia-btn--minus" data-action="away_undo" style="width:100%;margin-top:6px"></button>
</div>
</div>
<button type="button" class="regia-btn regia-btn--yellow" data-action="close_set" style="margin-top:12px">Chiudi set</button>
<%= render regia_board_partial(@match) %>
</section>
<% unless @stream_closed %>
@@ -96,4 +74,4 @@
</div>
</div>
<script src="/regia.js?v=4"></script>
<script src="/regia.js?v=5"></script>