From d7be0407a3e62a33fa3a739b3e0b17b98cb3ac6f Mon Sep 17 00:00:00 2001 From: Felicio Date: Tue, 15 Aug 2023 18:13:30 +0100 Subject: [PATCH] feat: add knockout stage bracket --- lib/cesium_cup/tournament/match.ex | 3 +- lib/cesium_cup_web/components/bracket.ex | 68 ++++++++++++++ lib/cesium_cup_web/live/home_live/index.ex | 5 + .../live/home_live/index.html.heex | 93 +++++++++++-------- .../live/match_live/form_component.html.heex | 6 ++ .../20221210233254_create_matches.exs | 2 + priv/repo/seeds/schedule.exs | 7 ++ 7 files changed, 142 insertions(+), 42 deletions(-) create mode 100644 lib/cesium_cup_web/components/bracket.ex diff --git a/lib/cesium_cup/tournament/match.ex b/lib/cesium_cup/tournament/match.ex index 8801a82..5da6397 100644 --- a/lib/cesium_cup/tournament/match.ex +++ b/lib/cesium_cup/tournament/match.ex @@ -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 @@ -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 diff --git a/lib/cesium_cup_web/components/bracket.ex b/lib/cesium_cup_web/components/bracket.ex new file mode 100644 index 0000000..ad4ca8f --- /dev/null +++ b/lib/cesium_cup_web/components/bracket.ex @@ -0,0 +1,68 @@ +defmodule CesiumCupWeb.Components.Bracket do + @moduledoc """ + """ + use Phoenix.LiveComponent + + alias CesiumCup.Tournament + alias CesiumCupWeb.Router.Helpers, as: Routes + + def render(assigns) do + ~H""" +
+ <%= if @current_round > 0 do %> +
+
+ <.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} /> +
+ <% match = Enum.find(@matches, &(&1.elimination_round_index == @index)) %> + <%= live_redirect to: Routes.match_show_path(@socket, :show, match) do %> +
+
+
+

+ <%= if match.home_team do %> + <%= match.home_team.name %> + <% else %> + TBD + <% end %> +

+ +

+ <%= if not is_nil(match.home_team_id) and match.state != :upcoming do %> + <%= get_home_team_score(match.id) %> + <% end %> +

+
+
+

+ <%= if match.away_team do %> + <%= match.away_team.name %> + <% else %> + TBD + <% end %> +

+ +

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

+
+
+
+ <% end %> +
+ <% end %> +
+ """ + 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 diff --git a/lib/cesium_cup_web/live/home_live/index.ex b/lib/cesium_cup_web/live/home_live/index.ex index a6aee01..705dec8 100644 --- a/lib/cesium_cup_web/live/home_live/index.ex +++ b/lib/cesium_cup_web/live/home_live/index.ex @@ -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 diff --git a/lib/cesium_cup_web/live/home_live/index.html.heex b/lib/cesium_cup_web/live/home_live/index.html.heex index 5f5c1a4..a6c45e2 100644 --- a/lib/cesium_cup_web/live/home_live/index.html.heex +++ b/lib/cesium_cup_web/live/home_live/index.html.heex @@ -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 %> @@ -197,52 +202,58 @@ <% else %> -
-
-
-
- - - <%= for match <- Enum.sort(@selected_elimination_round.matches, &(Date.compare(&1.date, &2.date) in [:lt, :eq])) do %> - - - - + + + <% end %> + +
- <%= display_date(match.date) %> - - <%= 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 %> - - <%= 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 %> +
+
+
+
+ + + <%= for match <- Enum.sort(@selected_elimination_round.matches, &(Date.compare(&1.date, &2.date) in [:lt, :eq])) do %> + + + - + - - <% end %> - -
+ <%= display_date(match.date) %> + + <%= 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 %> - - <%= 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 %> + + <%= 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 %> -
+
+ <%= 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 %> +
+
- + <% else %> +
+ <.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()} /> +
+ <% end %> <% end %>
diff --git a/lib/cesium_cup_web/live/match_live/form_component.html.heex b/lib/cesium_cup_web/live/match_live/form_component.html.heex index d807511..da9c58b 100644 --- a/lib/cesium_cup_web/live/match_live/form_component.html.heex +++ b/lib/cesium_cup_web/live/match_live/form_component.html.heex @@ -27,6 +27,12 @@ <%= error_tag(f, :group_id) %> +
  • + <%= label(f, :elimination_round_index) %> + <%= number_input(f, :elimination_round_index) %> + <%= error_tag(f, :elimination_round_index) %> +
  • +
  • <%= 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) %> diff --git a/priv/repo/migrations/20221210233254_create_matches.exs b/priv/repo/migrations/20221210233254_create_matches.exs index c4a58ac..03e2c7b 100644 --- a/priv/repo/migrations/20221210233254_create_matches.exs +++ b/priv/repo/migrations/20221210233254_create_matches.exs @@ -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) diff --git a/priv/repo/seeds/schedule.exs b/priv/repo/seeds/schedule.exs index c9cb4cf..ce55c6e 100644 --- a/priv/repo/seeds/schedule.exs +++ b/priv/repo/seeds/schedule.exs @@ -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") } ]