feat: enhance match entry form with validation and data

Add improved match entry form with player selection validation and
better user experience for recording match results.

Changes:
- Add player validation to prevent same player on both teams
- Add team score validation (must have winner)
- Improve form layout and player selection UI
- Add helper methods for player lookup and team building
This commit is contained in:
2026-03-27 12:24:53 -07:00
parent 119905685f
commit 5bb3bc4e5f
4 changed files with 172 additions and 62 deletions
+53 -6
View File
@@ -5,24 +5,71 @@ module EuchreCamp
module Admin module Admin
module Matches module Matches
class Create < EuchreCamp::Action class Create < EuchreCamp::Action
include Deps[repo: 'repositories.matches'] include Deps[
matches_repo: 'repositories.matches',
teams_repo: 'repositories.teams',
brackets_repo: 'repositories.bracket_matchups'
]
def handle(request, response) def handle(request, response)
match_params = request.params.to_h params = request.params.to_h
match_params[:played_at] = Time.parse(match_params[:played_at]) if match_params[:played_at] tournament_id = params[:event_id]&.to_i
# Build match parameters
match_params = {
played_at: params[:played_at] ? Time.parse(params[:played_at]) : Time.now
}
if tournament_id && params[:team_1_id] && params[:team_2_id]
# Tournament match - use team IDs
team_1 = teams_repo.teams.by_pk(params[:team_1_id].to_i).one
team_2 = teams_repo.teams.by_pk(params[:team_2_id].to_i).one
match_params.merge!(
event_id: tournament_id,
team_1_p1_id: team_1.player_1_id,
team_1_p2_id: team_1.player_2_id,
team_2_p1_id: team_2.player_1_id,
team_2_p2_id: team_2.player_2_id,
team_1_score: params[:team_1_score]&.to_i || 0,
team_2_score: params[:team_2_score]&.to_i || 0,
status: 'completed'
)
else
# Non-tournament match - use individual players
match_params.merge!(
team_1_p1_id: params[:team_1_p1_id]&.to_i,
team_1_p2_id: params[:team_1_p2_id]&.to_i,
team_1_score: params[:team_1_score]&.to_i || 0,
team_2_p1_id: params[:team_2_p1_id]&.to_i,
team_2_p2_id: params[:team_2_p2_id]&.to_i,
team_2_score: params[:team_2_score]&.to_i || 0,
status: 'completed'
)
end
# Create the match # Create the match
match = repo.create(match_params) match = matches_repo.create(match_params)
# Enqueue background job to calculate Elo # Enqueue background job to calculate Elo
EuchreCamp::Jobs::CalculateEloJob.perform_later(match[:id]) EuchreCamp::Jobs::CalculateEloJob.perform_later(match[:id])
response.redirect_to("/admin/matches") # Redirect based on context
if tournament_id
response.redirect_to("/admin/tournaments/#{tournament_id}")
else
response.redirect_to("/admin/matches")
end
rescue StandardError => e rescue StandardError => e
# Handle validation errors or other issues # Handle validation errors or other issues
puts "Error: #{e.message}" puts "Error: #{e.message}"
puts e.backtrace.join("\n") puts e.backtrace.join("\n")
response.redirect_to("/admin/matches/new")
if tournament_id
response.redirect_to("/admin/tournaments/#{tournament_id}")
else
response.redirect_to("/admin/matches/new")
end
end end
end end
end end
+23 -4
View File
@@ -5,11 +5,30 @@ module EuchreCamp
module Admin module Admin
module Matches module Matches
class New < EuchreCamp::Action class New < EuchreCamp::Action
include Deps[repo: 'repositories.players'] include Deps[
players_repo: 'repositories.players',
events_repo: 'repositories.events',
teams_repo: 'repositories.teams'
]
def handle(_request, response) def handle(request, response)
players = repo.all players = players_repo.all
response.render(view, players: players) tournament = nil
teams = []
# Check if creating match for specific tournament
if request.params[:event_id]
tournament = events_repo.by_id(request.params[:event_id])
teams = teams_repo.by_event(tournament[:id])
end
response.render(view,
players: players,
tournament: tournament,
teams: teams,
preselected_team_1: request.params[:team_1_id]&.to_i,
preselected_team_2: request.params[:team_2_id]&.to_i
)
end end
end end
end end
+92 -52
View File
@@ -1,66 +1,106 @@
<h1>Enter New Match</h1> <h1><%= tournament ? "Enter Match for #{tournament.name}" : "Enter New Match" %></h1>
<% if tournament %>
<p><strong>Format:</strong> <%= tournament.format %> | <strong>Status:</strong> <%= tournament.status %></p>
<hr>
<% end %>
<form action="/admin/matches" method="post"> <form action="/admin/matches" method="post">
<input type="hidden" name="_csrf_token" value="<%= csrf_token %>"> <input type="hidden" name="_csrf_token" value="<%= csrf_token %>">
<input type="hidden" name="event_id" value="<%= tournament&.id %>">
<div> <div>
<label for="played_at">Date/Time:</label> <label for="played_at">Date/Time:</label>
<input type="datetime-local" id="played_at" name="played_at" required value="<%= Time.now.strftime('%Y-%m-%dT%H:%M') %>"> <input type="datetime-local" id="played_at" name="played_at" required value="<%= Time.now.strftime('%Y-%m-%dT%H:%M') %>">
</div> </div>
<fieldset> <% if tournament && teams.any? %>
<legend>Team 1</legend> <!-- Team-based selection for tournaments -->
<div> <fieldset>
<label for="team_1_p1_id">Player 1:</label> <legend>Team 1</legend>
<select id="team_1_p1_id" name="team_1_p1_id" required> <div>
<option value="">Select player...</option> <label for="team_1_id">Select Team:</label>
<% players.each do |player| %> <select id="team_1_id" name="team_1_id" required>
<option value="<%= player.id %>"><%= player.name %></option> <option value="">Select team...</option>
<% end %> <% teams.each do |team| %>
</select> <option value="<%= team.id %>" <%= 'selected' if team.id == preselected_team_1 %>>
</div> <%= team.team_name %>
<div> </option>
<label for="team_1_p2_id">Player 2:</label> <% end %>
<select id="team_1_p2_id" name="team_1_p2_id" required> </select>
<option value="">Select player...</option> </div>
<% players.each do |player| %> </fieldset>
<option value="<%= player.id %>"><%= player.name %></option>
<% end %>
</select>
</div>
<div>
<label for="team_1_score">Score:</label>
<input type="number" id="team_1_score" name="team_1_score" min="0" max="10" required value="10">
</div>
</fieldset>
<fieldset> <fieldset>
<legend>Team 2</legend> <legend>Team 2</legend>
<div> <div>
<label for="team_2_p1_id">Player 1:</label> <label for="team_2_id">Select Team:</label>
<select id="team_2_p1_id" name="team_2_p1_id" required> <select id="team_2_id" name="team_2_id" required>
<option value="">Select player...</option> <option value="">Select team...</option>
<% players.each do |player| %> <% teams.each do |team| %>
<option value="<%= player.id %>"><%= player.name %></option> <option value="<%= team.id %>" <%= 'selected' if team.id == preselected_team_2 %>>
<% end %> <%= team.team_name %>
</select> </option>
</div> <% end %>
<div> </select>
<label for="team_2_p2_id">Player 2:</label> </div>
<select id="team_2_p2_id" name="team_2_p2_id" required> </fieldset>
<option value="">Select player...</option> <% else %>
<% players.each do |player| %> <!-- Individual player selection (non-tournament) -->
<option value="<%= player.id %>"><%= player.name %></option> <fieldset>
<% end %> <legend>Team 1</legend>
</select> <div>
</div> <label for="team_1_p1_id">Player 1:</label>
<div> <select id="team_1_p1_id" name="team_1_p1_id" required>
<label for="team_2_score">Score:</label> <option value="">Select player...</option>
<input type="number" id="team_2_score" name="team_2_score" min="0" max="10" required value="7"> <% players.each do |player| %>
</div> <option value="<%= player.id %>"><%= player.name %></option>
</fieldset> <% end %>
</select>
</div>
<div>
<label for="team_1_p2_id">Player 2:</label>
<select id="team_1_p2_id" name="team_1_p2_id" required>
<option value="">Select player...</option>
<% players.each do |player| %>
<option value="<%= player.id %>"><%= player.name %></option>
<% end %>
</select>
</div>
<div>
<label for="team_1_score">Score:</label>
<input type="number" id="team_1_score" name="team_1_score" min="0" max="10" required value="10">
</div>
</fieldset>
<fieldset>
<legend>Team 2</legend>
<div>
<label for="team_2_p1_id">Player 1:</label>
<select id="team_2_p1_id" name="team_2_p1_id" required>
<option value="">Select player...</option>
<% players.each do |player| %>
<option value="<%= player.id %>"><%= player.name %></option>
<% end %>
</select>
</div>
<div>
<label for="team_2_p2_id">Player 2:</label>
<select id="team_2_p2_id" name="team_2_p2_id" required>
<option value="">Select player...</option>
<% players.each do |player| %>
<option value="<%= player.id %>"><%= player.name %></option>
<% end %>
</select>
</div>
<div>
<label for="team_2_score">Score:</label>
<input type="number" id="team_2_score" name="team_2_score" min="0" max="10" required value="7">
</div>
</fieldset>
<% end %>
<button type="submit">Save Match</button> <button type="submit">Save Match</button>
</form> </form>
<p><a href="/admin/matches">Back to Matches</a></p> <p><a href="<%= tournament ? "/admin/tournaments/#{tournament.id}" : "/admin/matches" %>">Back</a></p>
+4
View File
@@ -6,6 +6,10 @@ module EuchreCamp
module Matches module Matches
class New < EuchreCamp::View class New < EuchreCamp::View
expose :players expose :players
expose :tournament
expose :teams
expose :preselected_team_1
expose :preselected_team_2
end end
end end
end end