Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add knockout stage bracket #37

Merged
merged 1 commit into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/cesium_cup/tournament/match.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ defmodule CesiumCup.Tournament.Match do

@required_fields ~w(date)a

@optional_fields ~w(home_team_id away_team_id group_id elimination_round_id state group_round)a
@optional_fields ~w(home_team_id away_team_id group_id elimination_round_index elimination_round_id state group_round)a

@states ~w(upcoming first_half halftime second_half finished)a

Expand All @@ -27,6 +27,7 @@ defmodule CesiumCup.Tournament.Match do

field :group_round, :integer

field :elimination_round_index, :integer
belongs_to :elimination_round, EliminationRound

has_many :events, Event
Expand Down
69 changes: 69 additions & 0 deletions lib/cesium_cup_web/components/bracket.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
defmodule CesiumCupWeb.Components.Bracket do
@moduledoc """
The knockout stage bracket component
"""
use Phoenix.LiveComponent

alias CesiumCup.Tournament
alias CesiumCupWeb.Router.Helpers, as: Routes

def render(assigns) do
~H"""
<div id={"bracket-#{@index}"}>
<%= if @current_round > 0 do %>
<div class="flex mr-20 items-center">
<div class="flex-col mb-4">
<.live_component module={CesiumCupWeb.Components.Bracket} id={"bracket-#{2 * @index + 1}"} index={2 * @index + 1} current_round={@current_round - 1} number_rounds={@number_rounds} matches={@matches} />
<.live_component module={CesiumCupWeb.Components.Bracket} id={"bracket-#{2 * @index + 2}"} index={2 * @index + 2} current_round={@current_round - 1} number_rounds={@number_rounds} matches={@matches} />
</div>
<% match = Enum.find(@matches, &(&1.elimination_round_index == @index)) %>
<%= live_redirect to: Routes.match_show_path(@socket, :show, match) do %>
<div class="cursor-pointer bg-white border border-zinc-200 rounded-md shadow-sm hover:bg-zinc-50 my-1 relative w-40 h-28">
<div class="h-full w-full flex-col divide-y divide-gray-200">
<div class="w-full flex justify-between items-center h-full h-1/2 text-gray-900 px-4 py-2">
<p>
<%= if match.home_team do %>
<%= match.home_team.name %>
<% else %>
TBD
<% end %>
</p>

<p>
<%= if not is_nil(match.home_team_id) and match.state != :upcoming do %>
<%= get_home_team_score(match.id) %>
<% end %>
</p>
</div>
<div class="w-full flex justify-between items-center h-full h-1/2 text-gray-900 px-4 py-2">
<p>
<%= if match.away_team do %>
<%= match.away_team.name %>
<% else %>
TBD
<% end %>
</p>

<p>
<%= if not is_nil(match.home_team_id) and match.state != :upcoming do %>
<%= get_away_team_score(match.id) %>
<% end %>
</p>
</div>
</div>
</div>
<% end %>
</div>
<% end %>
</div>
"""
end

defp get_home_team_score(match_id) do
Tournament.get_home_team_score(match_id)
end

defp get_away_team_score(match_id) do
Tournament.get_away_team_score(match_id)
end
end
5 changes: 5 additions & 0 deletions lib/cesium_cup_web/live/home_live/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ defmodule CesiumCupWeb.HomeLive.Index do
|> Enum.sort(&(Date.compare(&1.date, &2.date) in [:lt, :eq]))
end

defp list_knockout_stage_matches do
Tournament.list_matches(preloads: [:home_team, :away_team])
|> Enum.filter(&is_nil(&1.group_round))
end

