diff --git a/app/controllers/mats_controller.rb b/app/controllers/mats_controller.rb
index 7d3745a..15b0603 100644
--- a/app/controllers/mats_controller.rb
+++ b/app/controllers/mats_controller.rb
@@ -1,22 +1,21 @@
class MatsController < ApplicationController
before_action :set_mat, only: [:show, :edit, :update, :destroy, :assign_next_match]
before_action :check_access, only: [:new,:create,:update,:destroy,:edit,:show, :assign_next_match]
- before_action :check_for_matches, only: [:show]
# GET /mats/1
# GET /mats/1.json
def show
- bout_number_param = params[:bout_number] # Read the bout_number from the URL params
-
- if bout_number_param
- @show_next_bout_button = false
- @match = @mat.queue_matches.compact.find { |m| m.bout_number == bout_number_param.to_i }
+ bout_number_param = params[:bout_number]
+ @queue_matches = @mat.queue_matches
+ @match = if bout_number_param
+ @queue_matches.compact.find { |m| m.bout_number == bout_number_param.to_i }
else
- @show_next_bout_button = true
- @match = @mat.queue1_match
+ @queue_matches[0]
end
-
- @next_match = @mat.queue2_match # Second match on the mat
+ # If a requested bout is no longer queued, fall back to queue1.
+ @match ||= @queue_matches[0]
+ @next_match = @queue_matches[1]
+ @show_next_bout_button = false
@wrestlers = []
if @match
@@ -142,11 +141,4 @@ class MatsController < ApplicationController
end
- def check_for_matches
- if @mat
- if @mat.tournament.matches.empty?
- redirect_to "/tournaments/#{@tournament.id}/no_matches"
- end
- end
- end
end
diff --git a/app/models/mat.rb b/app/models/mat.rb
index 2ef4085..9843e6f 100644
--- a/app/models/mat.rb
+++ b/app/models/mat.rb
@@ -184,6 +184,7 @@ class Mat < ApplicationRecord
def clear_queue!
update!(queue1: nil, queue2: nil, queue3: nil, queue4: nil)
+ broadcast_current_match
end
def unfinished_matches
diff --git a/app/models/tournament.rb b/app/models/tournament.rb
index a5f273b..918ade2 100644
--- a/app/models/tournament.rb
+++ b/app/models/tournament.rb
@@ -97,6 +97,7 @@ class Tournament < ApplicationRecord
def destroy_all_matches
matches.destroy_all
+ mats.each(&:clear_queue!)
end
def matches_by_round(round)
diff --git a/app/services/tournament_services/wipe_tournament_matches.rb b/app/services/tournament_services/wipe_tournament_matches.rb
index 1738a8c..d666c8b 100644
--- a/app/services/tournament_services/wipe_tournament_matches.rb
+++ b/app/services/tournament_services/wipe_tournament_matches.rb
@@ -14,10 +14,10 @@ class WipeTournamentMatches
end
def wipeMatches
- @tournament.matches.destroy_all
+ @tournament.destroy_all_matches
end
def resetSchoolScores
@tournament.schools.update_all("score = 0.0")
end
-end
\ No newline at end of file
+end
diff --git a/app/views/matches/_matchstats.html.erb b/app/views/matches/_matchstats.html.erb
index 3688bbc..4c58f53 100644
--- a/app/views/matches/_matchstats.html.erb
+++ b/app/views/matches/_matchstats.html.erb
@@ -18,8 +18,19 @@
Bout <%= @match.bout_number %>
- <% if @show_next_bout_button && @next_match %>
- <%= link_to "Skip to Next Match for Mat #{@mat.name}", mat_path(@mat, bout_number: @next_match.bout_number), class: "btn btn-primary" %>
+ <% if @mat %>
+ <% queue_matches = @queue_matches || @mat.queue_matches %>
+
+ <% queue_matches.each_with_index do |queue_match, index| %>
+ <% queue_label = "Queue #{index + 1}" %>
+ <% if queue_match %>
+ <% button_class = queue_match.id == @match.id ? "btn btn-success btn-sm" : "btn btn-primary btn-sm" %>
+ <%= link_to "#{queue_label}: Bout #{queue_match.bout_number}", mat_path(@mat, bout_number: queue_match.bout_number), class: button_class %>
+ <% else %>
+
+ <% end %>
+ <% end %>
+
<% end %>
Bracket Position: <%= @match.bracket_position %>
diff --git a/app/views/mats/_current_match.html.erb b/app/views/mats/_current_match.html.erb
index 77f3605..8aa9b2e 100644
--- a/app/views/mats/_current_match.html.erb
+++ b/app/views/mats/_current_match.html.erb
@@ -1,6 +1,8 @@
<% @mat = mat %>
-<% @match = local_assigns[:match] || mat.queue1_match %>
-<% @next_match = local_assigns[:next_match] || mat.queue2_match %>
+<% @queue_matches = local_assigns[:queue_matches] || mat.queue_matches %>
+<% @match = local_assigns[:match] || @queue_matches[0] %>
+<% @match ||= @queue_matches[0] %>
+<% @next_match = local_assigns[:next_match] || @queue_matches[1] %>
<% @show_next_bout_button = local_assigns.key?(:show_next_bout_button) ? local_assigns[:show_next_bout_button] : true %>
<% @wrestlers = [] %>
diff --git a/test/controllers/mats_controller_test.rb b/test/controllers/mats_controller_test.rb
index 2a6e8e5..1d3ca5f 100644
--- a/test/controllers/mats_controller_test.rb
+++ b/test/controllers/mats_controller_test.rb
@@ -259,6 +259,62 @@ class MatsControllerTest < ActionController::TestCase
assert_match /#{bout_number}/, response.body, "The bout_number should be rendered on the page"
end
+ test "mat show renders queue buttons for all four queue slots" do
+ sign_in_owner
+
+ available_matches = @tournament.matches.where(mat_id: nil).limit(3).to_a
+ @mat.assign_match_to_queue!(available_matches[0], 2) if available_matches[0]
+ @mat.assign_match_to_queue!(available_matches[1], 3) if available_matches[1]
+ @mat.assign_match_to_queue!(available_matches[2], 4) if available_matches[2]
+
+ get :show, params: { id: @mat.id }
+ assert_response :success
+
+ assert_includes response.body, "Queue 1: Bout"
+ assert_includes response.body, "Queue 2:"
+ assert_includes response.body, "Queue 3:"
+ assert_includes response.body, "Queue 4:"
+ end
+
+ test "mat show highlights selected queue button and keeps bout_number links working" do
+ sign_in_owner
+
+ queue2_match = @mat.queue2_match
+ unless queue2_match
+ assignable = @tournament.matches.where(mat_id: nil).first
+ @mat.assign_match_to_queue!(assignable, 2) if assignable
+ queue2_match = @mat.reload.queue2_match
+ end
+ assert queue2_match, "Expected queue2 match to be present"
+
+ get :show, params: { id: @mat.id, bout_number: queue2_match.bout_number, foo: "bar" }
+ assert_response :success
+
+ assert_includes response.body, "Queue 2: Bout #{queue2_match.bout_number}"
+ assert_match(/btn btn-success btn-sm/, response.body)
+ assert_includes response.body, "bout_number=#{queue2_match.bout_number}"
+ end
+
+ test "mat show falls back to queue1 when requested bout number is not currently queued" do
+ sign_in_owner
+
+ queue1 = @mat.reload.queue1_match
+ assert queue1, "Expected queue1 to be present"
+
+ get :show, params: { id: @mat.id, bout_number: 999999 }
+ assert_response :success
+ assert_includes response.body, "Bout #{queue1.bout_number}"
+ end
+
+ test "mat show renders no matches assigned when queue is empty" do
+ sign_in_owner
+
+ @mat.clear_queue!
+ get :show, params: { id: @mat.id }
+ assert_response :success
+ assert_includes response.body, "No matches assigned to this mat."
+ end
+
test "logged in tournament owner should redirect back to the first unfinished bout on a mat after submitting a match with a bout number param" do
sign_in_owner
@@ -287,11 +343,12 @@ class MatsControllerTest < ActionController::TestCase
end
#TESTS THAT NEED MATCHES PUT ABOVE THIS
- test "redirect show if no matches" do
+ test "show renders when no matches" do
sign_in_owner
wipe
show
- no_matches
+ success
+ assert_includes response.body, "No matches assigned to this mat."
end
# Assign Next Match Permissions
diff --git a/test/controllers/up_matches_cache_test.rb b/test/controllers/up_matches_cache_test.rb
index 7c80274..f48d341 100644
--- a/test/controllers/up_matches_cache_test.rb
+++ b/test/controllers/up_matches_cache_test.rb
@@ -50,6 +50,29 @@ class UpMatchesCacheTest < ActionController::TestCase
assert_operator cache_writes(third_events), :>, 0, "Expected queue change to invalidate and rewrite at least one row fragment"
end
+ test "up_matches row fragments hit cache after queue clear rewrite" do
+ first_events = cache_events_for_up_matches do
+ get :up_matches, params: { id: @tournament.id }
+ assert_response :success
+ end
+ assert_operator cache_writes(first_events), :>, 0, "Expected initial render to write row fragments"
+
+ mat = @tournament.mats.first
+ clear_events = cache_events_for_up_matches do
+ mat.clear_queue!
+ get :up_matches, params: { id: @tournament.id }
+ assert_response :success
+ end
+ assert_operator cache_writes(clear_events), :>, 0, "Expected queue clear to invalidate and rewrite at least one row fragment"
+
+ repeat_events = cache_events_for_up_matches do
+ get :up_matches, params: { id: @tournament.id }
+ assert_response :success
+ end
+ assert_equal 0, cache_writes(repeat_events), "Expected subsequent render after queue clear rewrite to reuse cached row fragments"
+ assert_operator cache_hits(repeat_events), :>, 0, "Expected cache hits after queue clear rewrite"
+ end
+
private
def cache_events_for_up_matches
diff --git a/test/integration/mat_stats_live_updates_test.rb b/test/integration/mat_stats_live_updates_test.rb
new file mode 100644
index 0000000..7c7b5d5
--- /dev/null
+++ b/test/integration/mat_stats_live_updates_test.rb
@@ -0,0 +1,37 @@
+require "test_helper"
+
+class MatStatsLiveUpdatesTest < ActionDispatch::IntegrationTest
+ include ActionView::RecordIdentifier
+
+ test "destroying all matches broadcasts no-match state for mat stats stream" do
+ create_double_elim_tournament_single_weight_1_6(4)
+ mat = @tournament.mats.create!(name: "Mat 1")
+ @tournament.reset_and_fill_bout_board
+ stream = stream_name_for(mat)
+
+ clear_streams(stream)
+ @tournament.destroy_all_matches
+
+ assert_operator broadcasts_for(stream).size, :>, 0
+ payload = broadcasts_for(stream).last
+ assert_includes payload, dom_id(mat, :current_match)
+ assert_includes payload, "No matches assigned to this mat."
+ end
+
+ private
+
+ def broadcasts_for(stream)
+ ActionCable.server.pubsub.broadcasts(stream)
+ end
+
+ def clear_streams(*streams)
+ ActionCable.server.pubsub.clear
+ streams.each do |stream|
+ broadcasts_for(stream).clear
+ end
+ end
+
+ def stream_name_for(streamable)
+ Turbo::StreamsChannel.send(:stream_name_from, [streamable])
+ end
+end
diff --git a/test/models/match_broadcast_test.rb b/test/models/match_broadcast_test.rb
index da51b34..5980a86 100644
--- a/test/models/match_broadcast_test.rb
+++ b/test/models/match_broadcast_test.rb
@@ -49,6 +49,38 @@ class MatchBroadcastTest < ActiveSupport::TestCase
assert_includes broadcasts_for(stream).last, dom_id(mat, :current_match)
end
+ test "destroy_all_matches clears mats and broadcasts no matches assigned" do
+ create_double_elim_tournament_single_weight_1_6(4)
+ mat = @tournament.mats.create!(name: "Mat 1")
+ @tournament.reset_and_fill_bout_board
+ stream = stream_name_for(mat)
+
+ clear_streams(stream)
+ @tournament.destroy_all_matches
+
+ assert_operator broadcasts_for(stream).size, :>, 0
+ payload = broadcasts_for(stream).last
+ assert_includes payload, dom_id(mat, :current_match)
+ assert_includes payload, "No matches assigned to this mat."
+ assert_equal [nil, nil, nil, nil], mat.reload.queue_match_ids
+ end
+
+ test "wipe tournament matches service clears mats and broadcasts no matches assigned" do
+ create_double_elim_tournament_single_weight_1_6(4)
+ mat = @tournament.mats.create!(name: "Mat 1")
+ @tournament.reset_and_fill_bout_board
+ stream = stream_name_for(mat)
+
+ clear_streams(stream)
+ WipeTournamentMatches.new(@tournament).setUpMatchGeneration
+
+ assert_operator broadcasts_for(stream).size, :>, 0
+ payload = broadcasts_for(stream).last
+ assert_includes payload, dom_id(mat, :current_match)
+ assert_includes payload, "No matches assigned to this mat."
+ assert_equal [nil, nil, nil, nil], mat.reload.queue_match_ids
+ end
+
private
def broadcasts_for(stream)
diff --git a/test/models/up_matches_broadcast_test.rb b/test/models/up_matches_broadcast_test.rb
index 1e3126b..5a95401 100644
--- a/test/models/up_matches_broadcast_test.rb
+++ b/test/models/up_matches_broadcast_test.rb
@@ -26,6 +26,21 @@ class UpMatchesBroadcastTest < ActiveSupport::TestCase
assert_up_matches_replace_payload(broadcasts_for(stream).last)
end
+ test "mat clear_queue broadcasts up matches board update" do
+ tournament = tournaments(:one)
+ mat = mats(:one)
+ match = matches(:tournament_1_bout_2000)
+ stream = stream_name_for(tournament)
+
+ mat.update!(queue1: match.id)
+ clear_streams(stream)
+
+ mat.clear_queue!
+
+ assert_operator broadcasts_for(stream).size, :>, 0
+ assert_up_matches_replace_payload(broadcasts_for(stream).last)
+ end
+
test "match mat assignment change broadcasts up matches board update" do
tournament = tournaments(:one)
mat = mats(:one)
@@ -39,6 +54,21 @@ class UpMatchesBroadcastTest < ActiveSupport::TestCase
assert_up_matches_replace_payload(broadcasts_for(stream).last)
end
+ test "match mat unassignment broadcasts up matches board update" do
+ tournament = tournaments(:one)
+ mat = mats(:one)
+ match = matches(:tournament_1_bout_2001)
+ stream = stream_name_for(tournament)
+
+ match.update!(mat_id: mat.id)
+ clear_streams(stream)
+
+ match.update!(mat_id: nil)
+
+ assert_operator broadcasts_for(stream).size, :>, 0
+ assert_up_matches_replace_payload(broadcasts_for(stream).last)
+ end
+
test "mat update without queue slot changes does not broadcast up matches board update" do
tournament = tournaments(:one)
mat = mats(:one)