From 8e4bcf7abdd0b8dbc1fc6f6a9b3baf670eb49646 Mon Sep 17 00:00:00 2001 From: shibao Date: Mon, 7 Feb 2022 23:58:29 -0500 Subject: [PATCH] add deletion check for containers --- CONTRIBUTING.md | 2 + lib/cannery/containers.ex | 39 ++++++++++++++++++-- lib/cannery_web/live/container_live/index.ex | 31 ++++++++++++++-- lib/cannery_web/live/container_live/show.ex | 20 +++++++++- lib/cannery_web/views/error_helpers.ex | 7 +++- priv/gettext/errors.pot | 8 +++- 6 files changed, 96 insertions(+), 11 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e566eac6..b2223480 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,6 +27,8 @@ If you're multilingual, this project can use your translations! Visit - Typespec arguments can be named like `@spec function(arg_name :: type()) :: return_type()`. Please use these for generic types, such as `map()` when the input data isn't immediately obvious. + - Please define all typespecs for a function together in one place, instead of + each function. - When making new models, please take inspiration from the existing models in regards to layout of sections, typespec design, and formatting. - With Elixir convention, for methods that raise on error please name them like diff --git a/lib/cannery/containers.ex b/lib/cannery/containers.ex index 88618e38..18e3ad12 100644 --- a/lib/cannery/containers.ex +++ b/lib/cannery/containers.ex @@ -3,8 +3,9 @@ defmodule Cannery.Containers do The Containers context. """ + import CanneryWeb.Gettext import Ecto.Query, warn: false - alias Cannery.{Accounts.User, Repo, Tags.Tag} + alias Cannery.{Accounts.User, Ammo.AmmoGroup, Repo, Tags.Tag} alias Cannery.Containers.{Container, ContainerTag} alias Ecto.Changeset @@ -88,7 +89,36 @@ defmodule Cannery.Containers do """ @spec delete_container(Container.t()) :: {:ok, Container.t()} | {:error, Changeset.t(Container.t())} - def delete_container(container), do: container |> Repo.delete() + def delete_container(container) do + Repo.one( + from ag in AmmoGroup, + where: ag.container_id == ^container.id, + select: count(ag.id) + ) + |> case do + 0 -> + container |> Repo.delete() + + amount -> + error_string = + dngettext( + "errors", + "There is still %{amount} ammo group in this container!", + "There are still %{amount} ammo groups in this container!", + amount + ) + + container + |> change_container() + |> Changeset.add_error( + :ammo_groups, + error_string, + amount: amount, + count: amount + ) + |> Changeset.apply_action(:delete) + end + end @doc """ Deletes a container. @@ -100,7 +130,10 @@ defmodule Cannery.Containers do """ @spec delete_container!(Container.t()) :: Container.t() - def delete_container!(container), do: container |> Repo.delete!() + def delete_container!(container) do + {:ok, container} = container |> delete_container() + container + end @doc """ Returns an `%Changeset{}` for tracking container changes. diff --git a/lib/cannery_web/live/container_live/index.ex b/lib/cannery_web/live/container_live/index.ex index 87e9c987..32c1d09f 100644 --- a/lib/cannery_web/live/container_live/index.ex +++ b/lib/cannery_web/live/container_live/index.ex @@ -5,8 +5,7 @@ defmodule CanneryWeb.ContainerLive.Index do use CanneryWeb, :live_view import CanneryWeb.ContainerLive.ContainerCard - alias Cannery.Containers - alias Cannery.Containers.Container + alias Cannery.{Containers, Containers.Container} @impl true def mount(_params, session, socket) do @@ -38,8 +37,32 @@ defmodule CanneryWeb.ContainerLive.Index do @impl true def handle_event("delete", %{"id" => id}, socket) do - Containers.get_container!(id) |> Containers.delete_container!() - {:noreply, socket |> display_containers()} + socket = + socket.assigns.containers + |> Enum.find(fn %{id: container_id} -> id == container_id end) + |> case do + nil -> + socket |> put_flash(:error, "Could not find that container") + + container -> + container + |> Containers.delete_container() + |> case do + {:ok, container} -> + socket + |> put_flash(:info, "#{container.name} has been deleted") + |> display_containers() + + {:error, %{action: :delete, errors: [ammo_groups: _error], valid?: false} = changeset} -> + ammo_groups_error = changeset |> changeset_errors(:ammo_groups) |> Enum.join(", ") + socket |> put_flash(:error, "Could not delete container: #{ammo_groups_error}") + + {:error, changeset} -> + socket |> put_flash(:error, changeset |> changeset_errors()) + end + end + + {:noreply, socket} end defp display_containers(%{assigns: %{current_user: current_user}} = socket) do diff --git a/lib/cannery_web/live/container_live/show.ex b/lib/cannery_web/live/container_live/show.ex index bf1c4c46..d16cecce 100644 --- a/lib/cannery_web/live/container_live/show.ex +++ b/lib/cannery_web/live/container_live/show.ex @@ -26,8 +26,24 @@ defmodule CanneryWeb.ContainerLive.Show do @impl true def handle_event("delete", _, socket) do - socket.assigns.container |> Containers.delete_container!() - {:noreply, socket |> push_redirect(to: Routes.container_index_path(socket, :index))} + socket = + socket.assigns.container + |> Containers.delete_container() + |> case do + {:ok, container} -> + socket + |> put_flash(:info, "#{container.name} has been deleted") + |> push_redirect(to: Routes.container_index_path(socket, :index)) + + {:error, %{action: :delete, errors: [ammo_groups: _error], valid?: false} = changeset} -> + ammo_groups_error = changeset |> changeset_errors(:ammo_groups) |> Enum.join(", ") + socket |> put_flash(:error, "Could not delete container: #{ammo_groups_error}") + + {:error, changeset} -> + socket |> put_flash(:error, changeset |> changeset_errors()) + end + + {:noreply, socket} end defp page_title(:show), do: "Show Container" diff --git a/lib/cannery_web/views/error_helpers.ex b/lib/cannery_web/views/error_helpers.ex index ce054c20..191fd50c 100644 --- a/lib/cannery_web/views/error_helpers.ex +++ b/lib/cannery_web/views/error_helpers.ex @@ -55,9 +55,10 @@ defmodule CanneryWeb.ErrorHelpers do end @doc """ - Displays all errors from a changeset + Displays all errors from a changeset, or just for a single key """ @spec changeset_errors(Changeset.t()) :: String.t() + @spec changeset_errors(Changeset.t(), key :: atom()) :: [String.t()] | nil def changeset_errors(changeset) do changeset |> changeset_error_map() @@ -66,6 +67,10 @@ defmodule CanneryWeb.ErrorHelpers do end) end + def changeset_errors(changeset, key) do + changeset |> changeset_error_map() |> Map.get(key) + end + @doc """ Displays all errors from a changeset in a key value map """ diff --git a/priv/gettext/errors.pot b/priv/gettext/errors.pot index 39a220be..7b040f87 100644 --- a/priv/gettext/errors.pot +++ b/priv/gettext/errors.pot @@ -7,7 +7,6 @@ ## Run `mix gettext.extract` to bring this file up to ## date. Leave `msgstr`s empty as changing them here has no ## effect: edit them in PO (`.po`) files instead. - ## From Ecto.Changeset.cast/4 msgid "can't be blank" msgstr "" @@ -93,3 +92,10 @@ msgstr "" msgid "must be equal to %{number}" msgstr "" + +#, elixir-format, ex-autogen +#: lib/cannery/containers.ex:104 +msgid "There is still %{amount} ammo group in this container!" +msgid_plural "There are still %{amount} ammo groups in this container!" +msgstr[0] "" +msgstr[1] ""