forked from shibao/cannery
		
	allow filtering ammo types when creating new packs and fix some form errors not displaying on create
This commit is contained in:
		@@ -959,25 +959,79 @@ defmodule Cannery.Ammo do
 | 
			
		||||
 | 
			
		||||
  defp do_create_packs(
 | 
			
		||||
         %{"type_id" => type_id, "container_id" => container_id} = attrs,
 | 
			
		||||
         _multiplier,
 | 
			
		||||
         multiplier,
 | 
			
		||||
         user
 | 
			
		||||
       )
 | 
			
		||||
       when is_binary(type_id) and is_binary(container_id) do
 | 
			
		||||
    changeset =
 | 
			
		||||
      %Pack{}
 | 
			
		||||
      |> Pack.create_changeset(
 | 
			
		||||
        get_type!(type_id, user),
 | 
			
		||||
        Containers.get_container!(container_id, user),
 | 
			
		||||
        user,
 | 
			
		||||
        attrs
 | 
			
		||||
      )
 | 
			
		||||
      |> Changeset.add_error(:multiplier, dgettext("errors", "Invalid multiplier"))
 | 
			
		||||
 | 
			
		||||
    {:error, changeset}
 | 
			
		||||
    %Pack{}
 | 
			
		||||
    |> Pack.create_changeset(
 | 
			
		||||
      get_type!(type_id, user),
 | 
			
		||||
      Containers.get_container!(container_id, user),
 | 
			
		||||
      user,
 | 
			
		||||
      attrs
 | 
			
		||||
    )
 | 
			
		||||
    |> maybe_add_multiplier_error(multiplier)
 | 
			
		||||
    |> Changeset.apply_action(:insert)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp do_create_packs(invalid_attrs, _multiplier, user) do
 | 
			
		||||
    {:error, %Pack{} |> Pack.create_changeset(nil, nil, user, invalid_attrs)}
 | 
			
		||||
  defp do_create_packs(
 | 
			
		||||
         %{"type_id" => type_id} = attrs,
 | 
			
		||||
         multiplier,
 | 
			
		||||
         user
 | 
			
		||||
       )
 | 
			
		||||
       when is_binary(type_id) do
 | 
			
		||||
    %Pack{}
 | 
			
		||||
    |> Pack.create_changeset(
 | 
			
		||||
      get_type!(type_id, user),
 | 
			
		||||
      nil,
 | 
			
		||||
      user,
 | 
			
		||||
      attrs
 | 
			
		||||
    )
 | 
			
		||||
    |> maybe_add_multiplier_error(multiplier)
 | 
			
		||||
    |> Changeset.apply_action(:insert)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp do_create_packs(
 | 
			
		||||
         %{"container_id" => container_id} = attrs,
 | 
			
		||||
         multiplier,
 | 
			
		||||
         user
 | 
			
		||||
       )
 | 
			
		||||
       when is_binary(container_id) do
 | 
			
		||||
    %Pack{}
 | 
			
		||||
    |> Pack.create_changeset(
 | 
			
		||||
      nil,
 | 
			
		||||
      Containers.get_container!(container_id, user),
 | 
			
		||||
      user,
 | 
			
		||||
      attrs
 | 
			
		||||
    )
 | 
			
		||||
    |> maybe_add_multiplier_error(multiplier)
 | 
			
		||||
    |> Changeset.apply_action(:insert)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp do_create_packs(invalid_attrs, multiplier, user) do
 | 
			
		||||
    %Pack{}
 | 
			
		||||
    |> Pack.create_changeset(nil, nil, user, invalid_attrs)
 | 
			
		||||
    |> maybe_add_multiplier_error(multiplier)
 | 
			
		||||
    |> Changeset.apply_action(:insert)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp maybe_add_multiplier_error(changeset, multiplier)
 | 
			
		||||
       when multiplier >= 1 and
 | 
			
		||||
              multiplier <= @pack_create_limit do
 | 
			
		||||
    changeset
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp maybe_add_multiplier_error(changeset, multiplier) do
 | 
			
		||||
    changeset
 | 
			
		||||
    |> Changeset.add_error(
 | 
			
		||||
      :multiplier,
 | 
			
		||||
      dgettext(
 | 
			
		||||
        "errors",
 | 
			
		||||
        "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}",
 | 
			
		||||
        max: @pack_create_limit,
 | 
			
		||||
        multiplier: multiplier
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @spec preload_pack(Pack.t()) :: Pack.t()
 | 
			
		||||
 
 | 
			
		||||
@@ -70,36 +70,45 @@ defmodule Cannery.Ammo.Pack do
 | 
			
		||||
        ) :: changeset()
 | 
			
		||||
  def create_changeset(
 | 
			
		||||
        pack,
 | 
			
		||||
        %Type{id: type_id},
 | 
			
		||||
        %Container{id: container_id, user_id: user_id},
 | 
			
		||||
        type,
 | 
			
		||||
        container,
 | 
			
		||||
        %User{id: user_id},
 | 
			
		||||
        attrs
 | 
			
		||||
      )
 | 
			
		||||
      when is_binary(type_id) and is_binary(container_id) and is_binary(user_id) do
 | 
			
		||||
      when is_binary(user_id) do
 | 
			
		||||
    type_id =
 | 
			
		||||
      case type do
 | 
			
		||||
        %Type{id: type_id} when is_binary(type_id) ->
 | 
			
		||||
          type_id
 | 
			
		||||
 | 
			
		||||
        _invalid_type ->
 | 
			
		||||
          nil
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
    container_id =
 | 
			
		||||
      case container do
 | 
			
		||||
        %Container{id: container_id, user_id: ^user_id} when is_binary(container_id) ->
 | 
			
		||||
          container_id
 | 
			
		||||
 | 
			
		||||
        _invalid_container ->
 | 
			
		||||
          nil
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
    pack
 | 
			
		||||
    |> change(type_id: type_id)
 | 
			
		||||
    |> change(user_id: user_id)
 | 
			
		||||
    |> change(container_id: container_id)
 | 
			
		||||
    |> cast(attrs, [:count, :price_paid, :notes, :staged, :purchased_on, :lot_number])
 | 
			
		||||
    |> change(user_id: user_id)
 | 
			
		||||
    |> cast(attrs, [:count, :lot_number, :notes, :price_paid, :purchased_on, :staged])
 | 
			
		||||
    |> validate_required(:type_id, message: dgettext("errors", "Please select a valid type"))
 | 
			
		||||
    |> validate_required(:container_id,
 | 
			
		||||
      message: dgettext("errors", "Please select a valid container")
 | 
			
		||||
    )
 | 
			
		||||
    |> validate_number(:count, greater_than: 0)
 | 
			
		||||
    |> validate_number(:price_paid, greater_than_or_equal_to: 0)
 | 
			
		||||
    |> validate_length(:lot_number, max: 255)
 | 
			
		||||
    |> validate_required([:count, :staged, :purchased_on, :type_id, :container_id, :user_id])
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @doc """
 | 
			
		||||
  Invalid changeset, used to prompt user to select type and container
 | 
			
		||||
  """
 | 
			
		||||
  def create_changeset(pack, _invalid_type, _invalid_container, _invalid_user, attrs) do
 | 
			
		||||
    pack
 | 
			
		||||
    |> cast(attrs, [:type_id, :container_id])
 | 
			
		||||
    |> validate_required([:type_id, :container_id])
 | 
			
		||||
    |> validate_number(:count, greater_than: 0)
 | 
			
		||||
    |> validate_number(:price_paid, greater_than_or_equal_to: 0)
 | 
			
		||||
    |> validate_length(:lot_number, max: 255)
 | 
			
		||||
    |> add_error(:invalid, dgettext("errors", "Please select a type and container"))
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @doc false
 | 
			
		||||
  @spec update_changeset(t() | new_pack(), attrs :: map(), User.t()) :: changeset()
 | 
			
		||||
  def update_changeset(pack, attrs, user) do
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,11 @@ defmodule CanneryWeb.PackLive.FormComponent do
 | 
			
		||||
  alias Ecto.Changeset
 | 
			
		||||
  alias Phoenix.LiveView.Socket
 | 
			
		||||
 | 
			
		||||
  @pack_create_limit 10_000
 | 
			
		||||
  @impl true
 | 
			
		||||
  @spec mount(Socket.t()) :: {:ok, Socket.t()}
 | 
			
		||||
  def mount(socket) do
 | 
			
		||||
    {:ok, socket |> assign(:class, :all)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  @spec update(
 | 
			
		||||
@@ -24,7 +28,6 @@ defmodule CanneryWeb.PackLive.FormComponent do
 | 
			
		||||
  def update(%{assigns: %{current_user: current_user}} = socket) do
 | 
			
		||||
    socket =
 | 
			
		||||
      socket
 | 
			
		||||
      |> assign(:pack_create_limit, @pack_create_limit)
 | 
			
		||||
      |> assign(:types, Ammo.list_types(current_user))
 | 
			
		||||
      |> assign_new(:containers, fn -> Containers.list_containers(current_user) end)
 | 
			
		||||
 | 
			
		||||
@@ -33,7 +36,20 @@ defmodule CanneryWeb.PackLive.FormComponent do
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_event("validate", %{"pack" => pack_params}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign_changeset(pack_params, :validate)}
 | 
			
		||||
    matched_class =
 | 
			
		||||
      case pack_params["class"] do
 | 
			
		||||
        "rifle" -> :rifle
 | 
			
		||||
        "shotgun" -> :shotgun
 | 
			
		||||
        "pistol" -> :pistol
 | 
			
		||||
        _other -> :all
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
    socket =
 | 
			
		||||
      socket
 | 
			
		||||
      |> assign_changeset(pack_params, :validate)
 | 
			
		||||
      |> assign(:class, matched_class)
 | 
			
		||||
 | 
			
		||||
    {:noreply, socket}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event(
 | 
			
		||||
@@ -51,11 +67,18 @@ defmodule CanneryWeb.PackLive.FormComponent do
 | 
			
		||||
    containers |> Enum.map(fn %{id: id, name: name} -> {name, id} end)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @spec type_options([Type.t()]) :: [{String.t(), Type.id()}]
 | 
			
		||||
  defp type_options(types) do
 | 
			
		||||
  @spec type_options([Type.t()], Type.class() | :all) ::
 | 
			
		||||
          [{String.t(), Type.id()}]
 | 
			
		||||
  defp type_options(types, :all) do
 | 
			
		||||
    types |> Enum.map(fn %{id: id, name: name} -> {name, id} end)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp type_options(types, selected_class) do
 | 
			
		||||
    types
 | 
			
		||||
    |> Enum.filter(fn %{class: class} -> class == selected_class end)
 | 
			
		||||
    |> Enum.map(fn %{id: id, name: name} -> {name, id} end)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # Save Helpers
 | 
			
		||||
 | 
			
		||||
  defp assign_changeset(
 | 
			
		||||
@@ -126,53 +149,15 @@ defmodule CanneryWeb.PackLive.FormComponent do
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp save_pack(
 | 
			
		||||
         %{assigns: %{changeset: changeset}} = socket,
 | 
			
		||||
         %{assigns: %{changeset: changeset, current_user: current_user, return_to: return_to}} =
 | 
			
		||||
           socket,
 | 
			
		||||
         action,
 | 
			
		||||
         %{"multiplier" => multiplier_str} = pack_params
 | 
			
		||||
       )
 | 
			
		||||
       when action in [:new, :clone] do
 | 
			
		||||
    socket =
 | 
			
		||||
      case multiplier_str |> Integer.parse() do
 | 
			
		||||
        {multiplier, _remainder}
 | 
			
		||||
        when multiplier >= 1 and multiplier <= @pack_create_limit ->
 | 
			
		||||
          socket |> create_multiple(pack_params, multiplier)
 | 
			
		||||
 | 
			
		||||
        {multiplier, _remainder} ->
 | 
			
		||||
          error_msg =
 | 
			
		||||
            dgettext(
 | 
			
		||||
              "errors",
 | 
			
		||||
              "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}",
 | 
			
		||||
              max: @pack_create_limit,
 | 
			
		||||
              multiplier: multiplier
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
          save_multiplier_error(socket, changeset, error_msg)
 | 
			
		||||
 | 
			
		||||
        :error ->
 | 
			
		||||
          error_msg = dgettext("errors", "Could not parse number of copies")
 | 
			
		||||
          save_multiplier_error(socket, changeset, error_msg)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
    {:noreply, socket}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @spec save_multiplier_error(Socket.t(), Changeset.t(), String.t()) :: Socket.t()
 | 
			
		||||
  defp save_multiplier_error(socket, changeset, error_msg) do
 | 
			
		||||
    {:error, changeset} =
 | 
			
		||||
      changeset
 | 
			
		||||
      |> Changeset.add_error(:multiplier, error_msg)
 | 
			
		||||
      |> Changeset.apply_action(:insert)
 | 
			
		||||
 | 
			
		||||
    socket |> assign(:changeset, changeset)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp create_multiple(
 | 
			
		||||
         %{assigns: %{current_user: current_user, return_to: return_to}} = socket,
 | 
			
		||||
         pack_params,
 | 
			
		||||
         multiplier
 | 
			
		||||
       ) do
 | 
			
		||||
    case Ammo.create_packs(pack_params, multiplier, current_user) do
 | 
			
		||||
      {:ok, {count, _packs}} ->
 | 
			
		||||
      with {multiplier, _remainder} <- multiplier_str |> Integer.parse(),
 | 
			
		||||
           {:ok, {count, _packs}} <- Ammo.create_packs(pack_params, multiplier, current_user) do
 | 
			
		||||
        prompt =
 | 
			
		||||
          dngettext(
 | 
			
		||||
            "prompts",
 | 
			
		||||
@@ -182,9 +167,21 @@ defmodule CanneryWeb.PackLive.FormComponent do
 | 
			
		||||
          )
 | 
			
		||||
 | 
			
		||||
        socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
 | 
			
		||||
      else
 | 
			
		||||
        {:error, %Changeset{} = changeset} ->
 | 
			
		||||
          socket |> assign(changeset: changeset)
 | 
			
		||||
 | 
			
		||||
      {:error, %Changeset{} = changeset} ->
 | 
			
		||||
        socket |> assign(changeset: changeset)
 | 
			
		||||
    end
 | 
			
		||||
        :error ->
 | 
			
		||||
          error_msg = dgettext("errors", "Could not parse number of copies")
 | 
			
		||||
 | 
			
		||||
          {:error, changeset} =
 | 
			
		||||
            changeset
 | 
			
		||||
            |> Changeset.add_error(:multiplier, error_msg)
 | 
			
		||||
            |> Changeset.apply_action(:insert)
 | 
			
		||||
 | 
			
		||||
          socket |> assign(:changeset, changeset)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
    {:noreply, socket}
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -19,8 +19,23 @@
 | 
			
		||||
      <%= changeset_errors(@changeset) %>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <%= label(f, :class, gettext("Class"), class: "title text-lg text-primary-600") %>
 | 
			
		||||
    <%= select(
 | 
			
		||||
      f,
 | 
			
		||||
      :class,
 | 
			
		||||
      [
 | 
			
		||||
        {gettext("Any"), :all},
 | 
			
		||||
        {gettext("Rifle"), :rifle},
 | 
			
		||||
        {gettext("Shotgun"), :shotgun},
 | 
			
		||||
        {gettext("Pistol"), :pistol}
 | 
			
		||||
      ],
 | 
			
		||||
      class: "text-center col-span-2 input input-primary",
 | 
			
		||||
      value: @class
 | 
			
		||||
    ) %>
 | 
			
		||||
    <%= error_tag(f, :class, "col-span-3 text-center") %>
 | 
			
		||||
 | 
			
		||||
    <%= label(f, :type_id, gettext("Type"), class: "title text-lg text-primary-600") %>
 | 
			
		||||
    <%= select(f, :type_id, type_options(@types),
 | 
			
		||||
    <%= select(f, :type_id, type_options(@types, @class),
 | 
			
		||||
      class: "text-center col-span-2 input input-primary",
 | 
			
		||||
      id: "pack-form-type-select",
 | 
			
		||||
      phx_hook: "SlimSelect"
 | 
			
		||||
@@ -80,7 +95,6 @@
 | 
			
		||||
 | 
			
		||||
        <%= label(f, :multiplier, gettext("Copies"), class: "title text-lg text-primary-600") %>
 | 
			
		||||
        <%= number_input(f, :multiplier,
 | 
			
		||||
          max: @pack_create_limit,
 | 
			
		||||
          class: "text-center input input-primary",
 | 
			
		||||
          value: 1,
 | 
			
		||||
          phx_update: "ignore"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user