defp list_groups do
Tournament.list_groups(preloads: [:teams])
end
Expand Down
93 changes: 52 additions & 41 deletions lib/cesium_cup_web/live/home_live/index.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,17 @@
else
"border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"
end} whitespace-nowrap py-4 px-1 border-b-4 font-medium text-md") %>
<%= live_patch("Knockout Stage", to: "?tab=knockout_stage", class: "#{if @tab == "knockout_stage" do
"border-quinary text-gray-900"
else
"border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"
end} whitespace-nowrap py-4 px-1 border-b-4 font-medium text-md") %>
<%= for round <- @elimination_rounds do %>
<%= live_patch(round.name, to: "?tab=#{round.name}", class: "#{if @tab == round.name do
"border-quinary text-gray-900"
else
"border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"
end} whitespace-nowrap py-4 px-1 border-b-4 font-medium text-md") %>
end} show lg:hidden whitespace-nowrap py-4 px-1 border-b-4 font-medium text-md") %>
<% end %>
</nav>
</div>
Expand Down Expand Up @@ -197,52 +202,58 @@
</div>
</div>
<% else %>
<div class="flex flex-col my-10">
<div class="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div class="inline-block min-w-full py-2 align-middle md:px-6 lg:px-8">
<div class="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
<table class="min-w-full divide-y divide-gray-300">
<tbody class="divide-y divide-gray-200 bg-white">
<%= for match <- Enum.sort(@selected_elimination_round.matches, &(Date.compare(&1.date, &2.date) in [:lt, :eq])) do %>
<tr>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm text-gray-500 sm:pl-6">
<%= display_date(match.date) %>
</td>
<td class="whitespace-nowrap py-4 pl-20 text-sm font-medium text-gray-900 text-left">
<%= if match.home_team_id do %>
<%= live_redirect to: Routes.team_show_path(@socket, :show, get_team(match.home_team_id)) do %>
<%= get_team(match.home_team_id).name %>
<% end %>
<% else %>
TBD
<% end %>
</td>
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500 text-center">
<%= live_redirect to: Routes.match_show_path(@socket, :show, match) do %>
<%= if match.state != :upcoming do %>
<%= get_home_team_score(match.id) %> - <%= get_away_team_score(match.id) %>
<%= if @tab != "knockout_stage" do %>
<div class="show lg:hidden flex flex-col my-10">
<div class="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div class="inline-block min-w-full py-2 align-middle md:px-6 lg:px-8">
<div class="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
<table class="min-w-full divide-y divide-gray-300">
<tbody class="divide-y divide-gray-200 bg-white">
<%= for match <- Enum.sort(@selected_elimination_round.matches, &(Date.compare(&1.date, &2.date) in [:lt, :eq])) do %>
<tr>
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm text-gray-500 sm:pl-6">
<%= display_date(match.date) %>
</td>
<td class="whitespace-nowrap py-4 pl-20 text-sm font-medium text-gray-900 text-left">
<%= if match.home_team_id do %>
<%= live_redirect to: Routes.team_show_path(@socket, :show, get_team(match.home_team_id)) do %>
<%= get_team(match.home_team_id).name %>
<% end %>
<% else %>
<%= display_time(match.date) %>
TBD
<% end %>
<% end %>
</td>
<td class="whitespace-nowrap py-4 pr-20 text-sm font-medium text-gray-900 text-right">
<%= if match.away_team_id do %>
<%= live_redirect to: Routes.team_show_path(@socket, :show, get_team(match.away_team_id)) do %>
<%= get_team(match.away_team_id).name %>
</td>
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500 text-center">
<%= live_redirect to: Routes.match_show_path(@socket, :show, match) do %>
<%= if match.state != :upcoming do %>
<%= get_home_team_score(match.id) %> - <%= get_away_team_score(match.id) %>
<% else %>
<%= display_time(match.date) %>
<% end %>
<% end %>
<% else %>
TBD
<% end %>
</td>
</tr>
<% end %>
</tbody>
</table>
</td>
<td class="whitespace-nowrap py-4 pr-20 text-sm font-medium text-gray-900 text-right">
<%= if match.away_team_id do %>
<%= live_redirect to: Routes.team_show_path(@socket, :show, get_team(match.away_team_id)) do %>
<%= get_team(match.away_team_id).name %>
<% end %>
<% else %>
TBD
<% end %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<% else %>
<div class="my-4">
<.live_component module={CesiumCupWeb.Components.Bracket} id="bracket" index={0} current_round={length(@elimination_rounds)} number_rounds={length(@elimination_rounds)} matches={list_knockout_stage_matches()} />
</div>
<% end %>
<% end %>

<div>
Expand Down
6 changes: 6 additions & 0 deletions lib/cesium_cup_web/live/match_live/form_component.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@
<%= error_tag(f, :group_id) %>
</li>

<li>
<%= label(f, :elimination_round_index) %>
<%= number_input(f, :elimination_round_index) %>
<%= error_tag(f, :elimination_round_index) %>
</li>

<li>
<%= label(f, :elimination_round_id) %>
<%= select(f, :elimination_round_id, Enum.map(@elimination_rounds, &{&1.name, &1.id}), prompt: "Choose a Elimination Round", selected: f.data.group_id) %>
Expand Down
2 changes: 2 additions & 0 deletions priv/repo/migrations/20221210233254_create_matches.exs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ defmodule CesiumCup.Repo.Migrations.CreateMatches do
add :home_team_id, references(:teams, on_delete: :nothing, type: :binary_id)
add :away_team_id, references(:teams, on_delete: :nothing, type: :binary_id)

add :elimination_round_index, :integer

add :elimination_round_id,
references(:elimination_rounds, on_delete: :nothing, type: :binary_id)

Expand Down
7 changes: 7 additions & 0 deletions priv/repo/seeds/schedule.exs
Original file line number Diff line number Diff line change
Expand Up @@ -221,36 +221,43 @@ defmodule CesiumCup.Repo.Seeds.Schedule do
%{
date: ~N[2023-12-02 15:00:00],
state: :upcoming,
elimination_round_index: 3,
elimination_round_id: get_elimination_round_id("Quarter-Finals")
},
%{
date: ~N[2023-12-03 15:00:00],
state: :upcoming,
elimination_round_index: 4,
elimination_round_id: get_elimination_round_id("Quarter-Finals")
},
%{
date: ~N[2023-12-04 15:00:00],
state: :upcoming,
elimination_round_index: 5,
elimination_round_id: get_elimination_round_id("Quarter-Finals")
},
%{
date: ~N[2023-12-05 15:00:00],
state: :upcoming,
elimination_round_index: 6,
elimination_round_id: get_elimination_round_id("Quarter-Finals")
},
%{
date: ~N[2023-12-06 16:00:00],
state: :upcoming,
elimination_round_index: 1,
elimination_round_id: get_elimination_round_id("Semi-Finals")
},
%{
date: ~N[2023-12-07 16:00:00],
state: :upcoming,
elimination_round_index: 2,
elimination_round_id: get_elimination_round_id("Semi-Finals")
},
%{
date: ~N[2023-12-10 20:00:00],
state: :upcoming,
elimination_round_index: 0,
elimination_round_id: get_elimination_round_id("Final")
}
]
Expand Down
Loading