From 7527087afae049b005345519e472d8ec877e44f6 Mon Sep 17 00:00:00 2001 From: Axel Clark Date: Mon, 8 Apr 2024 20:55:44 -0700 Subject: [PATCH] Covert in season form to live component * Add test for in season draft pick form component * Reorder queues and send league email * See #1299 * See #1229 --- lib/ex338/fantasy_team_authorizer.ex | 12 ++ lib/ex338/in_season_draft_picks.ex | 7 + .../in_season_draft_pick_form_component.ex | 83 ++++++++++++ lib/ex338_web/live/championship_live/show.ex | 82 ++++++++++-- lib/ex338_web/router.ex | 5 + .../live/championship_live/show_test.exs | 125 +++++++++++++++++- 6 files changed, 303 insertions(+), 11 deletions(-) create mode 100644 lib/ex338_web/live/championship_live/in_season_draft_pick_form_component.ex diff --git a/lib/ex338/fantasy_team_authorizer.ex b/lib/ex338/fantasy_team_authorizer.ex index 1639820c..9e8f2fd9 100644 --- a/lib/ex338/fantasy_team_authorizer.ex +++ b/lib/ex338/fantasy_team_authorizer.ex @@ -22,6 +22,18 @@ defmodule Ex338.FantasyTeamAuthorizer do end end + def authorize( + :edit_in_season_draft_pick, + %User{} = user, + %InSeasonDraftPick{} = in_season_draft_pick + ) do + if owner?(user.id, in_season_draft_pick) do + :ok + else + {:error, :not_authorized} + end + end + defp owner?(user_id, %DraftPick{} = draft_pick) do draft_pick = Repo.preload(draft_pick, fantasy_team: :owners) owners = draft_pick.fantasy_team.owners diff --git a/lib/ex338/in_season_draft_picks.ex b/lib/ex338/in_season_draft_picks.ex index d0727262..d1ef8c63 100644 --- a/lib/ex338/in_season_draft_picks.ex +++ b/lib/ex338/in_season_draft_picks.ex @@ -110,6 +110,13 @@ defmodule Ex338.InSeasonDraftPicks do Phoenix.PubSub.subscribe(Ex338.PubSub, @topic) end + def change_in_season_draft_pick_as_owner( + %InSeasonDraftPick{} = in_season_draft_pick, + attrs \\ %{} + ) do + InSeasonDraftPick.owner_changeset(in_season_draft_pick, attrs) + end + ## Helpers ## draft_player diff --git a/lib/ex338_web/live/championship_live/in_season_draft_pick_form_component.ex b/lib/ex338_web/live/championship_live/in_season_draft_pick_form_component.ex new file mode 100644 index 00000000..c9dee734 --- /dev/null +++ b/lib/ex338_web/live/championship_live/in_season_draft_pick_form_component.ex @@ -0,0 +1,83 @@ +defmodule Ex338Web.ChampionshipLive.InSeasonDraftPickFormComponent do + @moduledoc false + use Ex338Web, :live_component + + alias Ex338.DraftQueues + alias Ex338.InSeasonDraftPicks + alias Ex338Web.InSeasonDraftPickNotifier + + @impl true + def render(assigns) do + ~H""" +
+ <.header> + Submit <%= @in_season_draft_pick.championship.title %> Draft Pick + <:subtitle> + Please make a selection for <%= @in_season_draft_pick.draft_pick_asset.fantasy_team.team_name %>'s + round <%= @in_season_draft_pick.position %> pick. + + + + <.simple_form + for={@form} + id="in-season-draft-pick-form" + phx-target={@myself} + phx-change="validate" + phx-submit="save" + class="max-w-lg" + > + <.input + field={@form[:drafted_player_id]} + label="Player to Draft" + type="select" + options={format_players_for_select(@available_fantasy_players)} + prompt="Select a fantasy player" + /> + <:actions> + <.button phx-disable-with="Submitting...">Submit Draft Pick + + +
+ """ + end + + @impl true + def update(%{in_season_draft_pick: in_season_draft_pick} = assigns, socket) do + changeset = InSeasonDraftPicks.change_in_season_draft_pick_as_owner(in_season_draft_pick) + + {:ok, + socket + |> assign(assigns) + |> assign_form(changeset)} + end + + @impl true + def handle_event("validate", %{"in_season_draft_pick" => in_season_draft_pick_params}, socket) do + changeset = + socket.assigns.in_season_draft_pick + |> InSeasonDraftPicks.change_in_season_draft_pick_as_owner(in_season_draft_pick_params) + |> Map.put(:action, :validate) + + {:noreply, assign_form(socket, changeset)} + end + + def handle_event("save", %{"in_season_draft_pick" => in_season_draft_pick_params}, socket) do + case InSeasonDraftPicks.draft_player( + socket.assigns.in_season_draft_pick, + in_season_draft_pick_params + ) do + {:ok, %{update_pick: in_season_draft_pick}} -> + DraftQueues.reorder_for_league(socket.assigns.fantasy_league.id) + InSeasonDraftPickNotifier.send_update(in_season_draft_pick) + + {:noreply, push_patch(socket, to: socket.assigns.patch)} + + {:error, _multi_action, %Ecto.Changeset{} = changeset, _} -> + {:noreply, assign_form(socket, changeset)} + end + end + + defp assign_form(socket, %Ecto.Changeset{} = changeset) do + assign(socket, :form, to_form(changeset)) + end +end diff --git a/lib/ex338_web/live/championship_live/show.ex b/lib/ex338_web/live/championship_live/show.ex index 4dc46261..ac4328b0 100644 --- a/lib/ex338_web/live/championship_live/show.ex +++ b/lib/ex338_web/live/championship_live/show.ex @@ -8,10 +8,14 @@ defmodule Ex338Web.ChampionshipLive.Show do alias Ex338.Chats.Message alias Ex338.Events alias Ex338.FantasyLeagues + alias Ex338.FantasyTeamAuthorizer alias Ex338.InSeasonDraftPicks + alias Ex338.InSeasonDraftPicks.InSeasonDraftPick alias Ex338Web.ChampionshipLive.ChatComponent alias Ex338Web.Presence + require Logger + @impl true def mount(_params, _session, socket) do if connected?(socket) do @@ -26,19 +30,18 @@ defmodule Ex338Web.ChampionshipLive.Show do def handle_params(params, _session, socket) do %{"fantasy_league_id" => fantasy_league_id, "championship_id" => championship_id} = params + fantasy_league = FantasyLeagues.get(fantasy_league_id) + + championship = + Championships.get_championship_by_league(championship_id, fantasy_league_id) + socket = socket - |> assign( - :championship, - Championships.get_championship_by_league( - championship_id, - fantasy_league_id - ) - ) - |> assign(:fantasy_league, FantasyLeagues.get(fantasy_league_id)) + |> assign(:fantasy_league, fantasy_league) + |> assign(:championship, championship) |> maybe_assign_chat() - {:noreply, socket} + {:noreply, apply_action(socket, socket.assigns.live_action, params)} end defp maybe_assign_chat(socket) do @@ -83,6 +86,39 @@ defmodule Ex338Web.ChampionshipLive.Show do } end + defp apply_action(socket, :in_season_draft_pick_edit, params) do + with pick_id when not is_nil(pick_id) <- params["in_season_draft_pick_id"], + %InSeasonDraftPick{} = in_season_draft_pick <- + InSeasonDraftPicks.pick_with_assocs(pick_id), + :ok <- authorize_pick(socket, in_season_draft_pick) do + available_fantasy_players = InSeasonDraftPicks.available_players(in_season_draft_pick) + + socket + |> assign(:in_season_draft_pick, in_season_draft_pick) + |> assign(:available_fantasy_players, available_fantasy_players) + else + _nil_or_error -> + %{fantasy_league: fantasy_league, championship: championship} = socket.assigns + push_navigate(socket, to: show_path(fantasy_league, championship)) + end + end + + defp apply_action(socket, :show, _params) do + socket + end + + defp show_path(fantasy_league, championship) do + ~p"/fantasy_leagues/#{fantasy_league}/championships/#{championship}" + end + + defp authorize_pick(socket, in_season_draft_pick) do + FantasyTeamAuthorizer.authorize( + :edit_in_season_draft_pick, + socket.assigns.current_user, + in_season_draft_pick + ) + end + @impl true def handle_info(:refresh, socket) do championship = Championships.update_next_in_season_pick(socket.assigns.championship) @@ -143,6 +179,11 @@ defmodule Ex338Web.ChampionshipLive.Show do {:noreply, assign(socket, users: users)} end + def handle_info(message, socket) do + Logger.info("Unhandled message: #{inspect(message)}") + {:noreply, socket} + end + # Implementations defp schedule_refresh do @@ -335,6 +376,7 @@ defmodule Ex338Web.ChampionshipLive.Show do championship={@championship} socket={@socket} current_user={@current_user} + fantasy_league={@fantasy_league} /> <% end %> @@ -355,6 +397,21 @@ defmodule Ex338Web.ChampionshipLive.Show do <% end %> + <.modal + :if={@live_action == :in_season_draft_pick_edit} + id="in-season-draft-pick-modal" + show + on_cancel={JS.patch(show_path(@fantasy_league, @championship))} + > + <.live_component + module={Ex338Web.ChampionshipLive.InSeasonDraftPickFormComponent} + id={@in_season_draft_pick.id} + fantasy_league={@fantasy_league} + in_season_draft_pick={@in_season_draft_pick} + available_fantasy_players={@available_fantasy_players} + patch={show_path(@fantasy_league, @championship)} + /> + """ end @@ -570,7 +627,12 @@ defmodule Ex338Web.ChampionshipLive.Show do <%= pick.drafted_player.player_name %> <% else %> <%= if pick.available_to_pick? && (owner?(@current_user, pick) || admin?(@current_user)) do %> - <.link href={~p"/in_season_draft_picks/#{pick}/edit"} class="text-indigo-700"> + <.link + patch={ + ~p"/fantasy_leagues/#{@fantasy_league}/championships/#{@championship}/in_season_draft_picks/#{pick}/edit" + } + class="text-indigo-700" + > Submit Pick <% end %> diff --git a/lib/ex338_web/router.ex b/lib/ex338_web/router.ex index 9ee04235..45b45aa1 100644 --- a/lib/ex338_web/router.ex +++ b/lib/ex338_web/router.ex @@ -87,6 +87,11 @@ defmodule Ex338Web.Router do scope "/fantasy_leagues/:fantasy_league_id" do live "/draft_picks", DraftPickLive.Index, :index live "/championships", ChampionshipLive.Index, :index + + live "/championships/:championship_id/in_season_draft_picks/:in_season_draft_pick_id/edit", + ChampionshipLive.Show, + :in_season_draft_pick_edit + live "/championships/:championship_id", ChampionshipLive.Show, :show end end diff --git a/test/ex338_web/live/championship_live/show_test.exs b/test/ex338_web/live/championship_live/show_test.exs index da991b92..0924e842 100644 --- a/test/ex338_web/live/championship_live/show_test.exs +++ b/test/ex338_web/live/championship_live/show_test.exs @@ -2,8 +2,10 @@ defmodule Ex338Web.ChampionshipLive.ShowTest do use Ex338Web.ConnCase import Phoenix.LiveViewTest + import Swoosh.TestAssertions alias Ex338.CalendarAssistant + alias Ex338.DraftQueues.DraftQueue alias Ex338.InSeasonDraftPicks describe "show/2" do @@ -265,6 +267,7 @@ defmodule Ex338Web.ChampionshipLive.ShowTest do insert(:championship, category: "overall", in_season_draft: true, sports_league: sport) team_a = insert(:fantasy_team, fantasy_league: league) + insert(:owner, user: user, fantasy_team: team_a) pick1 = insert(:fantasy_player, sports_league: sport, draft_pick: true, player_name: "KD Pick #1") @@ -293,8 +296,8 @@ defmodule Ex338Web.ChampionshipLive.ShowTest do ) another_user = insert(:user) - team_b = insert(:fantasy_team, fantasy_league: league) + insert(:owner, user: another_user, fantasy_team: team_b) pick2 = insert(:fantasy_player, sports_league: sport, draft_pick: true, player_name: "KD Pick #2") @@ -366,5 +369,125 @@ defmodule Ex338Web.ChampionshipLive.ShowTest do assert has_element?(view, "p", draft_chat_message) end + + test "shows draft for overall championship with form to submit a pick", %{ + conn: conn, + user: user + } do + league = insert(:fantasy_league) + sport = insert(:sports_league) + insert(:league_sport, fantasy_league: league, sports_league: sport) + + championship = + insert(:championship, category: "overall", in_season_draft: true, sports_league: sport) + + team_a = insert(:fantasy_team, fantasy_league: league) + insert(:owner, user: user, fantasy_team: team_a) + + pick1 = + insert(:fantasy_player, sports_league: sport, draft_pick: true, player_name: "KD Pick #1") + + pick_asset1 = insert(:roster_position, fantasy_team: team_a, fantasy_player: pick1) + + horse = + insert(:fantasy_player, sports_league: sport, draft_pick: false, player_name: "My Horse") + + drafted_queue = insert(:draft_queue, fantasy_team: team_a, fantasy_player: horse) + + in_season_draft_pick = + insert( + :in_season_draft_pick, + draft_pick_asset: pick_asset1, + championship: championship, + position: 1 + ) + + chat = insert(:chat, room_name: "#{championship.title}:#{league.id}") + + insert(:fantasy_league_draft, + fantasy_league: league, + championship: championship, + chat: chat + ) + + team2 = insert(:fantasy_team, fantasy_league: league) + + unavailable_queue = + insert(:draft_queue, fantasy_team: team2, fantasy_player: horse, order: 1) + + horse2 = + insert(:fantasy_player, sports_league: sport, draft_pick: false, player_name: "My Horse") + + reordered_queue = + insert(:draft_queue, fantasy_team: team2, fantasy_player: horse2, order: 2) + + {:ok, view, _html} = + live(conn, ~p"/fantasy_leagues/#{league.id}/championships/#{championship.id}") + + assert has_element?(view, "h3", "Draft") + refute has_element?(view, "td", horse.player_name) + + view + |> element("a", "Submit Pick") + |> render_click() + + assert_patch( + view, + ~p"/fantasy_leagues/#{league.id}/championships/#{championship.id}/in_season_draft_picks/#{in_season_draft_pick}/edit" + ) + + view + |> form("#in-season-draft-pick-form", %{ + in_season_draft_pick: %{drafted_player_id: nil} + }) + |> render_change() =~ "can't be blank" + + view + |> form("#in-season-draft-pick-form", %{ + in_season_draft_pick: %{drafted_player_id: nil} + }) + |> render_submit() =~ "can't be blank" + + view + |> form("#in-season-draft-pick-form", %{ + in_season_draft_pick: %{drafted_player_id: horse.id} + }) + |> render_submit() + + assert has_element?(view, "td", horse.player_name) + + assert Repo.get!(DraftQueue, unavailable_queue.id).status == :unavailable + assert Repo.get!(DraftQueue, drafted_queue.id).status == :drafted + assert Repo.get!(DraftQueue, reordered_queue.id).status == :pending + assert Repo.get!(DraftQueue, reordered_queue.id).order == 1 + + assert_email_sent(fn email -> + assert email.subject =~ "338 Draft" + end) + end + + test "redirects to show if user is not owner", %{conn: conn} do + league = insert(:fantasy_league) + team = insert(:fantasy_team, team_name: "Brown", fantasy_league: league) + + sport = insert(:sports_league) + + championship = + insert(:championship, category: "overall", in_season_draft: true, sports_league: sport) + + player = insert(:fantasy_player, draft_pick: true, sports_league: sport) + pick_asset = insert(:roster_position, fantasy_team: team, fantasy_player: player) + + in_season_draft_pick = + insert(:in_season_draft_pick, position: 1, draft_pick_asset: pick_asset) + + {:error, {:live_redirect, %{to: path}}} = + live( + conn, + ~p"/fantasy_leagues/#{league.id}/championships/#{championship.id}/in_season_draft_picks/#{in_season_draft_pick}/edit" + ) + + assert path == ~p"/fantasy_leagues/#{league.id}/championships/#{championship.id}" + end end end