forked from shibao/cannery
		
	Compare commits
	
		
			23 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 81f21a02c4 | |||
| 9a17d4ad24 | |||
| 9835fe3f5e | |||
| 4dee8808f3 | |||
| 65c70ca398 | |||
| 550f6a6420 | |||
| 88c3f15fc8 | |||
| c33f15603b | |||
| 98775359af | |||
| e0e7b25bc4 | |||
| bdddf65685 | |||
| 6f50702b11 | |||
| 5f8d1a917f | |||
| 32801828fa | |||
| 6ed3312ea8 | |||
| b122253b9b | |||
| a68a16bf06 | |||
| 4b6d0952f8 | |||
| 0544b58ab6 | |||
| 6d26103784 | |||
| 0cae7c2940 | |||
| 1e645b5bb8 | |||
| bab2b26c13 | 
							
								
								
									
										26
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -1,3 +1,15 @@
 | 
			
		||||
# v0.9.2
 | 
			
		||||
- Add lot number to packs
 | 
			
		||||
- Don't show price paid and lot number columns when displaying packs if not used
 | 
			
		||||
- Fix additional shotgun fields not being exportable
 | 
			
		||||
 | 
			
		||||
# v0.9.1
 | 
			
		||||
- Rename ammo type's "type" to "class" to avoid confusion
 | 
			
		||||
- Rename "ammo type" to "type" to avoid confusion
 | 
			
		||||
- Fixes type search
 | 
			
		||||
- Fixes shot records table disappearing after selecting an empty ammo class
 | 
			
		||||
- Code quality improvements
 | 
			
		||||
 | 
			
		||||
# v0.9.0
 | 
			
		||||
- Add length limits to all string fields
 | 
			
		||||
- Add selectable ammo types
 | 
			
		||||
@@ -51,7 +63,7 @@
 | 
			
		||||
# v0.8.0
 | 
			
		||||
- Add search to catalog, ammo, container, tag and range index pages
 | 
			
		||||
- Tweak urls for catalog, ammo, containers, tags and shot records
 | 
			
		||||
- Fix bug with shot group chart not drawing lines between days correctly
 | 
			
		||||
- Fix bug with shot record chart not drawing lines between days correctly
 | 
			
		||||
- Improve cards across app (make them line up with each other)
 | 
			
		||||
- Update translations and add spanish!!! (thank you Brea and Hannah!)
 | 
			
		||||
 | 
			
		||||
@@ -63,7 +75,7 @@
 | 
			
		||||
- Fix toggle button styling
 | 
			
		||||
- Miscellanous code improvements
 | 
			
		||||
- Improve container index table
 | 
			
		||||
- Fix bug with ammo not updating after deleting shot group
 | 
			
		||||
- Fix bug with ammo not updating after deleting shot record
 | 
			
		||||
- Replace ammo "added on" with "purchased on"
 | 
			
		||||
- Miscellaneous wording improvements
 | 
			
		||||
- Update translations
 | 
			
		||||
@@ -72,8 +84,8 @@
 | 
			
		||||
- Add shading to table component
 | 
			
		||||
- Fix chart to sum by day
 | 
			
		||||
- Fix whitespace when copying invite url
 | 
			
		||||
- Make ammo type show page also display ammo groups as table
 | 
			
		||||
- Make container show page also display ammo groups as table
 | 
			
		||||
- Make ammo type show page also display packs as table
 | 
			
		||||
- Make container show page also display packs as table
 | 
			
		||||
- Display CPR for ammo packs
 | 
			
		||||
- Add original count for ammo packs
 | 
			
		||||
- Add ammo pack CPR and original count to json export
 | 
			
		||||
@@ -97,7 +109,7 @@
 | 
			
		||||
- Add ammo type cloning
 | 
			
		||||
- Add container cloning
 | 
			
		||||
- Fix bug with moving ammo packs between containers
 | 
			
		||||
- Add button to set rounds left to 0 when creating a shot group
 | 
			
		||||
- Add button to set rounds left to 0 when creating a shot record
 | 
			
		||||
- Update project dependencies
 | 
			
		||||
 | 
			
		||||
# v0.5.4
 | 
			
		||||
@@ -149,8 +161,8 @@
 | 
			
		||||
# v0.3.0
 | 
			
		||||
- Fix ammo type counts not showing when count is 0
 | 
			
		||||
- Add prompt to create first container before first ammo group
 | 
			
		||||
- Edit and delete shot groups from ammo group show page
 | 
			
		||||
- Use today's date when adding new shot groups
 | 
			
		||||
- Edit and delete shot records from ammo group show page
 | 
			
		||||
- Use today's date when adding new shot records
 | 
			
		||||
- Create multiple ammo groups at one time
 | 
			
		||||
 | 
			
		||||
# v0.2.3
 | 
			
		||||
 
 | 
			
		||||
@@ -17,8 +17,8 @@ If you're multilingual, this project can use your translations! Visit
 | 
			
		||||
  functions as short as possible while keeping variable names descriptive! For
 | 
			
		||||
  instance, use inline `do:` blocks for short functions and make your aliases as
 | 
			
		||||
  short as possible without introducing ambiguity.
 | 
			
		||||
  - I.e. since there's only one `AmmoGroup` in the app, please alias
 | 
			
		||||
    `AmmoGroup.t()` instead of using `Cannery.Ammo.AmmoGroup.t()`
 | 
			
		||||
  - I.e. since there's only one `Pack` in the app, please alias
 | 
			
		||||
    `Pack.t()` instead of using `Cannery.Ammo.Pack.t()`
 | 
			
		||||
- Use pipelines when possible. If only calling a single method, a pipeline isn't
 | 
			
		||||
  strictly necessary but still encouraged for future modification.
 | 
			
		||||
- Please add typespecs to your functions! Even your private functions may be
 | 
			
		||||
 
 | 
			
		||||
@@ -13,8 +13,8 @@ The self-hosted firearm tracker website.
 | 
			
		||||
# Features
 | 
			
		||||
 | 
			
		||||
- Create containers to store your ammunition, and tag them with custom tags
 | 
			
		||||
- Add ammunition types to Cannery, and then ammunition groups to your containers
 | 
			
		||||
- Stage groups of ammo for range day and record your ammo usage
 | 
			
		||||
- Add ammunition types to Cannery, and then ammo packs to your containers
 | 
			
		||||
- Stage ammo packs for range day and track your usage with shot records
 | 
			
		||||
- Invitations via invite tokens or public registration
 | 
			
		||||
 | 
			
		||||
# Installation
 | 
			
		||||
 
 | 
			
		||||
@@ -45,7 +45,7 @@ module.exports = (env, options) => {
 | 
			
		||||
        {
 | 
			
		||||
          test: /\.(woff(2)?|ttf|eot|svg|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
 | 
			
		||||
          type: 'asset/resource',
 | 
			
		||||
          generator: { filename: 'fonts/[name][ext]' }
 | 
			
		||||
          generator: { filename: 'fonts/[name].[ext]' }
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
@@ -4,50 +4,50 @@ defmodule Cannery.ActivityLog do
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  import Ecto.Query, warn: false
 | 
			
		||||
  alias Cannery.Ammo.{AmmoGroup, AmmoType}
 | 
			
		||||
  alias Cannery.{Accounts.User, ActivityLog.ShotGroup, Repo}
 | 
			
		||||
  alias Cannery.Ammo.{Pack, Type}
 | 
			
		||||
  alias Cannery.{Accounts.User, ActivityLog.ShotRecord, Repo}
 | 
			
		||||
  alias Ecto.{Multi, Queryable}
 | 
			
		||||
 | 
			
		||||
  @doc """
 | 
			
		||||
  Returns the list of shot_groups.
 | 
			
		||||
  Returns the list of shot_records.
 | 
			
		||||
 | 
			
		||||
  ## Examples
 | 
			
		||||
 | 
			
		||||
      iex> list_shot_groups(:all, %User{id: 123})
 | 
			
		||||
      [%ShotGroup{}, ...]
 | 
			
		||||
      iex> list_shot_records(:all, %User{id: 123})
 | 
			
		||||
      [%ShotRecord{}, ...]
 | 
			
		||||
 | 
			
		||||
      iex> list_shot_groups("cool", :all, %User{id: 123})
 | 
			
		||||
      [%ShotGroup{notes: "My cool shot group"}, ...]
 | 
			
		||||
      iex> list_shot_records("cool", :all, %User{id: 123})
 | 
			
		||||
      [%ShotRecord{notes: "My cool shot record"}, ...]
 | 
			
		||||
 | 
			
		||||
      iex> list_shot_groups("cool", :rifle, %User{id: 123})
 | 
			
		||||
      [%ShotGroup{notes: "Shot some rifle rounds"}, ...]
 | 
			
		||||
      iex> list_shot_records("cool", :rifle, %User{id: 123})
 | 
			
		||||
      [%ShotRecord{notes: "Shot some rifle rounds"}, ...]
 | 
			
		||||
 | 
			
		||||
  """
 | 
			
		||||
  @spec list_shot_groups(AmmoType.type() | :all, User.t()) :: [ShotGroup.t()]
 | 
			
		||||
  @spec list_shot_groups(search :: nil | String.t(), AmmoType.type() | :all, User.t()) ::
 | 
			
		||||
          [ShotGroup.t()]
 | 
			
		||||
  def list_shot_groups(search \\ nil, type, %{id: user_id}) do
 | 
			
		||||
    from(sg in ShotGroup,
 | 
			
		||||
  @spec list_shot_records(Type.class() | :all, User.t()) :: [ShotRecord.t()]
 | 
			
		||||
  @spec list_shot_records(search :: nil | String.t(), Type.class() | :all, User.t()) ::
 | 
			
		||||
          [ShotRecord.t()]
 | 
			
		||||
  def list_shot_records(search \\ nil, type, %{id: user_id}) do
 | 
			
		||||
    from(sg in ShotRecord,
 | 
			
		||||
      as: :sg,
 | 
			
		||||
      left_join: ag in AmmoGroup,
 | 
			
		||||
      left_join: ag in Pack,
 | 
			
		||||
      as: :ag,
 | 
			
		||||
      on: sg.ammo_group_id == ag.id,
 | 
			
		||||
      left_join: at in AmmoType,
 | 
			
		||||
      on: sg.pack_id == ag.id,
 | 
			
		||||
      left_join: at in Type,
 | 
			
		||||
      as: :at,
 | 
			
		||||
      on: ag.ammo_type_id == at.id,
 | 
			
		||||
      on: ag.type_id == at.id,
 | 
			
		||||
      where: sg.user_id == ^user_id,
 | 
			
		||||
      distinct: sg.id
 | 
			
		||||
    )
 | 
			
		||||
    |> list_shot_groups_search(search)
 | 
			
		||||
    |> list_shot_groups_filter_type(type)
 | 
			
		||||
    |> list_shot_records_search(search)
 | 
			
		||||
    |> list_shot_records_filter_type(type)
 | 
			
		||||
    |> Repo.all()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @spec list_shot_groups_search(Queryable.t(), search :: String.t() | nil) ::
 | 
			
		||||
  @spec list_shot_records_search(Queryable.t(), search :: String.t() | nil) ::
 | 
			
		||||
          Queryable.t()
 | 
			
		||||
  defp list_shot_groups_search(query, search) when search in ["", nil], do: query
 | 
			
		||||
  defp list_shot_records_search(query, search) when search in ["", nil], do: query
 | 
			
		||||
 | 
			
		||||
  defp list_shot_groups_search(query, search) when search |> is_binary() do
 | 
			
		||||
  defp list_shot_records_search(query, search) when search |> is_binary() do
 | 
			
		||||
    trimmed_search = String.trim(search)
 | 
			
		||||
 | 
			
		||||
    query
 | 
			
		||||
@@ -79,49 +79,68 @@ defmodule Cannery.ActivityLog do
 | 
			
		||||
    })
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @spec list_shot_groups_filter_type(Queryable.t(), AmmoType.type() | :all) ::
 | 
			
		||||
  @spec list_shot_records_filter_type(Queryable.t(), Type.class() | :all) ::
 | 
			
		||||
          Queryable.t()
 | 
			
		||||
  defp list_shot_groups_filter_type(query, :rifle),
 | 
			
		||||
    do: query |> where([at: at], at.type == :rifle)
 | 
			
		||||
  defp list_shot_records_filter_type(query, :rifle),
 | 
			
		||||
    do: query |> where([at: at], at.class == :rifle)
 | 
			
		||||
 | 
			
		||||
  defp list_shot_groups_filter_type(query, :pistol),
 | 
			
		||||
    do: query |> where([at: at], at.type == :pistol)
 | 
			
		||||
  defp list_shot_records_filter_type(query, :pistol),
 | 
			
		||||
    do: query |> where([at: at], at.class == :pistol)
 | 
			
		||||
 | 
			
		||||
  defp list_shot_groups_filter_type(query, :shotgun),
 | 
			
		||||
    do: query |> where([at: at], at.type == :shotgun)
 | 
			
		||||
  defp list_shot_records_filter_type(query, :shotgun),
 | 
			
		||||
    do: query |> where([at: at], at.class == :shotgun)
 | 
			
		||||
 | 
			
		||||
  defp list_shot_groups_filter_type(query, _all), do: query
 | 
			
		||||
  defp list_shot_records_filter_type(query, _all), do: query
 | 
			
		||||
 | 
			
		||||
  @spec list_shot_groups_for_ammo_group(AmmoGroup.t(), User.t()) :: [ShotGroup.t()]
 | 
			
		||||
  def list_shot_groups_for_ammo_group(
 | 
			
		||||
        %AmmoGroup{id: ammo_group_id, user_id: user_id},
 | 
			
		||||
  @doc """
 | 
			
		||||
  Returns a count of shot records.
 | 
			
		||||
 | 
			
		||||
  ## Examples
 | 
			
		||||
 | 
			
		||||
      iex> get_shot_record_count!(%User{id: 123})
 | 
			
		||||
      3
 | 
			
		||||
 | 
			
		||||
  """
 | 
			
		||||
  @spec get_shot_record_count!(User.t()) :: integer()
 | 
			
		||||
  def get_shot_record_count!(%User{id: user_id}) do
 | 
			
		||||
    Repo.one(
 | 
			
		||||
      from sg in ShotRecord,
 | 
			
		||||
        where: sg.user_id == ^user_id,
 | 
			
		||||
        select: count(sg.id),
 | 
			
		||||
        distinct: true
 | 
			
		||||
    ) || 0
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @spec list_shot_records_for_pack(Pack.t(), User.t()) :: [ShotRecord.t()]
 | 
			
		||||
  def list_shot_records_for_pack(
 | 
			
		||||
        %Pack{id: pack_id, user_id: user_id},
 | 
			
		||||
        %User{id: user_id}
 | 
			
		||||
      ) do
 | 
			
		||||
    Repo.all(
 | 
			
		||||
      from sg in ShotGroup,
 | 
			
		||||
        where: sg.ammo_group_id == ^ammo_group_id,
 | 
			
		||||
      from sg in ShotRecord,
 | 
			
		||||
        where: sg.pack_id == ^pack_id,
 | 
			
		||||
        where: sg.user_id == ^user_id
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @doc """
 | 
			
		||||
  Gets a single shot_group.
 | 
			
		||||
  Gets a single shot_record.
 | 
			
		||||
 | 
			
		||||
  Raises `Ecto.NoResultsError` if the Shot group does not exist.
 | 
			
		||||
  Raises `Ecto.NoResultsError` if the shot record does not exist.
 | 
			
		||||
 | 
			
		||||
  ## Examples
 | 
			
		||||
 | 
			
		||||
      iex> get_shot_group!(123, %User{id: 123})
 | 
			
		||||
      %ShotGroup{}
 | 
			
		||||
      iex> get_shot_record!(123, %User{id: 123})
 | 
			
		||||
      %ShotRecord{}
 | 
			
		||||
 | 
			
		||||
      iex> get_shot_group!(456, %User{id: 123})
 | 
			
		||||
      iex> get_shot_record!(456, %User{id: 123})
 | 
			
		||||
      ** (Ecto.NoResultsError)
 | 
			
		||||
 | 
			
		||||
  """
 | 
			
		||||
  @spec get_shot_group!(ShotGroup.id(), User.t()) :: ShotGroup.t()
 | 
			
		||||
  def get_shot_group!(id, %User{id: user_id}) do
 | 
			
		||||
  @spec get_shot_record!(ShotRecord.id(), User.t()) :: ShotRecord.t()
 | 
			
		||||
  def get_shot_record!(id, %User{id: user_id}) do
 | 
			
		||||
    Repo.one!(
 | 
			
		||||
      from sg in ShotGroup,
 | 
			
		||||
      from sg in ShotRecord,
 | 
			
		||||
        where: sg.id == ^id,
 | 
			
		||||
        where: sg.user_id == ^user_id,
 | 
			
		||||
        order_by: sg.date
 | 
			
		||||
@@ -129,251 +148,249 @@ defmodule Cannery.ActivityLog do
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @doc """
 | 
			
		||||
  Creates a shot_group.
 | 
			
		||||
  Creates a shot_record.
 | 
			
		||||
 | 
			
		||||
  ## Examples
 | 
			
		||||
 | 
			
		||||
      iex> create_shot_group(%{field: value}, %User{id: 123})
 | 
			
		||||
      {:ok, %ShotGroup{}}
 | 
			
		||||
      iex> create_shot_record(%{field: value}, %User{id: 123})
 | 
			
		||||
      {:ok, %ShotRecord{}}
 | 
			
		||||
 | 
			
		||||
      iex> create_shot_group(%{field: bad_value}, %User{id: 123})
 | 
			
		||||
      iex> create_shot_record(%{field: bad_value}, %User{id: 123})
 | 
			
		||||
      {:error, %Ecto.Changeset{}}
 | 
			
		||||
 | 
			
		||||
  """
 | 
			
		||||
  @spec create_shot_group(attrs :: map(), User.t(), AmmoGroup.t()) ::
 | 
			
		||||
          {:ok, ShotGroup.t()} | {:error, ShotGroup.changeset() | nil}
 | 
			
		||||
  def create_shot_group(attrs, user, ammo_group) do
 | 
			
		||||
  @spec create_shot_record(attrs :: map(), User.t(), Pack.t()) ::
 | 
			
		||||
          {:ok, ShotRecord.t()} | {:error, ShotRecord.changeset() | nil}
 | 
			
		||||
  def create_shot_record(attrs, user, pack) do
 | 
			
		||||
    Multi.new()
 | 
			
		||||
    |> Multi.insert(
 | 
			
		||||
      :create_shot_group,
 | 
			
		||||
      %ShotGroup{} |> ShotGroup.create_changeset(user, ammo_group, attrs)
 | 
			
		||||
      :create_shot_record,
 | 
			
		||||
      %ShotRecord{} |> ShotRecord.create_changeset(user, pack, attrs)
 | 
			
		||||
    )
 | 
			
		||||
    |> Multi.run(
 | 
			
		||||
      :ammo_group,
 | 
			
		||||
      fn _repo, %{create_shot_group: %{ammo_group_id: ammo_group_id, user_id: user_id}} ->
 | 
			
		||||
        ammo_group =
 | 
			
		||||
      :pack,
 | 
			
		||||
      fn _repo, %{create_shot_record: %{pack_id: pack_id, user_id: user_id}} ->
 | 
			
		||||
        pack =
 | 
			
		||||
          Repo.one(
 | 
			
		||||
            from ag in AmmoGroup,
 | 
			
		||||
              where: ag.id == ^ammo_group_id,
 | 
			
		||||
            from ag in Pack,
 | 
			
		||||
              where: ag.id == ^pack_id,
 | 
			
		||||
              where: ag.user_id == ^user_id
 | 
			
		||||
          )
 | 
			
		||||
 | 
			
		||||
        {:ok, ammo_group}
 | 
			
		||||
        {:ok, pack}
 | 
			
		||||
      end
 | 
			
		||||
    )
 | 
			
		||||
    |> Multi.update(
 | 
			
		||||
      :update_ammo_group,
 | 
			
		||||
      fn %{create_shot_group: %{count: shot_group_count}, ammo_group: %{count: ammo_group_count}} ->
 | 
			
		||||
        ammo_group |> AmmoGroup.range_changeset(%{"count" => ammo_group_count - shot_group_count})
 | 
			
		||||
      :update_pack,
 | 
			
		||||
      fn %{create_shot_record: %{count: shot_record_count}, pack: %{count: pack_count}} ->
 | 
			
		||||
        pack |> Pack.range_changeset(%{"count" => pack_count - shot_record_count})
 | 
			
		||||
      end
 | 
			
		||||
    )
 | 
			
		||||
    |> Repo.transaction()
 | 
			
		||||
    |> case do
 | 
			
		||||
      {:ok, %{create_shot_group: shot_group}} -> {:ok, shot_group}
 | 
			
		||||
      {:error, :create_shot_group, changeset, _changes_so_far} -> {:error, changeset}
 | 
			
		||||
      {:ok, %{create_shot_record: shot_record}} -> {:ok, shot_record}
 | 
			
		||||
      {:error, :create_shot_record, changeset, _changes_so_far} -> {:error, changeset}
 | 
			
		||||
      {:error, _other_transaction, _value, _changes_so_far} -> {:error, nil}
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @doc """
 | 
			
		||||
  Updates a shot_group.
 | 
			
		||||
  Updates a shot_record.
 | 
			
		||||
 | 
			
		||||
  ## Examples
 | 
			
		||||
 | 
			
		||||
      iex> update_shot_group(shot_group, %{field: new_value}, %User{id: 123})
 | 
			
		||||
      {:ok, %ShotGroup{}}
 | 
			
		||||
      iex> update_shot_record(shot_record, %{field: new_value}, %User{id: 123})
 | 
			
		||||
      {:ok, %ShotRecord{}}
 | 
			
		||||
 | 
			
		||||
      iex> update_shot_group(shot_group, %{field: bad_value}, %User{id: 123})
 | 
			
		||||
      iex> update_shot_record(shot_record, %{field: bad_value}, %User{id: 123})
 | 
			
		||||
      {:error, %Ecto.Changeset{}}
 | 
			
		||||
 | 
			
		||||
  """
 | 
			
		||||
  @spec update_shot_group(ShotGroup.t(), attrs :: map(), User.t()) ::
 | 
			
		||||
          {:ok, ShotGroup.t()} | {:error, ShotGroup.changeset() | nil}
 | 
			
		||||
  def update_shot_group(
 | 
			
		||||
        %ShotGroup{count: count, user_id: user_id} = shot_group,
 | 
			
		||||
  @spec update_shot_record(ShotRecord.t(), attrs :: map(), User.t()) ::
 | 
			
		||||
          {:ok, ShotRecord.t()} | {:error, ShotRecord.changeset() | nil}
 | 
			
		||||
  def update_shot_record(
 | 
			
		||||
        %ShotRecord{count: count, user_id: user_id} = shot_record,
 | 
			
		||||
        attrs,
 | 
			
		||||
        %User{id: user_id} = user
 | 
			
		||||
      ) do
 | 
			
		||||
    Multi.new()
 | 
			
		||||
    |> Multi.update(
 | 
			
		||||
      :update_shot_group,
 | 
			
		||||
      shot_group |> ShotGroup.update_changeset(user, attrs)
 | 
			
		||||
      :update_shot_record,
 | 
			
		||||
      shot_record |> ShotRecord.update_changeset(user, attrs)
 | 
			
		||||
    )
 | 
			
		||||
    |> Multi.run(
 | 
			
		||||
      :ammo_group,
 | 
			
		||||
      fn repo, %{update_shot_group: %{ammo_group_id: ammo_group_id, user_id: user_id}} ->
 | 
			
		||||
        {:ok,
 | 
			
		||||
         repo.one(from ag in AmmoGroup, where: ag.id == ^ammo_group_id and ag.user_id == ^user_id)}
 | 
			
		||||
      :pack,
 | 
			
		||||
      fn repo, %{update_shot_record: %{pack_id: pack_id, user_id: user_id}} ->
 | 
			
		||||
        {:ok, repo.one(from ag in Pack, where: ag.id == ^pack_id and ag.user_id == ^user_id)}
 | 
			
		||||
      end
 | 
			
		||||
    )
 | 
			
		||||
    |> Multi.update(
 | 
			
		||||
      :update_ammo_group,
 | 
			
		||||
      :update_pack,
 | 
			
		||||
      fn %{
 | 
			
		||||
           update_shot_group: %{count: new_count},
 | 
			
		||||
           ammo_group: %{count: ammo_group_count} = ammo_group
 | 
			
		||||
           update_shot_record: %{count: new_count},
 | 
			
		||||
           pack: %{count: pack_count} = pack
 | 
			
		||||
         } ->
 | 
			
		||||
        shot_diff_to_add = new_count - count
 | 
			
		||||
        new_ammo_group_count = ammo_group_count - shot_diff_to_add
 | 
			
		||||
        ammo_group |> AmmoGroup.range_changeset(%{"count" => new_ammo_group_count})
 | 
			
		||||
        new_pack_count = pack_count - shot_diff_to_add
 | 
			
		||||
        pack |> Pack.range_changeset(%{"count" => new_pack_count})
 | 
			
		||||
      end
 | 
			
		||||
    )
 | 
			
		||||
    |> Repo.transaction()
 | 
			
		||||
    |> case do
 | 
			
		||||
      {:ok, %{update_shot_group: shot_group}} -> {:ok, shot_group}
 | 
			
		||||
      {:error, :update_shot_group, changeset, _changes_so_far} -> {:error, changeset}
 | 
			
		||||
      {:ok, %{update_shot_record: shot_record}} -> {:ok, shot_record}
 | 
			
		||||
      {:error, :update_shot_record, changeset, _changes_so_far} -> {:error, changeset}
 | 
			
		||||
      {:error, _other_transaction, _value, _changes_so_far} -> {:error, nil}
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @doc """
 | 
			
		||||
  Deletes a shot_group.
 | 
			
		||||
  Deletes a shot_record.
 | 
			
		||||
 | 
			
		||||
  ## Examples
 | 
			
		||||
 | 
			
		||||
      iex> delete_shot_group(shot_group, %User{id: 123})
 | 
			
		||||
      {:ok, %ShotGroup{}}
 | 
			
		||||
      iex> delete_shot_record(shot_record, %User{id: 123})
 | 
			
		||||
      {:ok, %ShotRecord{}}
 | 
			
		||||
 | 
			
		||||
      iex> delete_shot_group(shot_group, %User{id: 123})
 | 
			
		||||
      iex> delete_shot_record(shot_record, %User{id: 123})
 | 
			
		||||
      {:error, %Ecto.Changeset{}}
 | 
			
		||||
 | 
			
		||||
  """
 | 
			
		||||
  @spec delete_shot_group(ShotGroup.t(), User.t()) ::
 | 
			
		||||
          {:ok, ShotGroup.t()} | {:error, ShotGroup.changeset()}
 | 
			
		||||
  def delete_shot_group(
 | 
			
		||||
        %ShotGroup{user_id: user_id} = shot_group,
 | 
			
		||||
  @spec delete_shot_record(ShotRecord.t(), User.t()) ::
 | 
			
		||||
          {:ok, ShotRecord.t()} | {:error, ShotRecord.changeset()}
 | 
			
		||||
  def delete_shot_record(
 | 
			
		||||
        %ShotRecord{user_id: user_id} = shot_record,
 | 
			
		||||
        %User{id: user_id}
 | 
			
		||||
      ) do
 | 
			
		||||
    Multi.new()
 | 
			
		||||
    |> Multi.delete(:delete_shot_group, shot_group)
 | 
			
		||||
    |> Multi.delete(:delete_shot_record, shot_record)
 | 
			
		||||
    |> Multi.run(
 | 
			
		||||
      :ammo_group,
 | 
			
		||||
      fn repo, %{delete_shot_group: %{ammo_group_id: ammo_group_id, user_id: user_id}} ->
 | 
			
		||||
        {:ok,
 | 
			
		||||
         repo.one(from ag in AmmoGroup, where: ag.id == ^ammo_group_id and ag.user_id == ^user_id)}
 | 
			
		||||
      :pack,
 | 
			
		||||
      fn repo, %{delete_shot_record: %{pack_id: pack_id, user_id: user_id}} ->
 | 
			
		||||
        {:ok, repo.one(from ag in Pack, where: ag.id == ^pack_id and ag.user_id == ^user_id)}
 | 
			
		||||
      end
 | 
			
		||||
    )
 | 
			
		||||
    |> Multi.update(
 | 
			
		||||
      :update_ammo_group,
 | 
			
		||||
      :update_pack,
 | 
			
		||||
      fn %{
 | 
			
		||||
           delete_shot_group: %{count: count},
 | 
			
		||||
           ammo_group: %{count: ammo_group_count} = ammo_group
 | 
			
		||||
           delete_shot_record: %{count: count},
 | 
			
		||||
           pack: %{count: pack_count} = pack
 | 
			
		||||
         } ->
 | 
			
		||||
        new_ammo_group_count = ammo_group_count + count
 | 
			
		||||
        ammo_group |> AmmoGroup.range_changeset(%{"count" => new_ammo_group_count})
 | 
			
		||||
        new_pack_count = pack_count + count
 | 
			
		||||
        pack |> Pack.range_changeset(%{"count" => new_pack_count})
 | 
			
		||||
      end
 | 
			
		||||
    )
 | 
			
		||||
    |> Repo.transaction()
 | 
			
		||||
    |> case do
 | 
			
		||||
      {:ok, %{delete_shot_group: shot_group}} -> {:ok, shot_group}
 | 
			
		||||
      {:error, :delete_shot_group, changeset, _changes_so_far} -> {:error, changeset}
 | 
			
		||||
      {:ok, %{delete_shot_record: shot_record}} -> {:ok, shot_record}
 | 
			
		||||
      {:error, :delete_shot_record, changeset, _changes_so_far} -> {:error, changeset}
 | 
			
		||||
      {:error, _other_transaction, _value, _changes_so_far} -> {:error, nil}
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @doc """
 | 
			
		||||
  Returns the number of shot rounds for an ammo group
 | 
			
		||||
  Returns the number of shot rounds for a pack
 | 
			
		||||
  """
 | 
			
		||||
  @spec get_used_count(AmmoGroup.t(), User.t()) :: non_neg_integer()
 | 
			
		||||
  def get_used_count(%AmmoGroup{id: ammo_group_id} = ammo_group, user) do
 | 
			
		||||
    [ammo_group]
 | 
			
		||||
  @spec get_used_count(Pack.t(), User.t()) :: non_neg_integer()
 | 
			
		||||
  def get_used_count(%Pack{id: pack_id} = pack, user) do
 | 
			
		||||
    [pack]
 | 
			
		||||
    |> get_used_counts(user)
 | 
			
		||||
    |> Map.get(ammo_group_id, 0)
 | 
			
		||||
    |> Map.get(pack_id, 0)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @doc """
 | 
			
		||||
  Returns the number of shot rounds for multiple ammo groups
 | 
			
		||||
  Returns the number of shot rounds for multiple packs
 | 
			
		||||
  """
 | 
			
		||||
  @spec get_used_counts([AmmoGroup.t()], User.t()) ::
 | 
			
		||||
          %{optional(AmmoGroup.id()) => non_neg_integer()}
 | 
			
		||||
  def get_used_counts(ammo_groups, %User{id: user_id}) do
 | 
			
		||||
    ammo_group_ids =
 | 
			
		||||
      ammo_groups
 | 
			
		||||
      |> Enum.map(fn %{id: ammo_group_id} -> ammo_group_id end)
 | 
			
		||||
  @spec get_used_counts([Pack.t()], User.t()) ::
 | 
			
		||||
          %{optional(Pack.id()) => non_neg_integer()}
 | 
			
		||||
  def get_used_counts(packs, %User{id: user_id}) do
 | 
			
		||||
    pack_ids =
 | 
			
		||||
      packs
 | 
			
		||||
      |> Enum.map(fn %{id: pack_id} -> pack_id end)
 | 
			
		||||
 | 
			
		||||
    Repo.all(
 | 
			
		||||
      from sg in ShotGroup,
 | 
			
		||||
        where: sg.ammo_group_id in ^ammo_group_ids,
 | 
			
		||||
      from sg in ShotRecord,
 | 
			
		||||
        where: sg.pack_id in ^pack_ids,
 | 
			
		||||
        where: sg.user_id == ^user_id,
 | 
			
		||||
        group_by: sg.ammo_group_id,
 | 
			
		||||
        select: {sg.ammo_group_id, sum(sg.count)}
 | 
			
		||||
        group_by: sg.pack_id,
 | 
			
		||||
        select: {sg.pack_id, sum(sg.count)}
 | 
			
		||||
    )
 | 
			
		||||
    |> Map.new()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @doc """
 | 
			
		||||
  Returns the last entered shot group date for an ammo group
 | 
			
		||||
  Returns the last entered shot record date for a pack
 | 
			
		||||
  """
 | 
			
		||||
  @spec get_last_used_date(AmmoGroup.t(), User.t()) :: Date.t() | nil
 | 
			
		||||
  def get_last_used_date(%AmmoGroup{id: ammo_group_id} = ammo_group, user) do
 | 
			
		||||
    [ammo_group]
 | 
			
		||||
  @spec get_last_used_date(Pack.t(), User.t()) :: Date.t() | nil
 | 
			
		||||
  def get_last_used_date(%Pack{id: pack_id} = pack, user) do
 | 
			
		||||
    [pack]
 | 
			
		||||
    |> get_last_used_dates(user)
 | 
			
		||||
    |> Map.get(ammo_group_id)
 | 
			
		||||
    |> Map.get(pack_id)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @doc """
 | 
			
		||||
  Returns the last entered shot group date for an ammo group
 | 
			
		||||
  Returns the last entered shot record date for a pack
 | 
			
		||||
  """
 | 
			
		||||
  @spec get_last_used_dates([AmmoGroup.t()], User.t()) :: %{optional(AmmoGroup.id()) => Date.t()}
 | 
			
		||||
  def get_last_used_dates(ammo_groups, %User{id: user_id}) do
 | 
			
		||||
    ammo_group_ids =
 | 
			
		||||
      ammo_groups
 | 
			
		||||
      |> Enum.map(fn %AmmoGroup{id: ammo_group_id, user_id: ^user_id} -> ammo_group_id end)
 | 
			
		||||
  @spec get_last_used_dates([Pack.t()], User.t()) :: %{optional(Pack.id()) => Date.t()}
 | 
			
		||||
  def get_last_used_dates(packs, %User{id: user_id}) do
 | 
			
		||||
    pack_ids =
 | 
			
		||||
      packs
 | 
			
		||||
      |> Enum.map(fn %Pack{id: pack_id, user_id: ^user_id} -> pack_id end)
 | 
			
		||||
 | 
			
		||||
    Repo.all(
 | 
			
		||||
      from sg in ShotGroup,
 | 
			
		||||
        where: sg.ammo_group_id in ^ammo_group_ids,
 | 
			
		||||
      from sg in ShotRecord,
 | 
			
		||||
        where: sg.pack_id in ^pack_ids,
 | 
			
		||||
        where: sg.user_id == ^user_id,
 | 
			
		||||
        group_by: sg.ammo_group_id,
 | 
			
		||||
        select: {sg.ammo_group_id, max(sg.date)}
 | 
			
		||||
        group_by: sg.pack_id,
 | 
			
		||||
        select: {sg.pack_id, max(sg.date)}
 | 
			
		||||
    )
 | 
			
		||||
    |> Map.new()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @doc """
 | 
			
		||||
  Gets the total number of rounds shot for an ammo type
 | 
			
		||||
  Gets the total number of rounds shot for a type
 | 
			
		||||
 | 
			
		||||
  Raises `Ecto.NoResultsError` if the Ammo type does not exist.
 | 
			
		||||
  Raises `Ecto.NoResultsError` if the type does not exist.
 | 
			
		||||
 | 
			
		||||
  ## Examples
 | 
			
		||||
 | 
			
		||||
      iex> get_used_count_for_ammo_type(123, %User{id: 123})
 | 
			
		||||
      iex> get_used_count_for_type(123, %User{id: 123})
 | 
			
		||||
      35
 | 
			
		||||
 | 
			
		||||
      iex> get_used_count_for_ammo_type(456, %User{id: 123})
 | 
			
		||||
      iex> get_used_count_for_type(456, %User{id: 123})
 | 
			
		||||
      ** (Ecto.NoResultsError)
 | 
			
		||||
 | 
			
		||||
  """
 | 
			
		||||
  @spec get_used_count_for_ammo_type(AmmoType.t(), User.t()) :: non_neg_integer()
 | 
			
		||||
  def get_used_count_for_ammo_type(%AmmoType{id: ammo_type_id} = ammo_type, user) do
 | 
			
		||||
    [ammo_type]
 | 
			
		||||
    |> get_used_count_for_ammo_types(user)
 | 
			
		||||
    |> Map.get(ammo_type_id, 0)
 | 
			
		||||
  @spec get_used_count_for_type(Type.t(), User.t()) :: non_neg_integer()
 | 
			
		||||
  def get_used_count_for_type(%Type{id: type_id} = type, user) do
 | 
			
		||||
    [type]
 | 
			
		||||
    |> get_used_count_for_types(user)
 | 
			
		||||
    |> Map.get(type_id, 0)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @doc """
 | 
			
		||||
  Gets the total number of rounds shot for multiple ammo types
 | 
			
		||||
  Gets the total number of rounds shot for multiple types
 | 
			
		||||
 | 
			
		||||
  ## Examples
 | 
			
		||||
 | 
			
		||||
      iex> get_used_count_for_ammo_types(123, %User{id: 123})
 | 
			
		||||
      iex> get_used_count_for_types(123, %User{id: 123})
 | 
			
		||||
      35
 | 
			
		||||
 | 
			
		||||
  """
 | 
			
		||||
  @spec get_used_count_for_ammo_types([AmmoType.t()], User.t()) ::
 | 
			
		||||
          %{optional(AmmoType.id()) => non_neg_integer()}
 | 
			
		||||
  def get_used_count_for_ammo_types(ammo_types, %User{id: user_id}) do
 | 
			
		||||
    ammo_type_ids =
 | 
			
		||||
      ammo_types
 | 
			
		||||
      |> Enum.map(fn %AmmoType{id: ammo_type_id, user_id: ^user_id} -> ammo_type_id end)
 | 
			
		||||
  @spec get_used_count_for_types([Type.t()], User.t()) ::
 | 
			
		||||
          %{optional(Type.id()) => non_neg_integer()}
 | 
			
		||||
  def get_used_count_for_types(types, %User{id: user_id}) do
 | 
			
		||||
    type_ids =
 | 
			
		||||
      types
 | 
			
		||||
      |> Enum.map(fn %Type{id: type_id, user_id: ^user_id} -> type_id end)
 | 
			
		||||
 | 
			
		||||
    Repo.all(
 | 
			
		||||
      from ag in AmmoGroup,
 | 
			
		||||
        left_join: sg in ShotGroup,
 | 
			
		||||
        on: ag.id == sg.ammo_group_id,
 | 
			
		||||
        where: ag.ammo_type_id in ^ammo_type_ids,
 | 
			
		||||
      from ag in Pack,
 | 
			
		||||
        left_join: sg in ShotRecord,
 | 
			
		||||
        on: ag.id == sg.pack_id,
 | 
			
		||||
        where: ag.type_id in ^type_ids,
 | 
			
		||||
        where: not (sg.count |> is_nil()),
 | 
			
		||||
        group_by: ag.ammo_type_id,
 | 
			
		||||
        select: {ag.ammo_type_id, sum(sg.count)}
 | 
			
		||||
        group_by: ag.type_id,
 | 
			
		||||
        select: {ag.type_id, sum(sg.count)}
 | 
			
		||||
    )
 | 
			
		||||
    |> Map.new()
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,12 @@
 | 
			
		||||
defmodule Cannery.ActivityLog.ShotGroup do
 | 
			
		||||
defmodule Cannery.ActivityLog.ShotRecord do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  A shot group records a group of ammo shot during a range trip
 | 
			
		||||
  A shot record records a group of ammo shot during a range trip
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use Ecto.Schema
 | 
			
		||||
  import CanneryWeb.Gettext
 | 
			
		||||
  import Ecto.Changeset
 | 
			
		||||
  alias Cannery.{Accounts.User, Ammo, Ammo.AmmoGroup}
 | 
			
		||||
  alias Cannery.{Accounts.User, Ammo, Ammo.Pack}
 | 
			
		||||
  alias Ecto.{Changeset, UUID}
 | 
			
		||||
 | 
			
		||||
  @derive {Jason.Encoder,
 | 
			
		||||
@@ -15,17 +15,17 @@ defmodule Cannery.ActivityLog.ShotGroup do
 | 
			
		||||
             :count,
 | 
			
		||||
             :date,
 | 
			
		||||
             :notes,
 | 
			
		||||
             :ammo_group_id
 | 
			
		||||
             :pack_id
 | 
			
		||||
           ]}
 | 
			
		||||
  @primary_key {:id, :binary_id, autogenerate: true}
 | 
			
		||||
  @foreign_key_type :binary_id
 | 
			
		||||
  schema "shot_groups" do
 | 
			
		||||
  schema "shot_records" do
 | 
			
		||||
    field :count, :integer
 | 
			
		||||
    field :date, :date
 | 
			
		||||
    field :notes, :string
 | 
			
		||||
 | 
			
		||||
    field :user_id, :binary_id
 | 
			
		||||
    field :ammo_group_id, :binary_id
 | 
			
		||||
    field :pack_id, :binary_id
 | 
			
		||||
 | 
			
		||||
    timestamps()
 | 
			
		||||
  end
 | 
			
		||||
@@ -35,59 +35,57 @@ defmodule Cannery.ActivityLog.ShotGroup do
 | 
			
		||||
          count: integer,
 | 
			
		||||
          notes: String.t() | nil,
 | 
			
		||||
          date: Date.t() | nil,
 | 
			
		||||
          ammo_group_id: AmmoGroup.id(),
 | 
			
		||||
          pack_id: Pack.id(),
 | 
			
		||||
          user_id: User.id(),
 | 
			
		||||
          inserted_at: NaiveDateTime.t(),
 | 
			
		||||
          updated_at: NaiveDateTime.t()
 | 
			
		||||
        }
 | 
			
		||||
  @type new_shot_group :: %__MODULE__{}
 | 
			
		||||
  @type new_shot_record :: %__MODULE__{}
 | 
			
		||||
  @type id :: UUID.t()
 | 
			
		||||
  @type changeset :: Changeset.t(t() | new_shot_group())
 | 
			
		||||
  @type changeset :: Changeset.t(t() | new_shot_record())
 | 
			
		||||
 | 
			
		||||
  @doc false
 | 
			
		||||
  @spec create_changeset(
 | 
			
		||||
          new_shot_group(),
 | 
			
		||||
          new_shot_record(),
 | 
			
		||||
          User.t() | any(),
 | 
			
		||||
          AmmoGroup.t() | any(),
 | 
			
		||||
          Pack.t() | any(),
 | 
			
		||||
          attrs :: map()
 | 
			
		||||
        ) :: changeset()
 | 
			
		||||
  def create_changeset(
 | 
			
		||||
        shot_group,
 | 
			
		||||
        shot_record,
 | 
			
		||||
        %User{id: user_id},
 | 
			
		||||
        %AmmoGroup{id: ammo_group_id, user_id: user_id} = ammo_group,
 | 
			
		||||
        %Pack{id: pack_id, user_id: user_id} = pack,
 | 
			
		||||
        attrs
 | 
			
		||||
      ) do
 | 
			
		||||
    shot_group
 | 
			
		||||
    shot_record
 | 
			
		||||
    |> change(user_id: user_id)
 | 
			
		||||
    |> change(ammo_group_id: ammo_group_id)
 | 
			
		||||
    |> change(pack_id: pack_id)
 | 
			
		||||
    |> cast(attrs, [:count, :notes, :date])
 | 
			
		||||
    |> validate_length(:notes, max: 255)
 | 
			
		||||
    |> validate_create_shot_group_count(ammo_group)
 | 
			
		||||
    |> validate_required([:date, :ammo_group_id, :user_id])
 | 
			
		||||
    |> validate_create_shot_record_count(pack)
 | 
			
		||||
    |> validate_required([:date, :pack_id, :user_id])
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def create_changeset(shot_group, _invalid_user, _invalid_ammo_group, attrs) do
 | 
			
		||||
    shot_group
 | 
			
		||||
  def create_changeset(shot_record, _invalid_user, _invalid_pack, attrs) do
 | 
			
		||||
    shot_record
 | 
			
		||||
    |> cast(attrs, [:count, :notes, :date])
 | 
			
		||||
    |> validate_length(:notes, max: 255)
 | 
			
		||||
    |> validate_required([:ammo_group_id, :user_id])
 | 
			
		||||
    |> validate_required([:pack_id, :user_id])
 | 
			
		||||
    |> add_error(:invalid, dgettext("errors", "Please select a valid user and ammo pack"))
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp validate_create_shot_group_count(changeset, %AmmoGroup{count: ammo_group_count}) do
 | 
			
		||||
  defp validate_create_shot_record_count(changeset, %Pack{count: pack_count}) do
 | 
			
		||||
    case changeset |> Changeset.get_field(:count) do
 | 
			
		||||
      nil ->
 | 
			
		||||
        changeset |> Changeset.add_error(:ammo_left, dgettext("errors", "can't be blank"))
 | 
			
		||||
 | 
			
		||||
      count when count > ammo_group_count ->
 | 
			
		||||
      count when count > pack_count ->
 | 
			
		||||
        changeset
 | 
			
		||||
        |> Changeset.add_error(:ammo_left, dgettext("errors", "Ammo left must be at least 0"))
 | 
			
		||||
 | 
			
		||||
      count when count <= 0 ->
 | 
			
		||||
        error =
 | 
			
		||||
          dgettext("errors", "Ammo left can be at most %{count} rounds",
 | 
			
		||||
            count: ammo_group_count - 1
 | 
			
		||||
          )
 | 
			
		||||
          dgettext("errors", "Ammo left can be at most %{count} rounds", count: pack_count - 1)
 | 
			
		||||
 | 
			
		||||
        changeset |> Changeset.add_error(:ammo_left, error)
 | 
			
		||||
 | 
			
		||||
@@ -97,29 +95,28 @@ defmodule Cannery.ActivityLog.ShotGroup do
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @doc false
 | 
			
		||||
  @spec update_changeset(t() | new_shot_group(), User.t(), attrs :: map()) :: changeset()
 | 
			
		||||
  def update_changeset(%__MODULE__{} = shot_group, user, attrs) do
 | 
			
		||||
    shot_group
 | 
			
		||||
  @spec update_changeset(t() | new_shot_record(), User.t(), attrs :: map()) :: changeset()
 | 
			
		||||
  def update_changeset(%__MODULE__{} = shot_record, user, attrs) do
 | 
			
		||||
    shot_record
 | 
			
		||||
    |> cast(attrs, [:count, :notes, :date])
 | 
			
		||||
    |> validate_length(:notes, max: 255)
 | 
			
		||||
    |> validate_number(:count, greater_than: 0)
 | 
			
		||||
    |> validate_required([:count, :date])
 | 
			
		||||
    |> validate_update_shot_group_count(shot_group, user)
 | 
			
		||||
    |> validate_update_shot_record_count(shot_record, user)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp validate_update_shot_group_count(
 | 
			
		||||
  defp validate_update_shot_record_count(
 | 
			
		||||
         changeset,
 | 
			
		||||
         %__MODULE__{ammo_group_id: ammo_group_id, count: count},
 | 
			
		||||
         %__MODULE__{pack_id: pack_id, count: count},
 | 
			
		||||
         user
 | 
			
		||||
       ) do
 | 
			
		||||
    %{count: ammo_group_count} = Ammo.get_ammo_group!(ammo_group_id, user)
 | 
			
		||||
    %{count: pack_count} = Ammo.get_pack!(pack_id, user)
 | 
			
		||||
 | 
			
		||||
    new_shot_group_count = changeset |> Changeset.get_field(:count)
 | 
			
		||||
    shot_diff_to_add = new_shot_group_count - count
 | 
			
		||||
    new_shot_record_count = changeset |> Changeset.get_field(:count)
 | 
			
		||||
    shot_diff_to_add = new_shot_record_count - count
 | 
			
		||||
 | 
			
		||||
    if shot_diff_to_add > ammo_group_count do
 | 
			
		||||
      error =
 | 
			
		||||
        dgettext("errors", "Count can be at most %{count} shots", count: ammo_group_count + count)
 | 
			
		||||
    if shot_diff_to_add > pack_count do
 | 
			
		||||
      error = dgettext("errors", "Count can be at most %{count} shots", count: pack_count + count)
 | 
			
		||||
 | 
			
		||||
      changeset |> Changeset.add_error(:count, error)
 | 
			
		||||
    else
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
defmodule Cannery.Ammo.AmmoGroup do
 | 
			
		||||
defmodule Cannery.Ammo.Pack do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  A group of a certain ammunition type.
 | 
			
		||||
 | 
			
		||||
@@ -9,7 +9,7 @@ defmodule Cannery.Ammo.AmmoGroup do
 | 
			
		||||
  use Ecto.Schema
 | 
			
		||||
  import CanneryWeb.Gettext
 | 
			
		||||
  import Ecto.Changeset
 | 
			
		||||
  alias Cannery.Ammo.AmmoType
 | 
			
		||||
  alias Cannery.Ammo.Type
 | 
			
		||||
  alias Cannery.{Accounts.User, Containers, Containers.Container}
 | 
			
		||||
  alias Ecto.{Changeset, UUID}
 | 
			
		||||
 | 
			
		||||
@@ -19,20 +19,22 @@ defmodule Cannery.Ammo.AmmoGroup do
 | 
			
		||||
             :count,
 | 
			
		||||
             :notes,
 | 
			
		||||
             :price_paid,
 | 
			
		||||
             :lot_number,
 | 
			
		||||
             :staged,
 | 
			
		||||
             :ammo_type_id,
 | 
			
		||||
             :type_id,
 | 
			
		||||
             :container_id
 | 
			
		||||
           ]}
 | 
			
		||||
  @primary_key {:id, :binary_id, autogenerate: true}
 | 
			
		||||
  @foreign_key_type :binary_id
 | 
			
		||||
  schema "ammo_groups" do
 | 
			
		||||
  schema "packs" do
 | 
			
		||||
    field :count, :integer
 | 
			
		||||
    field :notes, :string
 | 
			
		||||
    field :price_paid, :float
 | 
			
		||||
    field :staged, :boolean, default: false
 | 
			
		||||
    field :lot_number, :string
 | 
			
		||||
    field :purchased_on, :date
 | 
			
		||||
 | 
			
		||||
    belongs_to :ammo_type, AmmoType
 | 
			
		||||
    belongs_to :type, Type
 | 
			
		||||
    field :container_id, :binary_id
 | 
			
		||||
    field :user_id, :binary_id
 | 
			
		||||
 | 
			
		||||
@@ -45,60 +47,76 @@ defmodule Cannery.Ammo.AmmoGroup do
 | 
			
		||||
          notes: String.t() | nil,
 | 
			
		||||
          price_paid: float() | nil,
 | 
			
		||||
          staged: boolean(),
 | 
			
		||||
          lot_number: String.t() | nil,
 | 
			
		||||
          purchased_on: Date.t(),
 | 
			
		||||
          ammo_type: AmmoType.t() | nil,
 | 
			
		||||
          ammo_type_id: AmmoType.id(),
 | 
			
		||||
          type: Type.t() | nil,
 | 
			
		||||
          type_id: Type.id(),
 | 
			
		||||
          container_id: Container.id(),
 | 
			
		||||
          user_id: User.id(),
 | 
			
		||||
          inserted_at: NaiveDateTime.t(),
 | 
			
		||||
          updated_at: NaiveDateTime.t()
 | 
			
		||||
        }
 | 
			
		||||
  @type new_ammo_group :: %__MODULE__{}
 | 
			
		||||
  @type new_pack :: %__MODULE__{}
 | 
			
		||||
  @type id :: UUID.t()
 | 
			
		||||
  @type changeset :: Changeset.t(t() | new_ammo_group())
 | 
			
		||||
  @type changeset :: Changeset.t(t() | new_pack())
 | 
			
		||||
 | 
			
		||||
  @doc false
 | 
			
		||||
  @spec create_changeset(
 | 
			
		||||
          new_ammo_group(),
 | 
			
		||||
          AmmoType.t() | nil,
 | 
			
		||||
          new_pack(),
 | 
			
		||||
          Type.t() | nil,
 | 
			
		||||
          Container.t() | nil,
 | 
			
		||||
          User.t(),
 | 
			
		||||
          attrs :: map()
 | 
			
		||||
        ) :: changeset()
 | 
			
		||||
  def create_changeset(
 | 
			
		||||
        ammo_group,
 | 
			
		||||
        %AmmoType{id: ammo_type_id},
 | 
			
		||||
        pack,
 | 
			
		||||
        %Type{id: type_id},
 | 
			
		||||
        %Container{id: container_id, user_id: user_id},
 | 
			
		||||
        %User{id: user_id},
 | 
			
		||||
        attrs
 | 
			
		||||
      )
 | 
			
		||||
      when is_binary(ammo_type_id) and is_binary(container_id) and is_binary(user_id) do
 | 
			
		||||
    ammo_group
 | 
			
		||||
    |> change(ammo_type_id: ammo_type_id)
 | 
			
		||||
      when is_binary(type_id) and is_binary(container_id) and is_binary(user_id) do
 | 
			
		||||
    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])
 | 
			
		||||
    |> cast(attrs, [:count, :price_paid, :notes, :staged, :purchased_on, :lot_number])
 | 
			
		||||
    |> validate_number(:count, greater_than: 0)
 | 
			
		||||
    |> validate_required([:count, :staged, :purchased_on, :ammo_type_id, :container_id, :user_id])
 | 
			
		||||
    |> 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 ammo type and container
 | 
			
		||||
  Invalid changeset, used to prompt user to select type and container
 | 
			
		||||
  """
 | 
			
		||||
  def create_changeset(ammo_group, _invalid_ammo_type, _invalid_container, _invalid_user, attrs) do
 | 
			
		||||
    ammo_group
 | 
			
		||||
    |> cast(attrs, [:ammo_type_id, :container_id])
 | 
			
		||||
    |> validate_required([:ammo_type_id, :container_id])
 | 
			
		||||
    |> add_error(:invalid, dgettext("errors", "Please select an ammo 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_ammo_group(), attrs :: map(), User.t()) :: changeset()
 | 
			
		||||
  def update_changeset(ammo_group, attrs, user) do
 | 
			
		||||
    ammo_group
 | 
			
		||||
    |> cast(attrs, [:count, :price_paid, :notes, :staged, :purchased_on, :container_id])
 | 
			
		||||
  @spec update_changeset(t() | new_pack(), attrs :: map(), User.t()) :: changeset()
 | 
			
		||||
  def update_changeset(pack, attrs, user) do
 | 
			
		||||
    pack
 | 
			
		||||
    |> cast(attrs, [
 | 
			
		||||
      :count,
 | 
			
		||||
      :price_paid,
 | 
			
		||||
      :notes,
 | 
			
		||||
      :staged,
 | 
			
		||||
      :purchased_on,
 | 
			
		||||
      :lot_number,
 | 
			
		||||
      :container_id
 | 
			
		||||
    ])
 | 
			
		||||
    |> validate_number(:count, greater_than_or_equal_to: 0)
 | 
			
		||||
    |> validate_number(:price_paid, greater_than_or_equal_to: 0)
 | 
			
		||||
    |> validate_container_id(user)
 | 
			
		||||
    |> validate_length(:lot_number, max: 255)
 | 
			
		||||
    |> validate_required([:count, :staged, :purchased_on, :container_id])
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -113,12 +131,12 @@ defmodule Cannery.Ammo.AmmoGroup do
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @doc """
 | 
			
		||||
  This range changeset is used when "using up" ammo groups, and allows for
 | 
			
		||||
  This range changeset is used when "using up" packs, and allows for
 | 
			
		||||
  updating the count to 0
 | 
			
		||||
  """
 | 
			
		||||
  @spec range_changeset(t() | new_ammo_group(), attrs :: map()) :: changeset()
 | 
			
		||||
  def range_changeset(ammo_group, attrs) do
 | 
			
		||||
    ammo_group
 | 
			
		||||
  @spec range_changeset(t() | new_pack(), attrs :: map()) :: changeset()
 | 
			
		||||
  def range_changeset(pack, attrs) do
 | 
			
		||||
    pack
 | 
			
		||||
    |> cast(attrs, [:count, :staged])
 | 
			
		||||
    |> validate_required([:count, :staged])
 | 
			
		||||
  end
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
defmodule Cannery.Ammo.AmmoType do
 | 
			
		||||
defmodule Cannery.Ammo.Type do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  An ammunition type.
 | 
			
		||||
 | 
			
		||||
@@ -8,41 +8,51 @@ defmodule Cannery.Ammo.AmmoType do
 | 
			
		||||
  use Ecto.Schema
 | 
			
		||||
  import Ecto.Changeset
 | 
			
		||||
  alias Cannery.Accounts.User
 | 
			
		||||
  alias Cannery.Ammo.AmmoGroup
 | 
			
		||||
  alias Cannery.Ammo.Pack
 | 
			
		||||
  alias Ecto.{Changeset, UUID}
 | 
			
		||||
 | 
			
		||||
  @derive {Jason.Encoder,
 | 
			
		||||
           only: [
 | 
			
		||||
             :id,
 | 
			
		||||
             :name,
 | 
			
		||||
             :desc,
 | 
			
		||||
             :class,
 | 
			
		||||
             :bullet_type,
 | 
			
		||||
             :bullet_core,
 | 
			
		||||
             :cartridge,
 | 
			
		||||
             :caliber,
 | 
			
		||||
             :case_material,
 | 
			
		||||
             :jacket_type,
 | 
			
		||||
             :muzzle_velocity,
 | 
			
		||||
             :powder_type,
 | 
			
		||||
             :powder_grains_per_charge,
 | 
			
		||||
             :grains,
 | 
			
		||||
             :pressure,
 | 
			
		||||
             :primer_type,
 | 
			
		||||
             :firing_type,
 | 
			
		||||
             :manufacturer,
 | 
			
		||||
             :upc,
 | 
			
		||||
             :tracer,
 | 
			
		||||
             :incendiary,
 | 
			
		||||
             :blank,
 | 
			
		||||
             :corrosive,
 | 
			
		||||
             :manufacturer,
 | 
			
		||||
             :upc
 | 
			
		||||
             :cartridge,
 | 
			
		||||
             :jacket_type,
 | 
			
		||||
             :powder_grains_per_charge,
 | 
			
		||||
             :muzzle_velocity,
 | 
			
		||||
             :wadding,
 | 
			
		||||
             :shot_type,
 | 
			
		||||
             :shot_material,
 | 
			
		||||
             :shot_size,
 | 
			
		||||
             :unfired_length,
 | 
			
		||||
             :brass_height,
 | 
			
		||||
             :chamber_size,
 | 
			
		||||
             :load_grains,
 | 
			
		||||
             :shot_charge_weight,
 | 
			
		||||
             :dram_equivalent
 | 
			
		||||
           ]}
 | 
			
		||||
  @primary_key {:id, :binary_id, autogenerate: true}
 | 
			
		||||
  @foreign_key_type :binary_id
 | 
			
		||||
  schema "ammo_types" do
 | 
			
		||||
  schema "types" do
 | 
			
		||||
    field :name, :string
 | 
			
		||||
    field :desc, :string
 | 
			
		||||
 | 
			
		||||
    field :type, Ecto.Enum, values: [:rifle, :shotgun, :pistol]
 | 
			
		||||
    field :class, Ecto.Enum, values: [:rifle, :shotgun, :pistol]
 | 
			
		||||
 | 
			
		||||
    # common fields
 | 
			
		||||
    # https://shootersreference.com/reloadingdata/bullet_abbreviations/
 | 
			
		||||
@@ -83,7 +93,7 @@ defmodule Cannery.Ammo.AmmoType do
 | 
			
		||||
    field :dram_equivalent, :string
 | 
			
		||||
 | 
			
		||||
    field :user_id, :binary_id
 | 
			
		||||
    has_many :ammo_groups, AmmoGroup
 | 
			
		||||
    has_many :packs, Pack
 | 
			
		||||
 | 
			
		||||
    timestamps()
 | 
			
		||||
  end
 | 
			
		||||
@@ -92,20 +102,26 @@ defmodule Cannery.Ammo.AmmoType do
 | 
			
		||||
          id: id(),
 | 
			
		||||
          name: String.t(),
 | 
			
		||||
          desc: String.t() | nil,
 | 
			
		||||
          type: type(),
 | 
			
		||||
          class: class(),
 | 
			
		||||
          bullet_type: String.t() | nil,
 | 
			
		||||
          bullet_core: String.t() | nil,
 | 
			
		||||
          cartridge: String.t() | nil,
 | 
			
		||||
          caliber: String.t() | nil,
 | 
			
		||||
          case_material: String.t() | nil,
 | 
			
		||||
          jacket_type: String.t() | nil,
 | 
			
		||||
          muzzle_velocity: integer() | nil,
 | 
			
		||||
          powder_type: String.t() | nil,
 | 
			
		||||
          powder_grains_per_charge: integer() | nil,
 | 
			
		||||
          grains: integer() | nil,
 | 
			
		||||
          pressure: String.t() | nil,
 | 
			
		||||
          primer_type: String.t() | nil,
 | 
			
		||||
          firing_type: String.t() | nil,
 | 
			
		||||
          manufacturer: String.t() | nil,
 | 
			
		||||
          upc: String.t() | nil,
 | 
			
		||||
          tracer: boolean(),
 | 
			
		||||
          incendiary: boolean(),
 | 
			
		||||
          blank: boolean(),
 | 
			
		||||
          corrosive: boolean(),
 | 
			
		||||
          cartridge: String.t() | nil,
 | 
			
		||||
          jacket_type: String.t() | nil,
 | 
			
		||||
          powder_grains_per_charge: integer() | nil,
 | 
			
		||||
          muzzle_velocity: integer() | nil,
 | 
			
		||||
          wadding: String.t() | nil,
 | 
			
		||||
          shot_type: String.t() | nil,
 | 
			
		||||
          shot_material: String.t() | nil,
 | 
			
		||||
@@ -116,41 +132,41 @@ defmodule Cannery.Ammo.AmmoType do
 | 
			
		||||
          load_grains: integer() | nil,
 | 
			
		||||
          shot_charge_weight: String.t() | nil,
 | 
			
		||||
          dram_equivalent: String.t() | nil,
 | 
			
		||||
          tracer: boolean(),
 | 
			
		||||
          incendiary: boolean(),
 | 
			
		||||
          blank: boolean(),
 | 
			
		||||
          corrosive: boolean(),
 | 
			
		||||
          manufacturer: String.t() | nil,
 | 
			
		||||
          upc: String.t() | nil,
 | 
			
		||||
          user_id: User.id(),
 | 
			
		||||
          ammo_groups: [AmmoGroup.t()] | nil,
 | 
			
		||||
          packs: [Pack.t()] | nil,
 | 
			
		||||
          inserted_at: NaiveDateTime.t(),
 | 
			
		||||
          updated_at: NaiveDateTime.t()
 | 
			
		||||
        }
 | 
			
		||||
  @type new_ammo_type :: %__MODULE__{}
 | 
			
		||||
  @type new_type :: %__MODULE__{}
 | 
			
		||||
  @type id :: UUID.t()
 | 
			
		||||
  @type changeset :: Changeset.t(t() | new_ammo_type())
 | 
			
		||||
  @type type :: :rifle | :shotgun | :pistol | nil
 | 
			
		||||
  @type changeset :: Changeset.t(t() | new_type())
 | 
			
		||||
  @type class :: :rifle | :shotgun | :pistol | nil
 | 
			
		||||
 | 
			
		||||
  @spec changeset_fields() :: [atom()]
 | 
			
		||||
  defp changeset_fields,
 | 
			
		||||
    do: [
 | 
			
		||||
      :name,
 | 
			
		||||
      :desc,
 | 
			
		||||
      :type,
 | 
			
		||||
      :class,
 | 
			
		||||
      :bullet_type,
 | 
			
		||||
      :bullet_core,
 | 
			
		||||
      :cartridge,
 | 
			
		||||
      :caliber,
 | 
			
		||||
      :case_material,
 | 
			
		||||
      :jacket_type,
 | 
			
		||||
      :muzzle_velocity,
 | 
			
		||||
      :powder_type,
 | 
			
		||||
      :powder_grains_per_charge,
 | 
			
		||||
      :grains,
 | 
			
		||||
      :pressure,
 | 
			
		||||
      :primer_type,
 | 
			
		||||
      :firing_type,
 | 
			
		||||
      :manufacturer,
 | 
			
		||||
      :upc,
 | 
			
		||||
      :tracer,
 | 
			
		||||
      :incendiary,
 | 
			
		||||
      :blank,
 | 
			
		||||
      :corrosive,
 | 
			
		||||
      :cartridge,
 | 
			
		||||
      :jacket_type,
 | 
			
		||||
      :powder_grains_per_charge,
 | 
			
		||||
      :muzzle_velocity,
 | 
			
		||||
      :wadding,
 | 
			
		||||
      :shot_type,
 | 
			
		||||
      :shot_material,
 | 
			
		||||
@@ -160,29 +176,26 @@ defmodule Cannery.Ammo.AmmoType do
 | 
			
		||||
      :chamber_size,
 | 
			
		||||
      :load_grains,
 | 
			
		||||
      :shot_charge_weight,
 | 
			
		||||
      :dram_equivalent,
 | 
			
		||||
      :tracer,
 | 
			
		||||
      :incendiary,
 | 
			
		||||
      :blank,
 | 
			
		||||
      :corrosive,
 | 
			
		||||
      :manufacturer,
 | 
			
		||||
      :upc
 | 
			
		||||
      :dram_equivalent
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
  @spec string_fields() :: [atom()]
 | 
			
		||||
  defp string_fields,
 | 
			
		||||
    do: [
 | 
			
		||||
      :name,
 | 
			
		||||
      :desc,
 | 
			
		||||
      :bullet_type,
 | 
			
		||||
      :bullet_core,
 | 
			
		||||
      :cartridge,
 | 
			
		||||
      :caliber,
 | 
			
		||||
      :case_material,
 | 
			
		||||
      :jacket_type,
 | 
			
		||||
      :powder_type,
 | 
			
		||||
      :pressure,
 | 
			
		||||
      :primer_type,
 | 
			
		||||
      :firing_type,
 | 
			
		||||
      :manufacturer,
 | 
			
		||||
      :upc,
 | 
			
		||||
      :cartridge,
 | 
			
		||||
      :jacket_type,
 | 
			
		||||
      :wadding,
 | 
			
		||||
      :shot_type,
 | 
			
		||||
      :shot_material,
 | 
			
		||||
@@ -191,16 +204,14 @@ defmodule Cannery.Ammo.AmmoType do
 | 
			
		||||
      :brass_height,
 | 
			
		||||
      :chamber_size,
 | 
			
		||||
      :shot_charge_weight,
 | 
			
		||||
      :dram_equivalent,
 | 
			
		||||
      :manufacturer,
 | 
			
		||||
      :upc
 | 
			
		||||
      :dram_equivalent
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
  @doc false
 | 
			
		||||
  @spec create_changeset(new_ammo_type(), User.t(), attrs :: map()) :: changeset()
 | 
			
		||||
  def create_changeset(ammo_type, %User{id: user_id}, attrs) do
 | 
			
		||||
  @spec create_changeset(new_type(), User.t(), attrs :: map()) :: changeset()
 | 
			
		||||
  def create_changeset(type, %User{id: user_id}, attrs) do
 | 
			
		||||
    changeset =
 | 
			
		||||
      ammo_type
 | 
			
		||||
      type
 | 
			
		||||
      |> change(user_id: user_id)
 | 
			
		||||
      |> cast(attrs, changeset_fields())
 | 
			
		||||
 | 
			
		||||
@@ -210,10 +221,10 @@ defmodule Cannery.Ammo.AmmoType do
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @doc false
 | 
			
		||||
  @spec update_changeset(t() | new_ammo_type(), attrs :: map()) :: changeset()
 | 
			
		||||
  def update_changeset(ammo_type, attrs) do
 | 
			
		||||
  @spec update_changeset(t() | new_type(), attrs :: map()) :: changeset()
 | 
			
		||||
  def update_changeset(type, attrs) do
 | 
			
		||||
    changeset =
 | 
			
		||||
      ammo_type
 | 
			
		||||
      type
 | 
			
		||||
      |> cast(attrs, changeset_fields())
 | 
			
		||||
 | 
			
		||||
    string_fields()
 | 
			
		||||
@@ -5,7 +5,7 @@ defmodule Cannery.Containers do
 | 
			
		||||
 | 
			
		||||
  import CanneryWeb.Gettext
 | 
			
		||||
  import Ecto.Query, warn: false
 | 
			
		||||
  alias Cannery.{Accounts.User, Ammo.AmmoGroup, Repo}
 | 
			
		||||
  alias Cannery.{Accounts.User, Ammo.Pack, Repo}
 | 
			
		||||
  alias Cannery.Containers.{Container, ContainerTag, Tag}
 | 
			
		||||
  alias Ecto.Changeset
 | 
			
		||||
 | 
			
		||||
@@ -203,7 +203,7 @@ defmodule Cannery.Containers do
 | 
			
		||||
          {:ok, Container.t()} | {:error, Container.changeset()}
 | 
			
		||||
  def delete_container(%Container{user_id: user_id} = container, %User{id: user_id}) do
 | 
			
		||||
    Repo.one(
 | 
			
		||||
      from ag in AmmoGroup,
 | 
			
		||||
      from ag in Pack,
 | 
			
		||||
        where: ag.container_id == ^container.id,
 | 
			
		||||
        select: count(ag.id)
 | 
			
		||||
    )
 | 
			
		||||
@@ -221,7 +221,7 @@ defmodule Cannery.Containers do
 | 
			
		||||
 | 
			
		||||
        container
 | 
			
		||||
        |> Container.update_changeset(%{})
 | 
			
		||||
        |> Changeset.add_error(:ammo_groups, error)
 | 
			
		||||
        |> Changeset.add_error(:packs, error)
 | 
			
		||||
        |> Changeset.apply_action(:delete)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
defmodule CanneryWeb.Components.AddShotGroupComponent do
 | 
			
		||||
defmodule CanneryWeb.Components.AddShotRecordComponent do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  Livecomponent that can create a ShotGroup
 | 
			
		||||
  Livecomponent that can create a ShotRecord
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb, :live_component
 | 
			
		||||
  alias Cannery.{Accounts.User, ActivityLog, ActivityLog.ShotGroup, Ammo.AmmoGroup}
 | 
			
		||||
  alias Cannery.{Accounts.User, ActivityLog, ActivityLog.ShotRecord, Ammo.Pack}
 | 
			
		||||
  alias Ecto.Changeset
 | 
			
		||||
  alias Phoenix.LiveView.{JS, Socket}
 | 
			
		||||
 | 
			
		||||
@@ -12,15 +12,15 @@ defmodule CanneryWeb.Components.AddShotGroupComponent do
 | 
			
		||||
  @spec update(
 | 
			
		||||
          %{
 | 
			
		||||
            required(:current_user) => User.t(),
 | 
			
		||||
            required(:ammo_group) => AmmoGroup.t(),
 | 
			
		||||
            required(:pack) => Pack.t(),
 | 
			
		||||
            optional(any()) => any()
 | 
			
		||||
          },
 | 
			
		||||
          Socket.t()
 | 
			
		||||
        ) :: {:ok, Socket.t()}
 | 
			
		||||
  def update(%{ammo_group: ammo_group, current_user: current_user} = assigns, socket) do
 | 
			
		||||
  def update(%{pack: pack, current_user: current_user} = assigns, socket) do
 | 
			
		||||
    changeset =
 | 
			
		||||
      %ShotGroup{date: Date.utc_today()}
 | 
			
		||||
      |> ShotGroup.create_changeset(current_user, ammo_group, %{})
 | 
			
		||||
      %ShotRecord{date: Date.utc_today()}
 | 
			
		||||
      |> ShotRecord.create_changeset(current_user, pack, %{})
 | 
			
		||||
 | 
			
		||||
    {:ok, socket |> assign(assigns) |> assign(:changeset, changeset)}
 | 
			
		||||
  end
 | 
			
		||||
@@ -28,12 +28,12 @@ defmodule CanneryWeb.Components.AddShotGroupComponent do
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_event(
 | 
			
		||||
        "validate",
 | 
			
		||||
        %{"shot_group" => shot_group_params},
 | 
			
		||||
        %{assigns: %{ammo_group: ammo_group, current_user: current_user}} = socket
 | 
			
		||||
        %{"shot_record" => shot_record_params},
 | 
			
		||||
        %{assigns: %{pack: pack, current_user: current_user}} = socket
 | 
			
		||||
      ) do
 | 
			
		||||
    params = shot_group_params |> process_params(ammo_group)
 | 
			
		||||
    params = shot_record_params |> process_params(pack)
 | 
			
		||||
 | 
			
		||||
    changeset = %ShotGroup{} |> ShotGroup.create_changeset(current_user, ammo_group, params)
 | 
			
		||||
    changeset = %ShotRecord{} |> ShotRecord.create_changeset(current_user, pack, params)
 | 
			
		||||
 | 
			
		||||
    changeset =
 | 
			
		||||
      case changeset |> Changeset.apply_action(:validate) do
 | 
			
		||||
@@ -46,17 +46,17 @@ defmodule CanneryWeb.Components.AddShotGroupComponent do
 | 
			
		||||
 | 
			
		||||
  def handle_event(
 | 
			
		||||
        "save",
 | 
			
		||||
        %{"shot_group" => shot_group_params},
 | 
			
		||||
        %{"shot_record" => shot_record_params},
 | 
			
		||||
        %{
 | 
			
		||||
          assigns: %{ammo_group: ammo_group, current_user: current_user, return_to: return_to}
 | 
			
		||||
          assigns: %{pack: pack, current_user: current_user, return_to: return_to}
 | 
			
		||||
        } = socket
 | 
			
		||||
      ) do
 | 
			
		||||
    socket =
 | 
			
		||||
      shot_group_params
 | 
			
		||||
      |> process_params(ammo_group)
 | 
			
		||||
      |> ActivityLog.create_shot_group(current_user, ammo_group)
 | 
			
		||||
      shot_record_params
 | 
			
		||||
      |> process_params(pack)
 | 
			
		||||
      |> ActivityLog.create_shot_record(current_user, pack)
 | 
			
		||||
      |> case do
 | 
			
		||||
        {:ok, _shot_group} ->
 | 
			
		||||
        {:ok, _shot_record} ->
 | 
			
		||||
          prompt = dgettext("prompts", "Shots recorded successfully")
 | 
			
		||||
          socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
 | 
			
		||||
 | 
			
		||||
@@ -68,8 +68,8 @@ defmodule CanneryWeb.Components.AddShotGroupComponent do
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # calculate count from shots left
 | 
			
		||||
  defp process_params(params, %AmmoGroup{count: count}) do
 | 
			
		||||
    shot_group_count =
 | 
			
		||||
  defp process_params(params, %Pack{count: count}) do
 | 
			
		||||
    shot_record_count =
 | 
			
		||||
      if params |> Map.get("ammo_left", "") == "" do
 | 
			
		||||
        nil
 | 
			
		||||
      else
 | 
			
		||||
@@ -77,6 +77,6 @@ defmodule CanneryWeb.Components.AddShotGroupComponent do
 | 
			
		||||
        count - new_count
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
    params |> Map.put("count", shot_group_count)
 | 
			
		||||
    params |> Map.put("count", shot_record_count)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
  <.form
 | 
			
		||||
    :let={f}
 | 
			
		||||
    for={@changeset}
 | 
			
		||||
    id="shot-group-form"
 | 
			
		||||
    id="shot-record-form"
 | 
			
		||||
    class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
 | 
			
		||||
    phx-target={@myself}
 | 
			
		||||
    phx-change="validate"
 | 
			
		||||
@@ -22,14 +22,14 @@
 | 
			
		||||
    <%= label(f, :ammo_left, gettext("Rounds left"), class: "title text-lg text-primary-600") %>
 | 
			
		||||
    <%= number_input(f, :ammo_left,
 | 
			
		||||
      min: 0,
 | 
			
		||||
      max: @ammo_group.count - 1,
 | 
			
		||||
      max: @pack.count - 1,
 | 
			
		||||
      placeholder: gettext("Rounds left"),
 | 
			
		||||
      class: "input input-primary"
 | 
			
		||||
    ) %>
 | 
			
		||||
    <button
 | 
			
		||||
      type="button"
 | 
			
		||||
      class="mx-2 my-1 text-sm btn btn-primary"
 | 
			
		||||
      phx-click={JS.dispatch("cannery:set-zero", to: "#shot-group-form_ammo_left")}
 | 
			
		||||
      phx-click={JS.dispatch("cannery:set-zero", to: "#shot-record-form_ammo_left")}
 | 
			
		||||
    >
 | 
			
		||||
      <%= gettext("Used up!") %>
 | 
			
		||||
    </button>
 | 
			
		||||
@@ -37,7 +37,7 @@
 | 
			
		||||
 | 
			
		||||
    <%= label(f, :notes, gettext("Notes"), class: "title text-lg text-primary-600") %>
 | 
			
		||||
    <%= textarea(f, :notes,
 | 
			
		||||
      id: "add-shot-group-form-notes",
 | 
			
		||||
      id: "add-shot-record-form-notes",
 | 
			
		||||
      class: "input input-primary col-span-2",
 | 
			
		||||
      maxlength: 255,
 | 
			
		||||
      placeholder: gettext("Really great weather"),
 | 
			
		||||
@@ -71,7 +71,7 @@ defmodule CanneryWeb.Components.ContainerTableComponent do
 | 
			
		||||
      current_user: current_user,
 | 
			
		||||
      tag_actions: tag_actions,
 | 
			
		||||
      actions: actions,
 | 
			
		||||
      pack_count: Ammo.get_ammo_groups_count_for_containers(containers, current_user),
 | 
			
		||||
      pack_count: Ammo.get_packs_count_for_containers(containers, current_user),
 | 
			
		||||
      round_count: Ammo.get_round_count_for_containers(containers, current_user)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ defmodule CanneryWeb.CoreComponents do
 | 
			
		||||
  use Phoenix.Component
 | 
			
		||||
  import CanneryWeb.{Gettext, ViewHelpers}
 | 
			
		||||
  alias Cannery.{Accounts, Accounts.Invite, Accounts.User}
 | 
			
		||||
  alias Cannery.{Ammo, Ammo.AmmoGroup}
 | 
			
		||||
  alias Cannery.{Ammo, Ammo.Pack}
 | 
			
		||||
  alias Cannery.{Containers.Container, Containers.Tag}
 | 
			
		||||
  alias CanneryWeb.{Endpoint, HomeLive}
 | 
			
		||||
  alias CanneryWeb.Router.Helpers, as: Routes
 | 
			
		||||
@@ -86,7 +86,7 @@ defmodule CanneryWeb.CoreComponents do
 | 
			
		||||
 | 
			
		||||
  def simple_tag_card(assigns)
 | 
			
		||||
 | 
			
		||||
  attr :ammo_group, AmmoGroup, required: true
 | 
			
		||||
  attr :pack, Pack, required: true
 | 
			
		||||
  attr :current_user, User, required: true
 | 
			
		||||
  attr :original_count, :integer, default: nil
 | 
			
		||||
  attr :cpr, :integer, default: nil
 | 
			
		||||
@@ -94,7 +94,7 @@ defmodule CanneryWeb.CoreComponents do
 | 
			
		||||
  attr :container, Container, default: nil
 | 
			
		||||
  slot(:inner_block)
 | 
			
		||||
 | 
			
		||||
  def ammo_group_card(assigns)
 | 
			
		||||
  def pack_card(assigns)
 | 
			
		||||
 | 
			
		||||
  @spec display_currency(float()) :: String.t()
 | 
			
		||||
  defp display_currency(float), do: :erlang.float_to_binary(float, decimals: 2)
 | 
			
		||||
 
 | 
			
		||||
@@ -27,10 +27,10 @@
 | 
			
		||||
      <%= @container.location %>
 | 
			
		||||
    </span>
 | 
			
		||||
 | 
			
		||||
    <%= if @container |> Ammo.get_ammo_groups_count_for_container!(@current_user) != 0 do %>
 | 
			
		||||
    <%= if @container |> Ammo.get_packs_count_for_container!(@current_user) != 0 do %>
 | 
			
		||||
      <span class="rounded-lg title text-lg">
 | 
			
		||||
        <%= gettext("Packs:") %>
 | 
			
		||||
        <%= @container |> Ammo.get_ammo_groups_count_for_container!(@current_user) %>
 | 
			
		||||
        <%= @container |> Ammo.get_packs_count_for_container!(@current_user) %>
 | 
			
		||||
      </span>
 | 
			
		||||
 | 
			
		||||
      <span class="rounded-lg title text-lg">
 | 
			
		||||
 
 | 
			
		||||
@@ -1,48 +1,45 @@
 | 
			
		||||
<div
 | 
			
		||||
  id={"ammo_group-#{@ammo_group.id}"}
 | 
			
		||||
  id={"pack-#{@pack.id}"}
 | 
			
		||||
  class="mx-4 my-2 px-8 py-4
 | 
			
		||||
    flex flex-col justify-center items-center
 | 
			
		||||
    border border-gray-400 rounded-lg shadow-lg hover:shadow-md
 | 
			
		||||
    transition-all duration-300 ease-in-out"
 | 
			
		||||
>
 | 
			
		||||
  <.link navigate={Routes.ammo_group_show_path(Endpoint, :show, @ammo_group)} class="mb-2 link">
 | 
			
		||||
  <.link navigate={Routes.pack_show_path(Endpoint, :show, @pack)} class="mb-2 link">
 | 
			
		||||
    <h1 class="title text-xl title-primary-500">
 | 
			
		||||
      <%= @ammo_group.ammo_type.name %>
 | 
			
		||||
      <%= @pack.type.name %>
 | 
			
		||||
    </h1>
 | 
			
		||||
  </.link>
 | 
			
		||||
 | 
			
		||||
  <div class="flex flex-col justify-center items-center">
 | 
			
		||||
    <span class="rounded-lg title text-lg">
 | 
			
		||||
      <%= gettext("Count:") %>
 | 
			
		||||
      <%= if @ammo_group.count == 0, do: gettext("Empty"), else: @ammo_group.count %>
 | 
			
		||||
      <%= if @pack.count == 0, do: gettext("Empty"), else: @pack.count %>
 | 
			
		||||
    </span>
 | 
			
		||||
 | 
			
		||||
    <span
 | 
			
		||||
      :if={@original_count && @original_count != @ammo_group.count}
 | 
			
		||||
      class="rounded-lg title text-lg"
 | 
			
		||||
    >
 | 
			
		||||
    <span :if={@original_count && @original_count != @pack.count} class="rounded-lg title text-lg">
 | 
			
		||||
      <%= gettext("Original Count:") %>
 | 
			
		||||
      <%= @original_count %>
 | 
			
		||||
    </span>
 | 
			
		||||
 | 
			
		||||
    <span :if={@ammo_group.notes} class="rounded-lg title text-lg">
 | 
			
		||||
    <span :if={@pack.notes} class="rounded-lg title text-lg">
 | 
			
		||||
      <%= gettext("Notes:") %>
 | 
			
		||||
      <%= @ammo_group.notes %>
 | 
			
		||||
      <%= @pack.notes %>
 | 
			
		||||
    </span>
 | 
			
		||||
 | 
			
		||||
    <span :if={@ammo_group.purchased_on} class="rounded-lg title text-lg">
 | 
			
		||||
    <span :if={@pack.purchased_on} class="rounded-lg title text-lg">
 | 
			
		||||
      <%= gettext("Purchased on:") %>
 | 
			
		||||
      <.date id={"#{@ammo_group.id}-purchased-on"} date={@ammo_group.purchased_on} />
 | 
			
		||||
      <.date id={"#{@pack.id}-purchased-on"} date={@pack.purchased_on} />
 | 
			
		||||
    </span>
 | 
			
		||||
 | 
			
		||||
    <span :if={@last_used_date} class="rounded-lg title text-lg">
 | 
			
		||||
      <%= gettext("Last used on:") %>
 | 
			
		||||
      <.date id={"#{@ammo_group.id}-last-used-on"} date={@last_used_date} />
 | 
			
		||||
      <.date id={"#{@pack.id}-last-used-on"} date={@last_used_date} />
 | 
			
		||||
    </span>
 | 
			
		||||
 | 
			
		||||
    <span :if={@ammo_group.price_paid} class="rounded-lg title text-lg">
 | 
			
		||||
    <span :if={@pack.price_paid} class="rounded-lg title text-lg">
 | 
			
		||||
      <%= gettext("Price paid:") %>
 | 
			
		||||
      <%= gettext("$%{amount}", amount: display_currency(@ammo_group.price_paid)) %>
 | 
			
		||||
      <%= gettext("$%{amount}", amount: display_currency(@pack.price_paid)) %>
 | 
			
		||||
    </span>
 | 
			
		||||
 | 
			
		||||
    <span :if={@cpr} class="rounded-lg title text-lg">
 | 
			
		||||
@@ -50,6 +47,11 @@
 | 
			
		||||
      <%= gettext("$%{amount}", amount: display_currency(@cpr)) %>
 | 
			
		||||
    </span>
 | 
			
		||||
 | 
			
		||||
    <span :if={@pack.lot_number} class="rounded-lg title text-lg">
 | 
			
		||||
      <%= gettext("Lot number:") %>
 | 
			
		||||
      <%= @pack.lot_number %>
 | 
			
		||||
    </span>
 | 
			
		||||
 | 
			
		||||
    <span :if={@container} class="rounded-lg title text-lg">
 | 
			
		||||
      <%= gettext("Container:") %>
 | 
			
		||||
 | 
			
		||||
@@ -44,7 +44,7 @@
 | 
			
		||||
        </li>
 | 
			
		||||
        <li class="mx-2 my-1">
 | 
			
		||||
          <.link
 | 
			
		||||
            navigate={Routes.ammo_type_index_path(Endpoint, :index)}
 | 
			
		||||
            navigate={Routes.type_index_path(Endpoint, :index)}
 | 
			
		||||
            class="text-white hover:underline"
 | 
			
		||||
          >
 | 
			
		||||
            <%= gettext("Catalog") %>
 | 
			
		||||
@@ -52,7 +52,7 @@
 | 
			
		||||
        </li>
 | 
			
		||||
        <li class="mx-2 my-1">
 | 
			
		||||
          <.link
 | 
			
		||||
            navigate={Routes.ammo_group_index_path(Endpoint, :index)}
 | 
			
		||||
            navigate={Routes.pack_index_path(Endpoint, :index)}
 | 
			
		||||
            class="text-white hover:underline"
 | 
			
		||||
          >
 | 
			
		||||
            <%= gettext("Ammo") %>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
defmodule CanneryWeb.Components.MoveAmmoGroupComponent do
 | 
			
		||||
defmodule CanneryWeb.Components.MovePackComponent do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  Livecomponent that can move an ammo group to another container
 | 
			
		||||
  Livecomponent that can move a pack to another container
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb, :live_component
 | 
			
		||||
  alias Cannery.{Accounts.User, Ammo, Ammo.AmmoGroup, Containers, Containers.Container}
 | 
			
		||||
  alias Cannery.{Accounts.User, Ammo, Ammo.Pack, Containers, Containers.Container}
 | 
			
		||||
  alias CanneryWeb.Endpoint
 | 
			
		||||
  alias Ecto.Changeset
 | 
			
		||||
  alias Phoenix.LiveView.Socket
 | 
			
		||||
@@ -13,17 +13,16 @@ defmodule CanneryWeb.Components.MoveAmmoGroupComponent do
 | 
			
		||||
  @spec update(
 | 
			
		||||
          %{
 | 
			
		||||
            required(:current_user) => User.t(),
 | 
			
		||||
            required(:ammo_group) => AmmoGroup.t(),
 | 
			
		||||
            required(:pack) => Pack.t(),
 | 
			
		||||
            optional(any()) => any()
 | 
			
		||||
          },
 | 
			
		||||
          Socket.t()
 | 
			
		||||
        ) :: {:ok, Socket.t()}
 | 
			
		||||
  def update(
 | 
			
		||||
        %{ammo_group: %{container_id: container_id} = ammo_group, current_user: current_user} =
 | 
			
		||||
          assigns,
 | 
			
		||||
        %{pack: %{container_id: container_id} = pack, current_user: current_user} = assigns,
 | 
			
		||||
        socket
 | 
			
		||||
      ) do
 | 
			
		||||
    changeset = ammo_group |> AmmoGroup.update_changeset(%{}, current_user)
 | 
			
		||||
    changeset = pack |> Pack.update_changeset(%{}, current_user)
 | 
			
		||||
 | 
			
		||||
    containers =
 | 
			
		||||
      Containers.list_containers(current_user)
 | 
			
		||||
@@ -41,16 +40,15 @@ defmodule CanneryWeb.Components.MoveAmmoGroupComponent do
 | 
			
		||||
  def handle_event(
 | 
			
		||||
        "move",
 | 
			
		||||
        %{"container_id" => container_id},
 | 
			
		||||
        %{assigns: %{ammo_group: ammo_group, current_user: current_user, return_to: return_to}} =
 | 
			
		||||
          socket
 | 
			
		||||
        %{assigns: %{pack: pack, current_user: current_user, return_to: return_to}} = socket
 | 
			
		||||
      ) do
 | 
			
		||||
    %{name: container_name} = Containers.get_container!(container_id, current_user)
 | 
			
		||||
 | 
			
		||||
    socket =
 | 
			
		||||
      ammo_group
 | 
			
		||||
      |> Ammo.update_ammo_group(%{"container_id" => container_id}, current_user)
 | 
			
		||||
      pack
 | 
			
		||||
      |> Ammo.update_pack(%{"container_id" => container_id}, current_user)
 | 
			
		||||
      |> case do
 | 
			
		||||
        {:ok, _ammo_group} ->
 | 
			
		||||
        {:ok, _pack} ->
 | 
			
		||||
          prompt = dgettext("prompts", "Ammo moved to %{name} successfully", name: container_name)
 | 
			
		||||
          socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
 | 
			
		||||
 | 
			
		||||
@@ -92,7 +90,7 @@ defmodule CanneryWeb.Components.MoveAmmoGroupComponent do
 | 
			
		||||
      <% else %>
 | 
			
		||||
        <.live_component
 | 
			
		||||
          module={CanneryWeb.Components.TableComponent}
 | 
			
		||||
          id="move_ammo_group_table"
 | 
			
		||||
          id="move_pack_table"
 | 
			
		||||
          columns={@columns}
 | 
			
		||||
          rows={@rows}
 | 
			
		||||
        />
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
defmodule CanneryWeb.Components.AmmoGroupTableComponent do
 | 
			
		||||
defmodule CanneryWeb.Components.PackTableComponent do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  A component that displays a list of ammo groups
 | 
			
		||||
  A component that displays a list of packs
 | 
			
		||||
  """
 | 
			
		||||
  use CanneryWeb, :live_component
 | 
			
		||||
  alias Cannery.{Accounts.User, Ammo.AmmoGroup, ComparableDate}
 | 
			
		||||
  alias Cannery.{Accounts.User, Ammo.Pack, ComparableDate}
 | 
			
		||||
  alias Cannery.{ActivityLog, Ammo, Containers}
 | 
			
		||||
  alias CanneryWeb.Components.TableComponent
 | 
			
		||||
  alias Ecto.UUID
 | 
			
		||||
@@ -14,9 +14,9 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do
 | 
			
		||||
          %{
 | 
			
		||||
            required(:id) => UUID.t(),
 | 
			
		||||
            required(:current_user) => User.t(),
 | 
			
		||||
            required(:ammo_groups) => [AmmoGroup.t()],
 | 
			
		||||
            required(:packs) => [Pack.t()],
 | 
			
		||||
            required(:show_used) => boolean(),
 | 
			
		||||
            optional(:ammo_type) => Rendered.t(),
 | 
			
		||||
            optional(:type) => Rendered.t(),
 | 
			
		||||
            optional(:range) => Rendered.t(),
 | 
			
		||||
            optional(:container) => Rendered.t(),
 | 
			
		||||
            optional(:actions) => Rendered.t(),
 | 
			
		||||
@@ -25,28 +25,27 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do
 | 
			
		||||
          Socket.t()
 | 
			
		||||
        ) :: {:ok, Socket.t()}
 | 
			
		||||
  def update(
 | 
			
		||||
        %{id: _id, ammo_groups: _ammo_group, current_user: _current_user, show_used: _show_used} =
 | 
			
		||||
          assigns,
 | 
			
		||||
        %{id: _id, packs: _pack, current_user: _current_user, show_used: _show_used} = assigns,
 | 
			
		||||
        socket
 | 
			
		||||
      ) do
 | 
			
		||||
    socket =
 | 
			
		||||
      socket
 | 
			
		||||
      |> assign(assigns)
 | 
			
		||||
      |> assign_new(:ammo_type, fn -> [] end)
 | 
			
		||||
      |> assign_new(:type, fn -> [] end)
 | 
			
		||||
      |> assign_new(:range, fn -> [] end)
 | 
			
		||||
      |> assign_new(:container, fn -> [] end)
 | 
			
		||||
      |> assign_new(:actions, fn -> [] end)
 | 
			
		||||
      |> display_ammo_groups()
 | 
			
		||||
      |> display_packs()
 | 
			
		||||
 | 
			
		||||
    {:ok, socket}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp display_ammo_groups(
 | 
			
		||||
  defp display_packs(
 | 
			
		||||
         %{
 | 
			
		||||
           assigns: %{
 | 
			
		||||
             ammo_groups: ammo_groups,
 | 
			
		||||
             packs: packs,
 | 
			
		||||
             current_user: current_user,
 | 
			
		||||
             ammo_type: ammo_type,
 | 
			
		||||
             type: type,
 | 
			
		||||
             range: range,
 | 
			
		||||
             container: container,
 | 
			
		||||
             actions: actions,
 | 
			
		||||
@@ -54,6 +53,9 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do
 | 
			
		||||
           }
 | 
			
		||||
         } = socket
 | 
			
		||||
       ) do
 | 
			
		||||
    lot_number_used = packs |> Enum.any?(fn %{lot_number: lot_number} -> !!lot_number end)
 | 
			
		||||
    price_paid_used = packs |> Enum.any?(fn %{price_paid: price_paid} -> !!price_paid end)
 | 
			
		||||
 | 
			
		||||
    columns =
 | 
			
		||||
      []
 | 
			
		||||
      |> TableComponent.maybe_compose_columns(
 | 
			
		||||
@@ -78,8 +80,18 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do
 | 
			
		||||
        %{label: gettext("Range"), key: :range},
 | 
			
		||||
        range != []
 | 
			
		||||
      )
 | 
			
		||||
      |> TableComponent.maybe_compose_columns(%{label: gettext("CPR"), key: :cpr})
 | 
			
		||||
      |> TableComponent.maybe_compose_columns(%{label: gettext("Price paid"), key: :price_paid})
 | 
			
		||||
      |> TableComponent.maybe_compose_columns(
 | 
			
		||||
        %{label: gettext("Lot number"), key: :lot_number},
 | 
			
		||||
        lot_number_used
 | 
			
		||||
      )
 | 
			
		||||
      |> TableComponent.maybe_compose_columns(
 | 
			
		||||
        %{label: gettext("CPR"), key: :cpr},
 | 
			
		||||
        price_paid_used
 | 
			
		||||
      )
 | 
			
		||||
      |> TableComponent.maybe_compose_columns(
 | 
			
		||||
        %{label: gettext("Price paid"), key: :price_paid},
 | 
			
		||||
        price_paid_used
 | 
			
		||||
      )
 | 
			
		||||
      |> TableComponent.maybe_compose_columns(
 | 
			
		||||
        %{label: gettext("% left"), key: :remaining},
 | 
			
		||||
        show_used
 | 
			
		||||
@@ -93,33 +105,33 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do
 | 
			
		||||
        key: :count
 | 
			
		||||
      })
 | 
			
		||||
      |> TableComponent.maybe_compose_columns(
 | 
			
		||||
        %{label: gettext("Ammo type"), key: :ammo_type},
 | 
			
		||||
        ammo_type != []
 | 
			
		||||
        %{label: gettext("Type"), key: :type},
 | 
			
		||||
        type != []
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
    containers =
 | 
			
		||||
      ammo_groups
 | 
			
		||||
      packs
 | 
			
		||||
      |> Enum.map(fn %{container_id: container_id} -> container_id end)
 | 
			
		||||
      |> Containers.get_containers(current_user)
 | 
			
		||||
 | 
			
		||||
    extra_data = %{
 | 
			
		||||
      current_user: current_user,
 | 
			
		||||
      ammo_type: ammo_type,
 | 
			
		||||
      type: type,
 | 
			
		||||
      columns: columns,
 | 
			
		||||
      container: container,
 | 
			
		||||
      containers: containers,
 | 
			
		||||
      original_counts: Ammo.get_original_counts(ammo_groups, current_user),
 | 
			
		||||
      cprs: Ammo.get_cprs(ammo_groups, current_user),
 | 
			
		||||
      last_used_dates: ActivityLog.get_last_used_dates(ammo_groups, current_user),
 | 
			
		||||
      percentages_remaining: Ammo.get_percentages_remaining(ammo_groups, current_user),
 | 
			
		||||
      original_counts: Ammo.get_original_counts(packs, current_user),
 | 
			
		||||
      cprs: Ammo.get_cprs(packs, current_user),
 | 
			
		||||
      last_used_dates: ActivityLog.get_last_used_dates(packs, current_user),
 | 
			
		||||
      percentages_remaining: Ammo.get_percentages_remaining(packs, current_user),
 | 
			
		||||
      actions: actions,
 | 
			
		||||
      range: range
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rows =
 | 
			
		||||
      ammo_groups
 | 
			
		||||
      |> Enum.map(fn ammo_group ->
 | 
			
		||||
        ammo_group |> get_row_data_for_ammo_group(extra_data)
 | 
			
		||||
      packs
 | 
			
		||||
      |> Enum.map(fn pack ->
 | 
			
		||||
        pack |> get_row_data_for_pack(extra_data)
 | 
			
		||||
      end)
 | 
			
		||||
 | 
			
		||||
    socket |> assign(columns: columns, rows: rows)
 | 
			
		||||
@@ -134,26 +146,26 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do
 | 
			
		||||
    """
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @spec get_row_data_for_ammo_group(AmmoGroup.t(), additional_data :: map()) :: map()
 | 
			
		||||
  defp get_row_data_for_ammo_group(ammo_group, %{columns: columns} = additional_data) do
 | 
			
		||||
  @spec get_row_data_for_pack(Pack.t(), additional_data :: map()) :: map()
 | 
			
		||||
  defp get_row_data_for_pack(pack, %{columns: columns} = additional_data) do
 | 
			
		||||
    columns
 | 
			
		||||
    |> Map.new(fn %{key: key} ->
 | 
			
		||||
      {key, get_value_for_key(key, ammo_group, additional_data)}
 | 
			
		||||
      {key, get_value_for_key(key, pack, additional_data)}
 | 
			
		||||
    end)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @spec get_value_for_key(atom(), AmmoGroup.t(), additional_data :: map()) ::
 | 
			
		||||
  @spec get_value_for_key(atom(), Pack.t(), additional_data :: map()) ::
 | 
			
		||||
          any() | {any(), Rendered.t()}
 | 
			
		||||
  defp get_value_for_key(
 | 
			
		||||
         :ammo_type,
 | 
			
		||||
         %{ammo_type: %{name: ammo_type_name} = ammo_type},
 | 
			
		||||
         %{ammo_type: ammo_type_block}
 | 
			
		||||
         :type,
 | 
			
		||||
         %{type: %{name: type_name} = type},
 | 
			
		||||
         %{type: type_block}
 | 
			
		||||
       ) do
 | 
			
		||||
    assigns = %{ammo_type: ammo_type, ammo_type_block: ammo_type_block}
 | 
			
		||||
    assigns = %{type: type, type_block: type_block}
 | 
			
		||||
 | 
			
		||||
    {ammo_type_name,
 | 
			
		||||
    {type_name,
 | 
			
		||||
     ~H"""
 | 
			
		||||
     <%= render_slot(@ammo_type_block, @ammo_type) %>
 | 
			
		||||
     <%= render_slot(@type_block, @type) %>
 | 
			
		||||
     """}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -170,9 +182,9 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do
 | 
			
		||||
     """}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_value_for_key(:used_up_on, %{id: ammo_group_id}, %{last_used_dates: last_used_dates}) do
 | 
			
		||||
    last_used_date = last_used_dates |> Map.get(ammo_group_id)
 | 
			
		||||
    assigns = %{id: ammo_group_id, last_used_date: last_used_date}
 | 
			
		||||
  defp get_value_for_key(:used_up_on, %{id: pack_id}, %{last_used_dates: last_used_dates}) do
 | 
			
		||||
    last_used_date = last_used_dates |> Map.get(pack_id)
 | 
			
		||||
    assigns = %{id: pack_id, last_used_date: last_used_date}
 | 
			
		||||
 | 
			
		||||
    {last_used_date,
 | 
			
		||||
     ~H"""
 | 
			
		||||
@@ -184,29 +196,29 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do
 | 
			
		||||
     """}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_value_for_key(:range, %{staged: staged} = ammo_group, %{range: range}) do
 | 
			
		||||
    assigns = %{range: range, ammo_group: ammo_group}
 | 
			
		||||
  defp get_value_for_key(:range, %{staged: staged} = pack, %{range: range}) do
 | 
			
		||||
    assigns = %{range: range, pack: pack}
 | 
			
		||||
 | 
			
		||||
    {staged,
 | 
			
		||||
     ~H"""
 | 
			
		||||
     <%= render_slot(@range, @ammo_group) %>
 | 
			
		||||
     <%= render_slot(@range, @pack) %>
 | 
			
		||||
     """}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_value_for_key(
 | 
			
		||||
         :remaining,
 | 
			
		||||
         %{id: ammo_group_id},
 | 
			
		||||
         %{id: pack_id},
 | 
			
		||||
         %{percentages_remaining: percentages_remaining}
 | 
			
		||||
       ) do
 | 
			
		||||
    percentage = Map.fetch!(percentages_remaining, ammo_group_id)
 | 
			
		||||
    percentage = Map.fetch!(percentages_remaining, pack_id)
 | 
			
		||||
    {percentage, gettext("%{percentage}%", percentage: percentage)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_value_for_key(:actions, ammo_group, %{actions: actions}) do
 | 
			
		||||
    assigns = %{actions: actions, ammo_group: ammo_group}
 | 
			
		||||
  defp get_value_for_key(:actions, pack, %{actions: actions}) do
 | 
			
		||||
    assigns = %{actions: actions, pack: pack}
 | 
			
		||||
 | 
			
		||||
    ~H"""
 | 
			
		||||
    <%= render_slot(@actions, @ammo_group) %>
 | 
			
		||||
    <%= render_slot(@actions, @pack) %>
 | 
			
		||||
    """
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -214,7 +226,7 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do
 | 
			
		||||
 | 
			
		||||
  defp get_value_for_key(
 | 
			
		||||
         :container,
 | 
			
		||||
         %{container_id: container_id} = ammo_group,
 | 
			
		||||
         %{container_id: container_id} = pack,
 | 
			
		||||
         %{container: container_block, containers: containers}
 | 
			
		||||
       ) do
 | 
			
		||||
    container = %{name: container_name} = Map.fetch!(containers, container_id)
 | 
			
		||||
@@ -222,35 +234,35 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do
 | 
			
		||||
    assigns = %{
 | 
			
		||||
      container: container,
 | 
			
		||||
      container_block: container_block,
 | 
			
		||||
      ammo_group: ammo_group
 | 
			
		||||
      pack: pack
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    {container_name,
 | 
			
		||||
     ~H"""
 | 
			
		||||
     <%= render_slot(@container_block, {@ammo_group, @container}) %>
 | 
			
		||||
     <%= render_slot(@container_block, {@pack, @container}) %>
 | 
			
		||||
     """}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_value_for_key(
 | 
			
		||||
         :original_count,
 | 
			
		||||
         %{id: ammo_group_id},
 | 
			
		||||
         %{id: pack_id},
 | 
			
		||||
         %{original_counts: original_counts}
 | 
			
		||||
       ) do
 | 
			
		||||
    Map.fetch!(original_counts, ammo_group_id)
 | 
			
		||||
    Map.fetch!(original_counts, pack_id)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_value_for_key(:cpr, %{price_paid: nil}, _additional_data),
 | 
			
		||||
    do: {0, gettext("No cost information")}
 | 
			
		||||
 | 
			
		||||
  defp get_value_for_key(:cpr, %{id: ammo_group_id}, %{cprs: cprs}) do
 | 
			
		||||
    amount = Map.fetch!(cprs, ammo_group_id)
 | 
			
		||||
  defp get_value_for_key(:cpr, %{id: pack_id}, %{cprs: cprs}) do
 | 
			
		||||
    amount = Map.fetch!(cprs, pack_id)
 | 
			
		||||
    {amount, gettext("$%{amount}", amount: display_currency(amount))}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_value_for_key(:count, %{count: count}, _additional_data),
 | 
			
		||||
    do: if(count == 0, do: {0, gettext("Empty")}, else: count)
 | 
			
		||||
 | 
			
		||||
  defp get_value_for_key(key, ammo_group, _additional_data), do: ammo_group |> Map.get(key)
 | 
			
		||||
  defp get_value_for_key(key, pack, _additional_data), do: pack |> Map.get(key)
 | 
			
		||||
 | 
			
		||||
  @spec display_currency(float()) :: String.t()
 | 
			
		||||
  defp display_currency(float), do: :erlang.float_to_binary(float, decimals: 2)
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
defmodule CanneryWeb.Components.ShotGroupTableComponent do
 | 
			
		||||
defmodule CanneryWeb.Components.ShotRecordTableComponent do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  A component that displays a list of shot groups
 | 
			
		||||
  A component that displays a list of shot records
 | 
			
		||||
  """
 | 
			
		||||
  use CanneryWeb, :live_component
 | 
			
		||||
  alias Cannery.{Accounts.User, ActivityLog.ShotGroup, Ammo, ComparableDate}
 | 
			
		||||
  alias Cannery.{Accounts.User, ActivityLog.ShotRecord, Ammo, ComparableDate}
 | 
			
		||||
  alias Ecto.UUID
 | 
			
		||||
  alias Phoenix.LiveView.{Rendered, Socket}
 | 
			
		||||
 | 
			
		||||
@@ -12,26 +12,29 @@ defmodule CanneryWeb.Components.ShotGroupTableComponent do
 | 
			
		||||
          %{
 | 
			
		||||
            required(:id) => UUID.t(),
 | 
			
		||||
            required(:current_user) => User.t(),
 | 
			
		||||
            optional(:shot_groups) => [ShotGroup.t()],
 | 
			
		||||
            optional(:shot_records) => [ShotRecord.t()],
 | 
			
		||||
            optional(:actions) => Rendered.t(),
 | 
			
		||||
            optional(any()) => any()
 | 
			
		||||
          },
 | 
			
		||||
          Socket.t()
 | 
			
		||||
        ) :: {:ok, Socket.t()}
 | 
			
		||||
  def update(%{id: _id, shot_groups: _shot_groups, current_user: _current_user} = assigns, socket) do
 | 
			
		||||
  def update(
 | 
			
		||||
        %{id: _id, shot_records: _shot_records, current_user: _current_user} = assigns,
 | 
			
		||||
        socket
 | 
			
		||||
      ) do
 | 
			
		||||
    socket =
 | 
			
		||||
      socket
 | 
			
		||||
      |> assign(assigns)
 | 
			
		||||
      |> assign_new(:actions, fn -> [] end)
 | 
			
		||||
      |> display_shot_groups()
 | 
			
		||||
      |> display_shot_records()
 | 
			
		||||
 | 
			
		||||
    {:ok, socket}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp display_shot_groups(
 | 
			
		||||
  defp display_shot_records(
 | 
			
		||||
         %{
 | 
			
		||||
           assigns: %{
 | 
			
		||||
             shot_groups: shot_groups,
 | 
			
		||||
             shot_records: shot_records,
 | 
			
		||||
             current_user: current_user,
 | 
			
		||||
             actions: actions
 | 
			
		||||
           }
 | 
			
		||||
@@ -45,17 +48,17 @@ defmodule CanneryWeb.Components.ShotGroupTableComponent do
 | 
			
		||||
      %{label: gettext("Actions"), key: :actions, sortable: false}
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    ammo_groups =
 | 
			
		||||
      shot_groups
 | 
			
		||||
      |> Enum.map(fn %{ammo_group_id: ammo_group_id} -> ammo_group_id end)
 | 
			
		||||
      |> Ammo.get_ammo_groups(current_user)
 | 
			
		||||
    packs =
 | 
			
		||||
      shot_records
 | 
			
		||||
      |> Enum.map(fn %{pack_id: pack_id} -> pack_id end)
 | 
			
		||||
      |> Ammo.get_packs(current_user)
 | 
			
		||||
 | 
			
		||||
    extra_data = %{current_user: current_user, actions: actions, ammo_groups: ammo_groups}
 | 
			
		||||
    extra_data = %{current_user: current_user, actions: actions, packs: packs}
 | 
			
		||||
 | 
			
		||||
    rows =
 | 
			
		||||
      shot_groups
 | 
			
		||||
      |> Enum.map(fn shot_group ->
 | 
			
		||||
        shot_group |> get_row_data_for_shot_group(columns, extra_data)
 | 
			
		||||
      shot_records
 | 
			
		||||
      |> Enum.map(fn shot_record ->
 | 
			
		||||
        shot_record |> get_row_data_for_shot_record(columns, extra_data)
 | 
			
		||||
      end)
 | 
			
		||||
 | 
			
		||||
    socket
 | 
			
		||||
@@ -81,22 +84,22 @@ defmodule CanneryWeb.Components.ShotGroupTableComponent do
 | 
			
		||||
    """
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @spec get_row_data_for_shot_group(ShotGroup.t(), columns :: [map()], extra_data :: map()) ::
 | 
			
		||||
  @spec get_row_data_for_shot_record(ShotRecord.t(), columns :: [map()], extra_data :: map()) ::
 | 
			
		||||
          map()
 | 
			
		||||
  defp get_row_data_for_shot_group(shot_group, columns, extra_data) do
 | 
			
		||||
  defp get_row_data_for_shot_record(shot_record, columns, extra_data) do
 | 
			
		||||
    columns
 | 
			
		||||
    |> Map.new(fn %{key: key} ->
 | 
			
		||||
      {key, get_row_value(key, shot_group, extra_data)}
 | 
			
		||||
      {key, get_row_value(key, shot_record, extra_data)}
 | 
			
		||||
    end)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_row_value(:name, %{ammo_group_id: ammo_group_id}, %{ammo_groups: ammo_groups}) do
 | 
			
		||||
    assigns = %{ammo_group: ammo_group = Map.fetch!(ammo_groups, ammo_group_id)}
 | 
			
		||||
  defp get_row_value(:name, %{pack_id: pack_id}, %{packs: packs}) do
 | 
			
		||||
    assigns = %{pack: pack = Map.fetch!(packs, pack_id)}
 | 
			
		||||
 | 
			
		||||
    {ammo_group.ammo_type.name,
 | 
			
		||||
    {pack.type.name,
 | 
			
		||||
     ~H"""
 | 
			
		||||
     <.link navigate={Routes.ammo_group_show_path(Endpoint, :show, @ammo_group)} class="link">
 | 
			
		||||
       <%= @ammo_group.ammo_type.name %>
 | 
			
		||||
     <.link navigate={Routes.pack_show_path(Endpoint, :show, @pack)} class="link">
 | 
			
		||||
       <%= @pack.type.name %>
 | 
			
		||||
     </.link>
 | 
			
		||||
     """}
 | 
			
		||||
  end
 | 
			
		||||
@@ -108,13 +111,13 @@ defmodule CanneryWeb.Components.ShotGroupTableComponent do
 | 
			
		||||
     """}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_row_value(:actions, shot_group, %{actions: actions}) do
 | 
			
		||||
    assigns = %{actions: actions, shot_group: shot_group}
 | 
			
		||||
  defp get_row_value(:actions, shot_record, %{actions: actions}) do
 | 
			
		||||
    assigns = %{actions: actions, shot_record: shot_record}
 | 
			
		||||
 | 
			
		||||
    ~H"""
 | 
			
		||||
    <%= render_slot(@actions, @shot_group) %>
 | 
			
		||||
    <%= render_slot(@actions, @shot_record) %>
 | 
			
		||||
    """
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_row_value(key, shot_group, _extra_data), do: shot_group |> Map.get(key)
 | 
			
		||||
  defp get_row_value(key, shot_record, _extra_data), do: shot_record |> Map.get(key)
 | 
			
		||||
end
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
defmodule CanneryWeb.Components.AmmoTypeTableComponent do
 | 
			
		||||
defmodule CanneryWeb.Components.TypeTableComponent do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  A component that displays a list of ammo type
 | 
			
		||||
  A component that displays a list of types
 | 
			
		||||
  """
 | 
			
		||||
  use CanneryWeb, :live_component
 | 
			
		||||
  alias Cannery.{Accounts.User, ActivityLog, Ammo, Ammo.AmmoType}
 | 
			
		||||
  alias Cannery.{Accounts.User, ActivityLog, Ammo, Ammo.Type}
 | 
			
		||||
  alias CanneryWeb.Components.TableComponent
 | 
			
		||||
  alias Ecto.UUID
 | 
			
		||||
  alias Phoenix.LiveView.{Rendered, Socket}
 | 
			
		||||
@@ -13,33 +13,33 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do
 | 
			
		||||
          %{
 | 
			
		||||
            required(:id) => UUID.t(),
 | 
			
		||||
            required(:current_user) => User.t(),
 | 
			
		||||
            optional(:type) => AmmoType.type() | nil,
 | 
			
		||||
            optional(:class) => Type.class() | nil,
 | 
			
		||||
            optional(:show_used) => boolean(),
 | 
			
		||||
            optional(:ammo_types) => [AmmoType.t()],
 | 
			
		||||
            optional(:types) => [Type.t()],
 | 
			
		||||
            optional(:actions) => Rendered.t(),
 | 
			
		||||
            optional(any()) => any()
 | 
			
		||||
          },
 | 
			
		||||
          Socket.t()
 | 
			
		||||
        ) :: {:ok, Socket.t()}
 | 
			
		||||
  def update(%{id: _id, ammo_types: _ammo_types, current_user: _current_user} = assigns, socket) do
 | 
			
		||||
  def update(%{id: _id, types: _types, current_user: _current_user} = assigns, socket) do
 | 
			
		||||
    socket =
 | 
			
		||||
      socket
 | 
			
		||||
      |> assign(assigns)
 | 
			
		||||
      |> assign_new(:show_used, fn -> false end)
 | 
			
		||||
      |> assign_new(:type, fn -> :all end)
 | 
			
		||||
      |> assign_new(:class, fn -> :all end)
 | 
			
		||||
      |> assign_new(:actions, fn -> [] end)
 | 
			
		||||
      |> display_ammo_types()
 | 
			
		||||
      |> display_types()
 | 
			
		||||
 | 
			
		||||
    {:ok, socket}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp display_ammo_types(
 | 
			
		||||
  defp display_types(
 | 
			
		||||
         %{
 | 
			
		||||
           assigns: %{
 | 
			
		||||
             ammo_types: ammo_types,
 | 
			
		||||
             types: types,
 | 
			
		||||
             current_user: current_user,
 | 
			
		||||
             show_used: show_used,
 | 
			
		||||
             type: type,
 | 
			
		||||
             class: class,
 | 
			
		||||
             actions: actions
 | 
			
		||||
           }
 | 
			
		||||
         } = socket
 | 
			
		||||
@@ -48,7 +48,7 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do
 | 
			
		||||
      [
 | 
			
		||||
        %{label: gettext("Cartridge"), key: :cartridge, type: :string},
 | 
			
		||||
        %{
 | 
			
		||||
          label: if(type == :shotgun, do: gettext("Gauge"), else: gettext("Caliber")),
 | 
			
		||||
          label: if(class == :shotgun, do: gettext("Gauge"), else: gettext("Caliber")),
 | 
			
		||||
          key: :caliber,
 | 
			
		||||
          type: :string
 | 
			
		||||
        },
 | 
			
		||||
@@ -59,7 +59,7 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do
 | 
			
		||||
        %{label: gettext("Grains"), key: :grains, type: :string},
 | 
			
		||||
        %{label: gettext("Bullet type"), key: :bullet_type, type: :string},
 | 
			
		||||
        %{
 | 
			
		||||
          label: if(type == :shotgun, do: gettext("Slug core"), else: gettext("Bullet core")),
 | 
			
		||||
          label: if(class == :shotgun, do: gettext("Slug core"), else: gettext("Bullet core")),
 | 
			
		||||
          key: :bullet_core,
 | 
			
		||||
          type: :string
 | 
			
		||||
        },
 | 
			
		||||
@@ -92,8 +92,8 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do
 | 
			
		||||
        # remove columns if all values match defaults
 | 
			
		||||
        default_value = if type == :atom, do: false, else: nil
 | 
			
		||||
 | 
			
		||||
        ammo_types
 | 
			
		||||
        |> Enum.any?(fn ammo_type -> Map.get(ammo_type, key, default_value) != default_value end)
 | 
			
		||||
        types
 | 
			
		||||
        |> Enum.any?(fn type -> Map.get(type, key, default_value) != default_value end)
 | 
			
		||||
      end)
 | 
			
		||||
 | 
			
		||||
    columns =
 | 
			
		||||
@@ -147,22 +147,22 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do
 | 
			
		||||
      })
 | 
			
		||||
      |> TableComponent.maybe_compose_columns(filtered_columns)
 | 
			
		||||
      |> TableComponent.maybe_compose_columns(
 | 
			
		||||
        %{label: gettext("Type"), key: :type, type: :atom},
 | 
			
		||||
        type in [:all, nil]
 | 
			
		||||
        %{label: gettext("Class"), key: :class, type: :atom},
 | 
			
		||||
        class in [:all, nil]
 | 
			
		||||
      )
 | 
			
		||||
      |> TableComponent.maybe_compose_columns(%{label: gettext("Name"), key: :name, type: :name})
 | 
			
		||||
 | 
			
		||||
    round_counts = ammo_types |> Ammo.get_round_count_for_ammo_types(current_user)
 | 
			
		||||
    packs_count = ammo_types |> Ammo.get_ammo_groups_count_for_types(current_user)
 | 
			
		||||
    average_costs = ammo_types |> Ammo.get_average_cost_for_ammo_types(current_user)
 | 
			
		||||
    round_counts = types |> Ammo.get_round_count_for_types(current_user)
 | 
			
		||||
    packs_count = types |> Ammo.get_packs_count_for_types(current_user)
 | 
			
		||||
    average_costs = types |> Ammo.get_average_cost_for_types(current_user)
 | 
			
		||||
 | 
			
		||||
    [used_counts, historical_round_counts, historical_pack_counts, used_pack_counts] =
 | 
			
		||||
      if show_used do
 | 
			
		||||
        [
 | 
			
		||||
          ammo_types |> ActivityLog.get_used_count_for_ammo_types(current_user),
 | 
			
		||||
          ammo_types |> Ammo.get_historical_count_for_ammo_types(current_user),
 | 
			
		||||
          ammo_types |> Ammo.get_ammo_groups_count_for_types(current_user, true),
 | 
			
		||||
          ammo_types |> Ammo.get_used_ammo_groups_count_for_types(current_user)
 | 
			
		||||
          types |> ActivityLog.get_used_count_for_types(current_user),
 | 
			
		||||
          types |> Ammo.get_historical_count_for_types(current_user),
 | 
			
		||||
          types |> Ammo.get_packs_count_for_types(current_user, true),
 | 
			
		||||
          types |> Ammo.get_used_packs_count_for_types(current_user)
 | 
			
		||||
        ]
 | 
			
		||||
      else
 | 
			
		||||
        [nil, nil, nil, nil]
 | 
			
		||||
@@ -181,9 +181,9 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rows =
 | 
			
		||||
      ammo_types
 | 
			
		||||
      |> Enum.map(fn ammo_type ->
 | 
			
		||||
        ammo_type |> get_ammo_type_values(columns, extra_data)
 | 
			
		||||
      types
 | 
			
		||||
      |> Enum.map(fn type ->
 | 
			
		||||
        type |> get_type_values(columns, extra_data)
 | 
			
		||||
      end)
 | 
			
		||||
 | 
			
		||||
    socket |> assign(columns: columns, rows: rows)
 | 
			
		||||
@@ -198,92 +198,92 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do
 | 
			
		||||
    """
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_ammo_type_values(ammo_type, columns, extra_data) do
 | 
			
		||||
  defp get_type_values(type, columns, extra_data) do
 | 
			
		||||
    columns
 | 
			
		||||
    |> Map.new(fn %{key: key, type: type} ->
 | 
			
		||||
      {key, get_ammo_type_value(type, key, ammo_type, extra_data)}
 | 
			
		||||
    |> Map.new(fn %{key: key, type: column_type} ->
 | 
			
		||||
      {key, get_type_value(column_type, key, type, extra_data)}
 | 
			
		||||
    end)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_ammo_type_value(:atom, key, ammo_type, _other_data),
 | 
			
		||||
    do: ammo_type |> Map.get(key) |> humanize()
 | 
			
		||||
  defp get_type_value(:atom, key, type, _other_data),
 | 
			
		||||
    do: type |> Map.get(key) |> humanize()
 | 
			
		||||
 | 
			
		||||
  defp get_ammo_type_value(:round_count, _key, %{id: ammo_type_id}, %{round_counts: round_counts}),
 | 
			
		||||
    do: Map.get(round_counts, ammo_type_id, 0)
 | 
			
		||||
  defp get_type_value(:round_count, _key, %{id: type_id}, %{round_counts: round_counts}),
 | 
			
		||||
    do: Map.get(round_counts, type_id, 0)
 | 
			
		||||
 | 
			
		||||
  defp get_ammo_type_value(
 | 
			
		||||
  defp get_type_value(
 | 
			
		||||
         :historical_round_count,
 | 
			
		||||
         _key,
 | 
			
		||||
         %{id: ammo_type_id},
 | 
			
		||||
         %{id: type_id},
 | 
			
		||||
         %{historical_round_counts: historical_round_counts}
 | 
			
		||||
       ) do
 | 
			
		||||
    Map.get(historical_round_counts, ammo_type_id, 0)
 | 
			
		||||
    Map.get(historical_round_counts, type_id, 0)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_ammo_type_value(
 | 
			
		||||
  defp get_type_value(
 | 
			
		||||
         :used_round_count,
 | 
			
		||||
         _key,
 | 
			
		||||
         %{id: ammo_type_id},
 | 
			
		||||
         %{id: type_id},
 | 
			
		||||
         %{used_counts: used_counts}
 | 
			
		||||
       ) do
 | 
			
		||||
    Map.get(used_counts, ammo_type_id, 0)
 | 
			
		||||
    Map.get(used_counts, type_id, 0)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_ammo_type_value(
 | 
			
		||||
  defp get_type_value(
 | 
			
		||||
         :historical_pack_count,
 | 
			
		||||
         _key,
 | 
			
		||||
         %{id: ammo_type_id},
 | 
			
		||||
         %{id: type_id},
 | 
			
		||||
         %{historical_pack_counts: historical_pack_counts}
 | 
			
		||||
       ) do
 | 
			
		||||
    Map.get(historical_pack_counts, ammo_type_id, 0)
 | 
			
		||||
    Map.get(historical_pack_counts, type_id, 0)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_ammo_type_value(
 | 
			
		||||
  defp get_type_value(
 | 
			
		||||
         :used_pack_count,
 | 
			
		||||
         _key,
 | 
			
		||||
         %{id: ammo_type_id},
 | 
			
		||||
         %{id: type_id},
 | 
			
		||||
         %{used_pack_counts: used_pack_counts}
 | 
			
		||||
       ) do
 | 
			
		||||
    Map.get(used_pack_counts, ammo_type_id, 0)
 | 
			
		||||
    Map.get(used_pack_counts, type_id, 0)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_ammo_type_value(:ammo_count, _key, %{id: ammo_type_id}, %{packs_count: packs_count}),
 | 
			
		||||
    do: Map.get(packs_count, ammo_type_id)
 | 
			
		||||
  defp get_type_value(:ammo_count, _key, %{id: type_id}, %{packs_count: packs_count}),
 | 
			
		||||
    do: Map.get(packs_count, type_id)
 | 
			
		||||
 | 
			
		||||
  defp get_ammo_type_value(
 | 
			
		||||
  defp get_type_value(
 | 
			
		||||
         :avg_price_paid,
 | 
			
		||||
         _key,
 | 
			
		||||
         %{id: ammo_type_id},
 | 
			
		||||
         %{id: type_id},
 | 
			
		||||
         %{average_costs: average_costs}
 | 
			
		||||
       ) do
 | 
			
		||||
    case Map.get(average_costs, ammo_type_id) do
 | 
			
		||||
    case Map.get(average_costs, type_id) do
 | 
			
		||||
      nil -> {0, gettext("No cost information")}
 | 
			
		||||
      count -> {count, gettext("$%{amount}", amount: display_currency(count))}
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_ammo_type_value(:name, _key, %{name: ammo_type_name} = ammo_type, _other_data) do
 | 
			
		||||
    assigns = %{ammo_type: ammo_type}
 | 
			
		||||
  defp get_type_value(:name, _key, %{name: type_name} = type, _other_data) do
 | 
			
		||||
    assigns = %{type: type}
 | 
			
		||||
 | 
			
		||||
    {ammo_type_name,
 | 
			
		||||
    {type_name,
 | 
			
		||||
     ~H"""
 | 
			
		||||
     <.link navigate={Routes.ammo_type_show_path(Endpoint, :show, @ammo_type)} class="link">
 | 
			
		||||
       <%= @ammo_type.name %>
 | 
			
		||||
     <.link navigate={Routes.type_show_path(Endpoint, :show, @type)} class="link">
 | 
			
		||||
       <%= @type.name %>
 | 
			
		||||
     </.link>
 | 
			
		||||
     """}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_ammo_type_value(:actions, _key, ammo_type, %{actions: actions}) do
 | 
			
		||||
    assigns = %{actions: actions, ammo_type: ammo_type}
 | 
			
		||||
  defp get_type_value(:actions, _key, type, %{actions: actions}) do
 | 
			
		||||
    assigns = %{actions: actions, type: type}
 | 
			
		||||
 | 
			
		||||
    ~H"""
 | 
			
		||||
    <%= render_slot(@actions, @ammo_type) %>
 | 
			
		||||
    <%= render_slot(@actions, @type) %>
 | 
			
		||||
    """
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp get_ammo_type_value(nil, _key, _ammo_type, _other_data), do: nil
 | 
			
		||||
  defp get_type_value(nil, _key, _type, _other_data), do: nil
 | 
			
		||||
 | 
			
		||||
  defp get_ammo_type_value(_other, key, ammo_type, _other_data), do: ammo_type |> Map.get(key)
 | 
			
		||||
  defp get_type_value(_other, key, type, _other_data), do: type |> Map.get(key)
 | 
			
		||||
 | 
			
		||||
  @spec display_currency(float()) :: String.t()
 | 
			
		||||
  defp display_currency(float), do: :erlang.float_to_binary(float, decimals: 2)
 | 
			
		||||
@@ -3,73 +3,72 @@ defmodule CanneryWeb.ExportController do
 | 
			
		||||
  alias Cannery.{ActivityLog, Ammo, Containers}
 | 
			
		||||
 | 
			
		||||
  def export(%{assigns: %{current_user: current_user}} = conn, %{"mode" => "json"}) do
 | 
			
		||||
    ammo_types = Ammo.list_ammo_types(current_user, :all)
 | 
			
		||||
    used_counts = ammo_types |> ActivityLog.get_used_count_for_ammo_types(current_user)
 | 
			
		||||
    round_counts = ammo_types |> Ammo.get_round_count_for_ammo_types(current_user)
 | 
			
		||||
    ammo_group_counts = ammo_types |> Ammo.get_ammo_groups_count_for_types(current_user)
 | 
			
		||||
    types = Ammo.list_types(current_user, :all)
 | 
			
		||||
    used_counts = types |> ActivityLog.get_used_count_for_types(current_user)
 | 
			
		||||
    round_counts = types |> Ammo.get_round_count_for_types(current_user)
 | 
			
		||||
    pack_counts = types |> Ammo.get_packs_count_for_types(current_user)
 | 
			
		||||
 | 
			
		||||
    total_ammo_group_counts =
 | 
			
		||||
      ammo_types |> Ammo.get_ammo_groups_count_for_types(current_user, true)
 | 
			
		||||
    total_pack_counts = types |> Ammo.get_packs_count_for_types(current_user, true)
 | 
			
		||||
 | 
			
		||||
    average_costs = ammo_types |> Ammo.get_average_cost_for_ammo_types(current_user)
 | 
			
		||||
    average_costs = types |> Ammo.get_average_cost_for_types(current_user)
 | 
			
		||||
 | 
			
		||||
    ammo_types =
 | 
			
		||||
      ammo_types
 | 
			
		||||
      |> Enum.map(fn %{id: ammo_type_id} = ammo_type ->
 | 
			
		||||
        ammo_type
 | 
			
		||||
    types =
 | 
			
		||||
      types
 | 
			
		||||
      |> Enum.map(fn %{id: type_id} = type ->
 | 
			
		||||
        type
 | 
			
		||||
        |> Jason.encode!()
 | 
			
		||||
        |> Jason.decode!()
 | 
			
		||||
        |> Map.merge(%{
 | 
			
		||||
          "average_cost" => Map.get(average_costs, ammo_type_id),
 | 
			
		||||
          "round_count" => Map.get(round_counts, ammo_type_id, 0),
 | 
			
		||||
          "used_count" => Map.get(used_counts, ammo_type_id, 0),
 | 
			
		||||
          "ammo_group_count" => Map.get(ammo_group_counts, ammo_type_id, 0),
 | 
			
		||||
          "total_ammo_group_count" => Map.get(total_ammo_group_counts, ammo_type_id, 0)
 | 
			
		||||
          "average_cost" => Map.get(average_costs, type_id),
 | 
			
		||||
          "round_count" => Map.get(round_counts, type_id, 0),
 | 
			
		||||
          "used_count" => Map.get(used_counts, type_id, 0),
 | 
			
		||||
          "pack_count" => Map.get(pack_counts, type_id, 0),
 | 
			
		||||
          "total_pack_count" => Map.get(total_pack_counts, type_id, 0)
 | 
			
		||||
        })
 | 
			
		||||
      end)
 | 
			
		||||
 | 
			
		||||
    ammo_groups = Ammo.list_ammo_groups(nil, :all, current_user, true)
 | 
			
		||||
    used_counts = ammo_groups |> ActivityLog.get_used_counts(current_user)
 | 
			
		||||
    original_counts = ammo_groups |> Ammo.get_original_counts(current_user)
 | 
			
		||||
    cprs = ammo_groups |> Ammo.get_cprs(current_user)
 | 
			
		||||
    percentages_remaining = ammo_groups |> Ammo.get_percentages_remaining(current_user)
 | 
			
		||||
    packs = Ammo.list_packs(nil, :all, current_user, true)
 | 
			
		||||
    used_counts = packs |> ActivityLog.get_used_counts(current_user)
 | 
			
		||||
    original_counts = packs |> Ammo.get_original_counts(current_user)
 | 
			
		||||
    cprs = packs |> Ammo.get_cprs(current_user)
 | 
			
		||||
    percentages_remaining = packs |> Ammo.get_percentages_remaining(current_user)
 | 
			
		||||
 | 
			
		||||
    ammo_groups =
 | 
			
		||||
      ammo_groups
 | 
			
		||||
      |> Enum.map(fn %{id: ammo_group_id} = ammo_group ->
 | 
			
		||||
        ammo_group
 | 
			
		||||
    packs =
 | 
			
		||||
      packs
 | 
			
		||||
      |> Enum.map(fn %{id: pack_id} = pack ->
 | 
			
		||||
        pack
 | 
			
		||||
        |> Jason.encode!()
 | 
			
		||||
        |> Jason.decode!()
 | 
			
		||||
        |> Map.merge(%{
 | 
			
		||||
          "used_count" => Map.get(used_counts, ammo_group_id),
 | 
			
		||||
          "percentage_remaining" => Map.fetch!(percentages_remaining, ammo_group_id),
 | 
			
		||||
          "original_count" => Map.get(original_counts, ammo_group_id),
 | 
			
		||||
          "cpr" => Map.get(cprs, ammo_group_id)
 | 
			
		||||
          "used_count" => Map.get(used_counts, pack_id),
 | 
			
		||||
          "percentage_remaining" => Map.fetch!(percentages_remaining, pack_id),
 | 
			
		||||
          "original_count" => Map.get(original_counts, pack_id),
 | 
			
		||||
          "cpr" => Map.get(cprs, pack_id)
 | 
			
		||||
        })
 | 
			
		||||
      end)
 | 
			
		||||
 | 
			
		||||
    shot_groups = ActivityLog.list_shot_groups(:all, current_user)
 | 
			
		||||
    shot_records = ActivityLog.list_shot_records(:all, current_user)
 | 
			
		||||
 | 
			
		||||
    containers =
 | 
			
		||||
      Containers.list_containers(current_user)
 | 
			
		||||
      |> Enum.map(fn container ->
 | 
			
		||||
        ammo_group_count = container |> Ammo.get_ammo_groups_count_for_container!(current_user)
 | 
			
		||||
        pack_count = container |> Ammo.get_packs_count_for_container!(current_user)
 | 
			
		||||
        round_count = container |> Ammo.get_round_count_for_container!(current_user)
 | 
			
		||||
 | 
			
		||||
        container
 | 
			
		||||
        |> Jason.encode!()
 | 
			
		||||
        |> Jason.decode!()
 | 
			
		||||
        |> Map.merge(%{
 | 
			
		||||
          "ammo_group_count" => ammo_group_count,
 | 
			
		||||
          "pack_count" => pack_count,
 | 
			
		||||
          "round_count" => round_count
 | 
			
		||||
        })
 | 
			
		||||
      end)
 | 
			
		||||
 | 
			
		||||
    json(conn, %{
 | 
			
		||||
      user: current_user,
 | 
			
		||||
      ammo_types: ammo_types,
 | 
			
		||||
      ammo_groups: ammo_groups,
 | 
			
		||||
      shot_groups: shot_groups,
 | 
			
		||||
      types: types,
 | 
			
		||||
      packs: packs,
 | 
			
		||||
      shot_records: shot_records,
 | 
			
		||||
      containers: containers
 | 
			
		||||
    })
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
@@ -1,114 +0,0 @@
 | 
			
		||||
defmodule CanneryWeb.AmmoTypeLive.Index do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  Liveview for showing a Cannery.Ammo.AmmoType index
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb, :live_view
 | 
			
		||||
  alias Cannery.{Ammo, Ammo.AmmoType}
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def mount(%{"search" => search}, _session, socket) do
 | 
			
		||||
    {:ok, socket |> assign(type: :all, show_used: false, search: search) |> list_ammo_types()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def mount(_params, _session, socket) do
 | 
			
		||||
    {:ok, socket |> assign(type: :all, show_used: false, search: nil) |> list_ammo_types()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_params(params, _url, %{assigns: %{live_action: live_action}} = socket) do
 | 
			
		||||
    {:noreply, apply_action(socket, live_action, params)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit, %{"id" => id}) do
 | 
			
		||||
    %{name: ammo_type_name} = ammo_type = Ammo.get_ammo_type!(id, current_user)
 | 
			
		||||
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("Edit %{ammo_type_name}", ammo_type_name: ammo_type_name),
 | 
			
		||||
      ammo_type: ammo_type
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp apply_action(%{assigns: %{current_user: current_user}} = socket, :clone, %{"id" => id}) do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("New Ammo type"),
 | 
			
		||||
      ammo_type: %{Ammo.get_ammo_type!(id, current_user) | id: nil}
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp apply_action(socket, :new, _params) do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("New Ammo type"),
 | 
			
		||||
      ammo_type: %AmmoType{}
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp apply_action(socket, :index, _params) do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("Catalog"),
 | 
			
		||||
      search: nil,
 | 
			
		||||
      ammo_type: nil
 | 
			
		||||
    )
 | 
			
		||||
    |> list_ammo_types()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp apply_action(socket, :search, %{"search" => search}) do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("Catalog"),
 | 
			
		||||
      search: search,
 | 
			
		||||
      ammo_type: nil
 | 
			
		||||
    )
 | 
			
		||||
    |> list_ammo_types()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_event("delete", %{"id" => id}, %{assigns: %{current_user: current_user}} = socket) do
 | 
			
		||||
    %{name: name} = Ammo.get_ammo_type!(id, current_user) |> Ammo.delete_ammo_type!(current_user)
 | 
			
		||||
    prompt = dgettext("prompts", "%{name} deleted succesfully", name: name)
 | 
			
		||||
    {:noreply, socket |> put_flash(:info, prompt) |> list_ammo_types()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("toggle_show_used", _params, %{assigns: %{show_used: show_used}} = socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:show_used, !show_used) |> list_ammo_types()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("search", %{"search" => %{"search_term" => ""}}, socket) do
 | 
			
		||||
    {:noreply, socket |> push_patch(to: Routes.ammo_type_index_path(Endpoint, :index))}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("search", %{"search" => %{"search_term" => search_term}}, socket) do
 | 
			
		||||
    search_path = Routes.ammo_type_index_path(Endpoint, :search, search_term)
 | 
			
		||||
    {:noreply, socket |> push_patch(to: search_path)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_type", %{"ammo_type" => %{"type" => "rifle"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:type, :rifle) |> list_ammo_types()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_type", %{"ammo_type" => %{"type" => "shotgun"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:type, :shotgun) |> list_ammo_types()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_type", %{"ammo_type" => %{"type" => "pistol"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:type, :pistol) |> list_ammo_types()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_type", %{"ammo_type" => %{"type" => _all}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:type, :all) |> list_ammo_types()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp list_ammo_types(
 | 
			
		||||
         %{assigns: %{type: type, search: search, current_user: current_user}} = socket
 | 
			
		||||
       ) do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      ammo_types: Ammo.list_ammo_types(search, current_user, type),
 | 
			
		||||
      ammo_types_count: Ammo.get_ammo_types_count!(current_user)
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -79,15 +79,15 @@ defmodule CanneryWeb.ContainerLive.Index do
 | 
			
		||||
              prompt = dgettext("prompts", "%{name} has been deleted", name: container_name)
 | 
			
		||||
              socket |> put_flash(:info, prompt) |> display_containers()
 | 
			
		||||
 | 
			
		||||
            {:error, %{action: :delete, errors: [ammo_groups: _error], valid?: false} = changeset} ->
 | 
			
		||||
              ammo_groups_error = changeset |> changeset_errors(:ammo_groups) |> Enum.join(", ")
 | 
			
		||||
            {:error, %{action: :delete, errors: [packs: _error], valid?: false} = changeset} ->
 | 
			
		||||
              packs_error = changeset |> changeset_errors(:packs) |> Enum.join(", ")
 | 
			
		||||
 | 
			
		||||
              prompt =
 | 
			
		||||
                dgettext(
 | 
			
		||||
                  "errors",
 | 
			
		||||
                  "Could not delete %{name}: %{error}",
 | 
			
		||||
                  name: changeset |> Changeset.get_field(:name, "container"),
 | 
			
		||||
                  error: ammo_groups_error
 | 
			
		||||
                  error: packs_error
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
              socket |> put_flash(:error, prompt)
 | 
			
		||||
 
 | 
			
		||||
@@ -11,13 +11,13 @@ defmodule CanneryWeb.ContainerLive.Show do
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def mount(_params, _session, socket),
 | 
			
		||||
    do: {:ok, socket |> assign(type: :all, view_table: true)}
 | 
			
		||||
    do: {:ok, socket |> assign(class: :all, view_table: true)}
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_params(%{"id" => id}, _session, %{assigns: %{current_user: current_user}} = socket) do
 | 
			
		||||
    socket =
 | 
			
		||||
      socket
 | 
			
		||||
      |> assign(view_table: true)
 | 
			
		||||
      |> assign(:view_table, true)
 | 
			
		||||
      |> render_container(id, current_user)
 | 
			
		||||
 | 
			
		||||
    {:noreply, socket}
 | 
			
		||||
@@ -64,13 +64,13 @@ defmodule CanneryWeb.ContainerLive.Show do
 | 
			
		||||
          |> put_flash(:info, prompt)
 | 
			
		||||
          |> push_navigate(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(", ")
 | 
			
		||||
        {:error, %{action: :delete, errors: [packs: _error], valid?: false} = changeset} ->
 | 
			
		||||
          packs_error = changeset |> changeset_errors(:packs) |> Enum.join(", ")
 | 
			
		||||
 | 
			
		||||
          prompt =
 | 
			
		||||
            dgettext("errors", "Could not delete %{name}: %{error}",
 | 
			
		||||
              name: changeset |> Changeset.get_field(:name, "container"),
 | 
			
		||||
              error: ammo_groups_error
 | 
			
		||||
              error: packs_error
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
          socket |> put_flash(:error, prompt)
 | 
			
		||||
@@ -86,33 +86,33 @@ defmodule CanneryWeb.ContainerLive.Show do
 | 
			
		||||
    {:noreply, socket |> assign(:view_table, !view_table) |> render_container()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_type", %{"ammo_type" => %{"type" => "rifle"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:type, :rifle) |> render_container()}
 | 
			
		||||
  def handle_event("change_class", %{"type" => %{"class" => "rifle"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:class, :rifle) |> render_container()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_type", %{"ammo_type" => %{"type" => "shotgun"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:type, :shotgun) |> render_container()}
 | 
			
		||||
  def handle_event("change_class", %{"type" => %{"class" => "shotgun"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:class, :shotgun) |> render_container()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_type", %{"ammo_type" => %{"type" => "pistol"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:type, :pistol) |> render_container()}
 | 
			
		||||
  def handle_event("change_class", %{"type" => %{"class" => "pistol"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:class, :pistol) |> render_container()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_type", %{"ammo_type" => %{"type" => _all}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:type, :all) |> render_container()}
 | 
			
		||||
  def handle_event("change_class", %{"type" => %{"class" => _all}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:class, :all) |> render_container()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @spec render_container(Socket.t(), Container.id(), User.t()) :: Socket.t()
 | 
			
		||||
  defp render_container(
 | 
			
		||||
         %{assigns: %{type: type, live_action: live_action}} = socket,
 | 
			
		||||
         %{assigns: %{class: class, live_action: live_action}} = socket,
 | 
			
		||||
         id,
 | 
			
		||||
         current_user
 | 
			
		||||
       ) do
 | 
			
		||||
    %{name: container_name} = container = Containers.get_container!(id, current_user)
 | 
			
		||||
    ammo_groups = Ammo.list_ammo_groups_for_container(container, type, current_user)
 | 
			
		||||
    original_counts = ammo_groups |> Ammo.get_original_counts(current_user)
 | 
			
		||||
    cprs = ammo_groups |> Ammo.get_cprs(current_user)
 | 
			
		||||
    last_used_dates = ammo_groups |> ActivityLog.get_last_used_dates(current_user)
 | 
			
		||||
    packs = Ammo.list_packs_for_container(container, class, current_user)
 | 
			
		||||
    original_counts = packs |> Ammo.get_original_counts(current_user)
 | 
			
		||||
    cprs = packs |> Ammo.get_cprs(current_user)
 | 
			
		||||
    last_used_dates = packs |> ActivityLog.get_last_used_dates(current_user)
 | 
			
		||||
 | 
			
		||||
    page_title =
 | 
			
		||||
      case live_action do
 | 
			
		||||
@@ -125,8 +125,8 @@ defmodule CanneryWeb.ContainerLive.Show do
 | 
			
		||||
    |> assign(
 | 
			
		||||
      container: container,
 | 
			
		||||
      round_count: Ammo.get_round_count_for_container!(container, current_user),
 | 
			
		||||
      ammo_groups_count: Ammo.get_ammo_groups_count_for_container!(container, current_user),
 | 
			
		||||
      ammo_groups: ammo_groups,
 | 
			
		||||
      packs_count: Ammo.get_packs_count_for_container!(container, current_user),
 | 
			
		||||
      packs: packs,
 | 
			
		||||
      original_counts: original_counts,
 | 
			
		||||
      cprs: cprs,
 | 
			
		||||
      last_used_dates: last_used_dates,
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
 | 
			
		||||
  <span class="rounded-lg title text-lg">
 | 
			
		||||
    <%= gettext("Packs:") %>
 | 
			
		||||
    <%= @ammo_groups_count %>
 | 
			
		||||
    <%= @packs_count %>
 | 
			
		||||
  </span>
 | 
			
		||||
 | 
			
		||||
  <span class="rounded-lg title text-lg">
 | 
			
		||||
@@ -89,16 +89,16 @@
 | 
			
		||||
    <.form
 | 
			
		||||
      :let={f}
 | 
			
		||||
      for={%{}}
 | 
			
		||||
      as={:ammo_type}
 | 
			
		||||
      phx-change="change_type"
 | 
			
		||||
      phx-submit="change_type"
 | 
			
		||||
      as={:type}
 | 
			
		||||
      phx-change="change_class"
 | 
			
		||||
      phx-submit="change_class"
 | 
			
		||||
      class="flex items-center"
 | 
			
		||||
    >
 | 
			
		||||
      <%= label(f, :type, gettext("Type"), class: "title text-primary-600 text-lg text-center") %>
 | 
			
		||||
      <%= label(f, :class, gettext("Class"), class: "title text-primary-600 text-lg text-center") %>
 | 
			
		||||
 | 
			
		||||
      <%= select(
 | 
			
		||||
        f,
 | 
			
		||||
        :type,
 | 
			
		||||
        :class,
 | 
			
		||||
        [
 | 
			
		||||
          {gettext("All"), :all},
 | 
			
		||||
          {gettext("Rifle"), :rifle},
 | 
			
		||||
@@ -106,7 +106,7 @@
 | 
			
		||||
          {gettext("Pistol"), :pistol}
 | 
			
		||||
        ],
 | 
			
		||||
        class: "mx-2 my-1 min-w-md input input-primary",
 | 
			
		||||
        value: @type
 | 
			
		||||
        value: @class
 | 
			
		||||
      ) %>
 | 
			
		||||
    </.form>
 | 
			
		||||
 | 
			
		||||
@@ -118,33 +118,33 @@
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div class="w-full p-4">
 | 
			
		||||
    <%= if @ammo_groups |> Enum.empty?() do %>
 | 
			
		||||
    <%= if @packs |> Enum.empty?() do %>
 | 
			
		||||
      <h2 class="mx-4 title text-lg text-primary-600 text-center">
 | 
			
		||||
        <%= gettext("No ammo in this container") %>
 | 
			
		||||
      </h2>
 | 
			
		||||
    <% else %>
 | 
			
		||||
      <%= if @view_table do %>
 | 
			
		||||
        <.live_component
 | 
			
		||||
          module={CanneryWeb.Components.AmmoGroupTableComponent}
 | 
			
		||||
          id="ammo-type-show-table"
 | 
			
		||||
          ammo_groups={@ammo_groups}
 | 
			
		||||
          module={CanneryWeb.Components.PackTableComponent}
 | 
			
		||||
          id="type-show-table"
 | 
			
		||||
          packs={@packs}
 | 
			
		||||
          current_user={@current_user}
 | 
			
		||||
          show_used={false}
 | 
			
		||||
        >
 | 
			
		||||
          <:ammo_type :let={%{name: ammo_type_name} = ammo_type}>
 | 
			
		||||
            <.link navigate={Routes.ammo_type_show_path(Endpoint, :show, ammo_type)} class="link">
 | 
			
		||||
              <%= ammo_type_name %>
 | 
			
		||||
          <:type :let={%{name: type_name} = type}>
 | 
			
		||||
            <.link navigate={Routes.type_show_path(Endpoint, :show, type)} class="link">
 | 
			
		||||
              <%= type_name %>
 | 
			
		||||
            </.link>
 | 
			
		||||
          </:ammo_type>
 | 
			
		||||
          </:type>
 | 
			
		||||
        </.live_component>
 | 
			
		||||
      <% else %>
 | 
			
		||||
        <div class="flex flex-wrap justify-center items-stretch">
 | 
			
		||||
          <.ammo_group_card
 | 
			
		||||
            :for={%{id: ammo_group_id} = ammo_group <- @ammo_groups}
 | 
			
		||||
            ammo_group={ammo_group}
 | 
			
		||||
            original_count={Map.fetch!(@original_counts, ammo_group_id)}
 | 
			
		||||
            cpr={Map.get(@cprs, ammo_group_id)}
 | 
			
		||||
            last_used_date={Map.get(@last_used_dates, ammo_group_id)}
 | 
			
		||||
          <.pack_card
 | 
			
		||||
            :for={%{id: pack_id} = pack <- @packs}
 | 
			
		||||
            pack={pack}
 | 
			
		||||
            original_count={Map.fetch!(@original_counts, pack_id)}
 | 
			
		||||
            cpr={Map.get(@cprs, pack_id)}
 | 
			
		||||
            last_used_date={Map.get(@last_used_dates, pack_id)}
 | 
			
		||||
            current_user={@current_user}
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,38 +1,38 @@
 | 
			
		||||
defmodule CanneryWeb.AmmoGroupLive.FormComponent do
 | 
			
		||||
defmodule CanneryWeb.PackLive.FormComponent do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  Livecomponent that can update or create an Cannery.Ammo.AmmoGroup
 | 
			
		||||
  Livecomponent that can update or create an Cannery.Ammo.Pack
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb, :live_component
 | 
			
		||||
  alias Cannery.Ammo.{AmmoGroup, AmmoType}
 | 
			
		||||
  alias Cannery.Ammo.{Pack, Type}
 | 
			
		||||
  alias Cannery.{Accounts.User, Ammo, Containers, Containers.Container}
 | 
			
		||||
  alias Ecto.Changeset
 | 
			
		||||
  alias Phoenix.LiveView.Socket
 | 
			
		||||
 | 
			
		||||
  @ammo_group_create_limit 10_000
 | 
			
		||||
  @pack_create_limit 10_000
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  @spec update(
 | 
			
		||||
          %{:ammo_group => AmmoGroup.t(), :current_user => User.t(), optional(any) => any},
 | 
			
		||||
          %{:pack => Pack.t(), :current_user => User.t(), optional(any) => any},
 | 
			
		||||
          Socket.t()
 | 
			
		||||
        ) :: {:ok, Socket.t()}
 | 
			
		||||
  def update(%{ammo_group: _ammo_group} = assigns, socket) do
 | 
			
		||||
  def update(%{pack: _pack} = assigns, socket) do
 | 
			
		||||
    socket |> assign(assigns) |> update()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @spec update(Socket.t()) :: {:ok, Socket.t()}
 | 
			
		||||
  def update(%{assigns: %{current_user: current_user}} = socket) do
 | 
			
		||||
    %{assigns: %{ammo_types: ammo_types, containers: containers}} =
 | 
			
		||||
    %{assigns: %{types: types, containers: containers}} =
 | 
			
		||||
      socket =
 | 
			
		||||
      socket
 | 
			
		||||
      |> assign(:ammo_group_create_limit, @ammo_group_create_limit)
 | 
			
		||||
      |> assign(:ammo_types, Ammo.list_ammo_types(current_user, :all))
 | 
			
		||||
      |> assign(:pack_create_limit, @pack_create_limit)
 | 
			
		||||
      |> assign(:types, Ammo.list_types(current_user, :all))
 | 
			
		||||
      |> assign_new(:containers, fn -> Containers.list_containers(current_user) end)
 | 
			
		||||
 | 
			
		||||
    params =
 | 
			
		||||
      if ammo_types |> List.first() |> is_nil(),
 | 
			
		||||
      if types |> List.first() |> is_nil(),
 | 
			
		||||
        do: %{},
 | 
			
		||||
        else: %{} |> Map.put("ammo_type_id", ammo_types |> List.first() |> Map.get(:id))
 | 
			
		||||
        else: %{} |> Map.put("type_id", types |> List.first() |> Map.get(:id))
 | 
			
		||||
 | 
			
		||||
    params =
 | 
			
		||||
      if containers |> List.first() |> is_nil(),
 | 
			
		||||
@@ -43,16 +43,16 @@ defmodule CanneryWeb.AmmoGroupLive.FormComponent do
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_event("validate", %{"ammo_group" => ammo_group_params}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign_changeset(ammo_group_params, :validate)}
 | 
			
		||||
  def handle_event("validate", %{"pack" => pack_params}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign_changeset(pack_params, :validate)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event(
 | 
			
		||||
        "save",
 | 
			
		||||
        %{"ammo_group" => ammo_group_params},
 | 
			
		||||
        %{"pack" => pack_params},
 | 
			
		||||
        %{assigns: %{action: action}} = socket
 | 
			
		||||
      ) do
 | 
			
		||||
    save_ammo_group(socket, action, ammo_group_params)
 | 
			
		||||
    save_pack(socket, action, pack_params)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # HTML Helpers
 | 
			
		||||
@@ -62,16 +62,16 @@ defmodule CanneryWeb.AmmoGroupLive.FormComponent do
 | 
			
		||||
    containers |> Enum.map(fn %{id: id, name: name} -> {name, id} end)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @spec ammo_type_options([AmmoType.t()]) :: [{String.t(), AmmoType.id()}]
 | 
			
		||||
  defp ammo_type_options(ammo_types) do
 | 
			
		||||
    ammo_types |> Enum.map(fn %{id: id, name: name} -> {name, id} end)
 | 
			
		||||
  @spec type_options([Type.t()]) :: [{String.t(), Type.id()}]
 | 
			
		||||
  defp type_options(types) do
 | 
			
		||||
    types |> Enum.map(fn %{id: id, name: name} -> {name, id} end)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # Save Helpers
 | 
			
		||||
 | 
			
		||||
  defp assign_changeset(
 | 
			
		||||
         %{assigns: %{action: action, ammo_group: ammo_group, current_user: user}} = socket,
 | 
			
		||||
         ammo_group_params,
 | 
			
		||||
         %{assigns: %{action: action, pack: pack, current_user: user}} = socket,
 | 
			
		||||
         pack_params,
 | 
			
		||||
         changeset_action \\ nil
 | 
			
		||||
       ) do
 | 
			
		||||
    default_action =
 | 
			
		||||
@@ -83,12 +83,12 @@ defmodule CanneryWeb.AmmoGroupLive.FormComponent do
 | 
			
		||||
    changeset =
 | 
			
		||||
      case default_action do
 | 
			
		||||
        :insert ->
 | 
			
		||||
          ammo_type = maybe_get_ammo_type(ammo_group_params, user)
 | 
			
		||||
          container = maybe_get_container(ammo_group_params, user)
 | 
			
		||||
          ammo_group |> AmmoGroup.create_changeset(ammo_type, container, user, ammo_group_params)
 | 
			
		||||
          type = maybe_get_type(pack_params, user)
 | 
			
		||||
          container = maybe_get_container(pack_params, user)
 | 
			
		||||
          pack |> Pack.create_changeset(type, container, user, pack_params)
 | 
			
		||||
 | 
			
		||||
        :update ->
 | 
			
		||||
          ammo_group |> AmmoGroup.update_changeset(ammo_group_params, user)
 | 
			
		||||
          pack |> Pack.update_changeset(pack_params, user)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
    changeset =
 | 
			
		||||
@@ -107,22 +107,21 @@ defmodule CanneryWeb.AmmoGroupLive.FormComponent do
 | 
			
		||||
 | 
			
		||||
  defp maybe_get_container(_params_not_found, _user), do: nil
 | 
			
		||||
 | 
			
		||||
  defp maybe_get_ammo_type(%{"ammo_type_id" => ammo_type_id}, user)
 | 
			
		||||
       when is_binary(ammo_type_id) do
 | 
			
		||||
    ammo_type_id |> Ammo.get_ammo_type!(user)
 | 
			
		||||
  defp maybe_get_type(%{"type_id" => type_id}, user)
 | 
			
		||||
       when is_binary(type_id) do
 | 
			
		||||
    type_id |> Ammo.get_type!(user)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp maybe_get_ammo_type(_params_not_found, _user), do: nil
 | 
			
		||||
  defp maybe_get_type(_params_not_found, _user), do: nil
 | 
			
		||||
 | 
			
		||||
  defp save_ammo_group(
 | 
			
		||||
         %{assigns: %{ammo_group: ammo_group, current_user: current_user, return_to: return_to}} =
 | 
			
		||||
           socket,
 | 
			
		||||
  defp save_pack(
 | 
			
		||||
         %{assigns: %{pack: pack, current_user: current_user, return_to: return_to}} = socket,
 | 
			
		||||
         :edit,
 | 
			
		||||
         ammo_group_params
 | 
			
		||||
         pack_params
 | 
			
		||||
       ) do
 | 
			
		||||
    socket =
 | 
			
		||||
      case Ammo.update_ammo_group(ammo_group, ammo_group_params, current_user) do
 | 
			
		||||
        {:ok, _ammo_group} ->
 | 
			
		||||
      case Ammo.update_pack(pack, pack_params, current_user) do
 | 
			
		||||
        {:ok, _pack} ->
 | 
			
		||||
          prompt = dgettext("prompts", "Ammo updated successfully")
 | 
			
		||||
          socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
 | 
			
		||||
 | 
			
		||||
@@ -133,24 +132,24 @@ defmodule CanneryWeb.AmmoGroupLive.FormComponent do
 | 
			
		||||
    {:noreply, socket}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp save_ammo_group(
 | 
			
		||||
  defp save_pack(
 | 
			
		||||
         %{assigns: %{changeset: changeset}} = socket,
 | 
			
		||||
         action,
 | 
			
		||||
         %{"multiplier" => multiplier_str} = ammo_group_params
 | 
			
		||||
         %{"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 <= @ammo_group_create_limit ->
 | 
			
		||||
          socket |> create_multiple(ammo_group_params, multiplier)
 | 
			
		||||
        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: @ammo_group_create_limit,
 | 
			
		||||
              max: @pack_create_limit,
 | 
			
		||||
              multiplier: multiplier
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
@@ -176,11 +175,11 @@ defmodule CanneryWeb.AmmoGroupLive.FormComponent do
 | 
			
		||||
 | 
			
		||||
  defp create_multiple(
 | 
			
		||||
         %{assigns: %{current_user: current_user, return_to: return_to}} = socket,
 | 
			
		||||
         ammo_group_params,
 | 
			
		||||
         pack_params,
 | 
			
		||||
         multiplier
 | 
			
		||||
       ) do
 | 
			
		||||
    case Ammo.create_ammo_groups(ammo_group_params, multiplier, current_user) do
 | 
			
		||||
      {:ok, {count, _ammo_groups}} ->
 | 
			
		||||
    case Ammo.create_packs(pack_params, multiplier, current_user) do
 | 
			
		||||
      {:ok, {count, _packs}} ->
 | 
			
		||||
        prompt =
 | 
			
		||||
          dngettext(
 | 
			
		||||
            "prompts",
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
  <.form
 | 
			
		||||
    :let={f}
 | 
			
		||||
    for={@changeset}
 | 
			
		||||
    id="ammo_group-form"
 | 
			
		||||
    id="pack-form"
 | 
			
		||||
    phx-target={@myself}
 | 
			
		||||
    phx-change="validate"
 | 
			
		||||
    phx-submit="save"
 | 
			
		||||
@@ -19,11 +19,11 @@
 | 
			
		||||
      <%= changeset_errors(@changeset) %>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <%= label(f, :ammo_type_id, gettext("Ammo type"), class: "title text-lg text-primary-600") %>
 | 
			
		||||
    <%= select(f, :ammo_type_id, ammo_type_options(@ammo_types),
 | 
			
		||||
    <%= label(f, :type_id, gettext("Type"), class: "title text-lg text-primary-600") %>
 | 
			
		||||
    <%= select(f, :type_id, type_options(@types),
 | 
			
		||||
      class: "text-center col-span-2 input input-primary"
 | 
			
		||||
    ) %>
 | 
			
		||||
    <%= error_tag(f, :ammo_type_id, "col-span-3 text-center") %>
 | 
			
		||||
    <%= error_tag(f, :type_id, "col-span-3 text-center") %>
 | 
			
		||||
 | 
			
		||||
    <%= label(f, :count, gettext("Count"), class: "title text-lg text-primary-600") %>
 | 
			
		||||
    <%= number_input(f, :count,
 | 
			
		||||
@@ -39,6 +39,13 @@
 | 
			
		||||
    ) %>
 | 
			
		||||
    <%= error_tag(f, :price_paid, "col-span-3 text-center") %>
 | 
			
		||||
 | 
			
		||||
    <%= label(f, :lot_number, gettext("Lot number"), class: "title text-lg text-primary-600") %>
 | 
			
		||||
    <%= text_input(f, :lot_number,
 | 
			
		||||
      class: "text-center col-span-2 input input-primary",
 | 
			
		||||
      maxlength: 255
 | 
			
		||||
    ) %>
 | 
			
		||||
    <%= error_tag(f, :price_paid, "col-span-3 text-center") %>
 | 
			
		||||
 | 
			
		||||
    <%= label(f, :purchased_on, gettext("Purchased on"), class: "title text-lg text-primary-600") %>
 | 
			
		||||
    <%= date_input(f, :purchased_on,
 | 
			
		||||
      class: "input input-primary col-span-2",
 | 
			
		||||
@@ -49,7 +56,7 @@
 | 
			
		||||
 | 
			
		||||
    <%= label(f, :notes, gettext("Notes"), class: "title text-lg text-primary-600") %>
 | 
			
		||||
    <%= textarea(f, :notes,
 | 
			
		||||
      id: "ammo-group-form-notes",
 | 
			
		||||
      id: "pack-form-notes",
 | 
			
		||||
      class: "text-center col-span-2 input input-primary",
 | 
			
		||||
      phx_hook: "MaintainAttrs",
 | 
			
		||||
      phx_update: "ignore"
 | 
			
		||||
@@ -68,7 +75,7 @@
 | 
			
		||||
 | 
			
		||||
        <%= label(f, :multiplier, gettext("Copies"), class: "title text-lg text-primary-600") %>
 | 
			
		||||
        <%= number_input(f, :multiplier,
 | 
			
		||||
          max: @ammo_group_create_limit,
 | 
			
		||||
          max: @pack_create_limit,
 | 
			
		||||
          class: "text-center input input-primary",
 | 
			
		||||
          value: 1,
 | 
			
		||||
          phx_update: "ignore"
 | 
			
		||||
@@ -1,34 +1,39 @@
 | 
			
		||||
defmodule CanneryWeb.AmmoGroupLive.Index do
 | 
			
		||||
defmodule CanneryWeb.PackLive.Index do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  Liveview to show a Cannery.Ammo.AmmoGroup index
 | 
			
		||||
  Liveview to show a Cannery.Ammo.Pack index
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb, :live_view
 | 
			
		||||
  alias Cannery.{Ammo, Ammo.AmmoGroup, Containers}
 | 
			
		||||
  alias Cannery.{Ammo, Ammo.Pack, Containers}
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def mount(%{"search" => search}, _session, socket) do
 | 
			
		||||
    {:ok, socket |> assign(type: :all, show_used: false, search: search) |> display_ammo_groups()}
 | 
			
		||||
    socket =
 | 
			
		||||
      socket
 | 
			
		||||
      |> assign(class: :all, show_used: false, search: search)
 | 
			
		||||
      |> display_packs()
 | 
			
		||||
 | 
			
		||||
    {:ok, socket}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def mount(_params, _session, socket) do
 | 
			
		||||
    {:ok, socket |> assign(type: :all, show_used: false, search: nil) |> display_ammo_groups()}
 | 
			
		||||
    {:ok, socket |> assign(class: :all, show_used: false, search: nil) |> display_packs()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_params(params, _url, %{assigns: %{live_action: live_action}} = socket) do
 | 
			
		||||
    {:noreply, apply_action(socket, live_action, params) |> display_ammo_groups()}
 | 
			
		||||
    {:noreply, apply_action(socket, live_action, params) |> display_packs()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp apply_action(
 | 
			
		||||
         %{assigns: %{current_user: current_user}} = socket,
 | 
			
		||||
         :add_shot_group,
 | 
			
		||||
         :add_shot_record,
 | 
			
		||||
         %{"id" => id}
 | 
			
		||||
       ) do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("Record shots"),
 | 
			
		||||
      ammo_group: Ammo.get_ammo_group!(id, current_user)
 | 
			
		||||
      pack: Ammo.get_pack!(id, current_user)
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -36,7 +41,7 @@ defmodule CanneryWeb.AmmoGroupLive.Index do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("Move ammo"),
 | 
			
		||||
      ammo_group: Ammo.get_ammo_group!(id, current_user)
 | 
			
		||||
      pack: Ammo.get_pack!(id, current_user)
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -44,7 +49,7 @@ defmodule CanneryWeb.AmmoGroupLive.Index do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("Edit ammo"),
 | 
			
		||||
      ammo_group: Ammo.get_ammo_group!(id, current_user)
 | 
			
		||||
      pack: Ammo.get_pack!(id, current_user)
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -52,7 +57,7 @@ defmodule CanneryWeb.AmmoGroupLive.Index do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: dgettext("actions", "Add Ammo"),
 | 
			
		||||
      ammo_group: %{Ammo.get_ammo_group!(id, current_user) | id: nil}
 | 
			
		||||
      pack: %{Ammo.get_pack!(id, current_user) | id: nil}
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -60,7 +65,7 @@ defmodule CanneryWeb.AmmoGroupLive.Index do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: dgettext("actions", "Add Ammo"),
 | 
			
		||||
      ammo_group: %AmmoGroup{}
 | 
			
		||||
      pack: %Pack{}
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -69,7 +74,7 @@ defmodule CanneryWeb.AmmoGroupLive.Index do
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("Ammo"),
 | 
			
		||||
      search: nil,
 | 
			
		||||
      ammo_group: nil
 | 
			
		||||
      pack: nil
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -78,86 +83,84 @@ defmodule CanneryWeb.AmmoGroupLive.Index do
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("Ammo"),
 | 
			
		||||
      search: search,
 | 
			
		||||
      ammo_group: nil
 | 
			
		||||
      pack: nil
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_event("delete", %{"id" => id}, %{assigns: %{current_user: current_user}} = socket) do
 | 
			
		||||
    Ammo.get_ammo_group!(id, current_user) |> Ammo.delete_ammo_group!(current_user)
 | 
			
		||||
    Ammo.get_pack!(id, current_user) |> Ammo.delete_pack!(current_user)
 | 
			
		||||
 | 
			
		||||
    prompt = dgettext("prompts", "Ammo deleted succesfully")
 | 
			
		||||
 | 
			
		||||
    {:noreply, socket |> put_flash(:info, prompt) |> display_ammo_groups()}
 | 
			
		||||
    {:noreply, socket |> put_flash(:info, prompt) |> display_packs()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event(
 | 
			
		||||
        "toggle_staged",
 | 
			
		||||
        %{"ammo_group_id" => id},
 | 
			
		||||
        %{"pack_id" => id},
 | 
			
		||||
        %{assigns: %{current_user: current_user}} = socket
 | 
			
		||||
      ) do
 | 
			
		||||
    ammo_group = Ammo.get_ammo_group!(id, current_user)
 | 
			
		||||
    pack = Ammo.get_pack!(id, current_user)
 | 
			
		||||
 | 
			
		||||
    {:ok, _ammo_group} =
 | 
			
		||||
      ammo_group |> Ammo.update_ammo_group(%{"staged" => !ammo_group.staged}, current_user)
 | 
			
		||||
    {:ok, _pack} = pack |> Ammo.update_pack(%{"staged" => !pack.staged}, current_user)
 | 
			
		||||
 | 
			
		||||
    {:noreply, socket |> display_ammo_groups()}
 | 
			
		||||
    {:noreply, socket |> display_packs()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("toggle_show_used", _params, %{assigns: %{show_used: show_used}} = socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:show_used, !show_used) |> display_ammo_groups()}
 | 
			
		||||
    {:noreply, socket |> assign(:show_used, !show_used) |> display_packs()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("search", %{"search" => %{"search_term" => ""}}, socket) do
 | 
			
		||||
    {:noreply, socket |> push_patch(to: Routes.ammo_group_index_path(Endpoint, :index))}
 | 
			
		||||
    {:noreply, socket |> push_patch(to: Routes.pack_index_path(Endpoint, :index))}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("search", %{"search" => %{"search_term" => search_term}}, socket) do
 | 
			
		||||
    socket =
 | 
			
		||||
      socket |> push_patch(to: Routes.ammo_group_index_path(Endpoint, :search, search_term))
 | 
			
		||||
    socket = socket |> push_patch(to: Routes.pack_index_path(Endpoint, :search, search_term))
 | 
			
		||||
 | 
			
		||||
    {:noreply, socket}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_type", %{"ammo_type" => %{"type" => "rifle"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:type, :rifle) |> display_ammo_groups()}
 | 
			
		||||
  def handle_event("change_class", %{"type" => %{"class" => "rifle"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:class, :rifle) |> display_packs()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_type", %{"ammo_type" => %{"type" => "shotgun"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:type, :shotgun) |> display_ammo_groups()}
 | 
			
		||||
  def handle_event("change_class", %{"type" => %{"class" => "shotgun"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:class, :shotgun) |> display_packs()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_type", %{"ammo_type" => %{"type" => "pistol"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:type, :pistol) |> display_ammo_groups()}
 | 
			
		||||
  def handle_event("change_class", %{"type" => %{"class" => "pistol"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:class, :pistol) |> display_packs()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_type", %{"ammo_type" => %{"type" => _all}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:type, :all) |> display_ammo_groups()}
 | 
			
		||||
  def handle_event("change_class", %{"type" => %{"class" => _all}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:class, :all) |> display_packs()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp display_ammo_groups(
 | 
			
		||||
  defp display_packs(
 | 
			
		||||
         %{
 | 
			
		||||
           assigns: %{
 | 
			
		||||
             type: type,
 | 
			
		||||
             class: class,
 | 
			
		||||
             search: search,
 | 
			
		||||
             current_user: current_user,
 | 
			
		||||
             show_used: show_used
 | 
			
		||||
           }
 | 
			
		||||
         } = socket
 | 
			
		||||
       ) do
 | 
			
		||||
    # get total number of ammo groups to determine whether to display onboarding
 | 
			
		||||
    # get total number of packs to determine whether to display onboarding
 | 
			
		||||
    # prompts
 | 
			
		||||
    ammo_groups_count = Ammo.get_ammo_groups_count!(current_user, true)
 | 
			
		||||
    ammo_groups = Ammo.list_ammo_groups(search, type, current_user, show_used)
 | 
			
		||||
    ammo_types_count = Ammo.get_ammo_types_count!(current_user)
 | 
			
		||||
    packs_count = Ammo.get_packs_count!(current_user, true)
 | 
			
		||||
    packs = Ammo.list_packs(search, class, current_user, show_used)
 | 
			
		||||
    types_count = Ammo.get_types_count!(current_user)
 | 
			
		||||
    containers_count = Containers.get_containers_count!(current_user)
 | 
			
		||||
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      ammo_groups: ammo_groups,
 | 
			
		||||
      ammo_types_count: ammo_types_count,
 | 
			
		||||
      packs: packs,
 | 
			
		||||
      types_count: types_count,
 | 
			
		||||
      containers_count: containers_count,
 | 
			
		||||
      ammo_groups_count: ammo_groups_count
 | 
			
		||||
      packs_count: packs_count
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -14,27 +14,27 @@
 | 
			
		||||
          <%= dgettext("actions", "add a container first") %>
 | 
			
		||||
        </.link>
 | 
			
		||||
      </div>
 | 
			
		||||
    <% @ammo_types_count == 0 -> %>
 | 
			
		||||
    <% @types_count == 0 -> %>
 | 
			
		||||
      <div class="flex justify-center items-center">
 | 
			
		||||
        <h2 class="m-2 title text-md text-primary-600">
 | 
			
		||||
          <%= dgettext("prompts", "You'll need to") %>
 | 
			
		||||
        </h2>
 | 
			
		||||
 | 
			
		||||
        <.link navigate={Routes.ammo_type_index_path(Endpoint, :new)} class="btn btn-primary">
 | 
			
		||||
          <%= dgettext("actions", "add an ammo type first") %>
 | 
			
		||||
        <.link navigate={Routes.type_index_path(Endpoint, :new)} class="btn btn-primary">
 | 
			
		||||
          <%= dgettext("actions", "add a type first") %>
 | 
			
		||||
        </.link>
 | 
			
		||||
      </div>
 | 
			
		||||
    <% @ammo_groups_count == 0 -> %>
 | 
			
		||||
    <% @packs_count == 0 -> %>
 | 
			
		||||
      <h2 class="title text-xl text-primary-600">
 | 
			
		||||
        <%= gettext("No ammo") %>
 | 
			
		||||
        <%= display_emoji("😔") %>
 | 
			
		||||
      </h2>
 | 
			
		||||
 | 
			
		||||
      <.link patch={Routes.ammo_group_index_path(Endpoint, :new)} class="btn btn-primary">
 | 
			
		||||
      <.link patch={Routes.pack_index_path(Endpoint, :new)} class="btn btn-primary">
 | 
			
		||||
        <%= dgettext("actions", "Add your first box!") %>
 | 
			
		||||
      </.link>
 | 
			
		||||
    <% true -> %>
 | 
			
		||||
      <.link patch={Routes.ammo_group_index_path(Endpoint, :new)} class="btn btn-primary">
 | 
			
		||||
      <.link patch={Routes.pack_index_path(Endpoint, :new)} class="btn btn-primary">
 | 
			
		||||
        <%= dgettext("actions", "Add Ammo") %>
 | 
			
		||||
      </.link>
 | 
			
		||||
 | 
			
		||||
@@ -42,16 +42,18 @@
 | 
			
		||||
        <.form
 | 
			
		||||
          :let={f}
 | 
			
		||||
          for={%{}}
 | 
			
		||||
          as={:ammo_type}
 | 
			
		||||
          phx-change="change_type"
 | 
			
		||||
          phx-submit="change_type"
 | 
			
		||||
          as={:type}
 | 
			
		||||
          phx-change="change_class"
 | 
			
		||||
          phx-submit="change_class"
 | 
			
		||||
          class="flex items-center"
 | 
			
		||||
        >
 | 
			
		||||
          <%= label(f, :type, gettext("Type"), class: "title text-primary-600 text-lg text-center") %>
 | 
			
		||||
          <%= label(f, :class, gettext("Class"),
 | 
			
		||||
            class: "title text-primary-600 text-lg text-center"
 | 
			
		||||
          ) %>
 | 
			
		||||
 | 
			
		||||
          <%= select(
 | 
			
		||||
            f,
 | 
			
		||||
            :type,
 | 
			
		||||
            :class,
 | 
			
		||||
            [
 | 
			
		||||
              {gettext("All"), :all},
 | 
			
		||||
              {gettext("Rifle"), :rifle},
 | 
			
		||||
@@ -59,7 +61,7 @@
 | 
			
		||||
              {gettext("Pistol"), :pistol}
 | 
			
		||||
            ],
 | 
			
		||||
            class: "mx-2 my-1 min-w-md input input-primary",
 | 
			
		||||
            value: @type
 | 
			
		||||
            value: @class
 | 
			
		||||
          ) %>
 | 
			
		||||
        </.form>
 | 
			
		||||
 | 
			
		||||
@@ -87,46 +89,46 @@
 | 
			
		||||
        </.toggle_button>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <%= if @ammo_groups |> Enum.empty?() do %>
 | 
			
		||||
      <%= if @packs |> Enum.empty?() do %>
 | 
			
		||||
        <h2 class="title text-xl text-primary-600">
 | 
			
		||||
          <%= gettext("No Ammo") %>
 | 
			
		||||
          <%= display_emoji("😔") %>
 | 
			
		||||
        </h2>
 | 
			
		||||
      <% else %>
 | 
			
		||||
        <.live_component
 | 
			
		||||
          module={CanneryWeb.Components.AmmoGroupTableComponent}
 | 
			
		||||
          id="ammo-group-index-table"
 | 
			
		||||
          ammo_groups={@ammo_groups}
 | 
			
		||||
          module={CanneryWeb.Components.PackTableComponent}
 | 
			
		||||
          id="pack-index-table"
 | 
			
		||||
          packs={@packs}
 | 
			
		||||
          current_user={@current_user}
 | 
			
		||||
          show_used={@show_used}
 | 
			
		||||
        >
 | 
			
		||||
          <:ammo_type :let={%{name: ammo_type_name} = ammo_type}>
 | 
			
		||||
            <.link navigate={Routes.ammo_type_show_path(Endpoint, :show, ammo_type)} class="link">
 | 
			
		||||
              <%= ammo_type_name %>
 | 
			
		||||
          <:type :let={%{name: type_name} = type}>
 | 
			
		||||
            <.link navigate={Routes.type_show_path(Endpoint, :show, type)} class="link">
 | 
			
		||||
              <%= type_name %>
 | 
			
		||||
            </.link>
 | 
			
		||||
          </:ammo_type>
 | 
			
		||||
          <:range :let={ammo_group}>
 | 
			
		||||
          </:type>
 | 
			
		||||
          <:range :let={pack}>
 | 
			
		||||
            <div class="min-w-20 py-2 px-4 h-full flex flew-wrap justify-center items-center">
 | 
			
		||||
              <button
 | 
			
		||||
                type="button"
 | 
			
		||||
                class="mx-2 my-1 text-sm btn btn-primary"
 | 
			
		||||
                phx-click="toggle_staged"
 | 
			
		||||
                phx-value-ammo_group_id={ammo_group.id}
 | 
			
		||||
                phx-value-pack_id={pack.id}
 | 
			
		||||
              >
 | 
			
		||||
                <%= if ammo_group.staged,
 | 
			
		||||
                <%= if pack.staged,
 | 
			
		||||
                  do: dgettext("actions", "Unstage"),
 | 
			
		||||
                  else: dgettext("actions", "Stage") %>
 | 
			
		||||
              </button>
 | 
			
		||||
 | 
			
		||||
              <.link
 | 
			
		||||
                patch={Routes.ammo_group_index_path(Endpoint, :add_shot_group, ammo_group)}
 | 
			
		||||
                patch={Routes.pack_index_path(Endpoint, :add_shot_record, pack)}
 | 
			
		||||
                class="mx-2 my-1 text-sm btn btn-primary"
 | 
			
		||||
              >
 | 
			
		||||
                <%= dgettext("actions", "Record shots") %>
 | 
			
		||||
              </.link>
 | 
			
		||||
            </div>
 | 
			
		||||
          </:range>
 | 
			
		||||
          <:container :let={{ammo_group, %{name: container_name} = container}}>
 | 
			
		||||
          <:container :let={{pack, %{name: container_name} = container}}>
 | 
			
		||||
            <div class="min-w-20 py-2 px-4 h-full flex flew-wrap justify-center items-center">
 | 
			
		||||
              <.link
 | 
			
		||||
                navigate={Routes.container_show_path(Endpoint, :show, container)}
 | 
			
		||||
@@ -136,45 +138,41 @@
 | 
			
		||||
              </.link>
 | 
			
		||||
 | 
			
		||||
              <.link
 | 
			
		||||
                patch={Routes.ammo_group_index_path(Endpoint, :move, ammo_group)}
 | 
			
		||||
                patch={Routes.pack_index_path(Endpoint, :move, pack)}
 | 
			
		||||
                class="mx-2 my-1 text-sm btn btn-primary"
 | 
			
		||||
              >
 | 
			
		||||
                <%= dgettext("actions", "Move ammo") %>
 | 
			
		||||
              </.link>
 | 
			
		||||
            </div>
 | 
			
		||||
          </:container>
 | 
			
		||||
          <:actions :let={%{count: ammo_group_count} = ammo_group}>
 | 
			
		||||
          <:actions :let={%{count: pack_count} = pack}>
 | 
			
		||||
            <div class="py-2 px-4 h-full space-x-4 flex justify-center items-center">
 | 
			
		||||
              <.link
 | 
			
		||||
                navigate={Routes.ammo_group_show_path(Endpoint, :show, ammo_group)}
 | 
			
		||||
                navigate={Routes.pack_show_path(Endpoint, :show, pack)}
 | 
			
		||||
                class="text-primary-600 link"
 | 
			
		||||
                aria-label={
 | 
			
		||||
                  dgettext("actions", "View ammo group of %{ammo_group_count} bullets",
 | 
			
		||||
                    ammo_group_count: ammo_group_count
 | 
			
		||||
                  )
 | 
			
		||||
                  dgettext("actions", "View pack of %{pack_count} bullets", pack_count: pack_count)
 | 
			
		||||
                }
 | 
			
		||||
              >
 | 
			
		||||
                <i class="fa-fw fa-lg fas fa-eye"></i>
 | 
			
		||||
              </.link>
 | 
			
		||||
 | 
			
		||||
              <.link
 | 
			
		||||
                patch={Routes.ammo_group_index_path(Endpoint, :edit, ammo_group)}
 | 
			
		||||
                patch={Routes.pack_index_path(Endpoint, :edit, pack)}
 | 
			
		||||
                class="text-primary-600 link"
 | 
			
		||||
                aria-label={
 | 
			
		||||
                  dgettext("actions", "Edit ammo group of %{ammo_group_count} bullets",
 | 
			
		||||
                    ammo_group_count: ammo_group_count
 | 
			
		||||
                  )
 | 
			
		||||
                  dgettext("actions", "Edit pack of %{pack_count} bullets", pack_count: pack_count)
 | 
			
		||||
                }
 | 
			
		||||
              >
 | 
			
		||||
                <i class="fa-fw fa-lg fas fa-edit"></i>
 | 
			
		||||
              </.link>
 | 
			
		||||
 | 
			
		||||
              <.link
 | 
			
		||||
                patch={Routes.ammo_group_index_path(Endpoint, :clone, ammo_group)}
 | 
			
		||||
                patch={Routes.pack_index_path(Endpoint, :clone, pack)}
 | 
			
		||||
                class="text-primary-600 link"
 | 
			
		||||
                aria-label={
 | 
			
		||||
                  dgettext("actions", "Clone ammo group of %{ammo_group_count} bullets",
 | 
			
		||||
                    ammo_group_count: ammo_group_count
 | 
			
		||||
                  dgettext("actions", "Clone pack of %{pack_count} bullets",
 | 
			
		||||
                    pack_count: pack_count
 | 
			
		||||
                  )
 | 
			
		||||
                }
 | 
			
		||||
              >
 | 
			
		||||
@@ -185,11 +183,11 @@
 | 
			
		||||
                href="#"
 | 
			
		||||
                class="text-primary-600 link"
 | 
			
		||||
                phx-click="delete"
 | 
			
		||||
                phx-value-id={ammo_group.id}
 | 
			
		||||
                phx-value-id={pack.id}
 | 
			
		||||
                data-confirm={dgettext("prompts", "Are you sure you want to delete this ammo?")}
 | 
			
		||||
                aria-label={
 | 
			
		||||
                  dgettext("actions", "Delete ammo group of %{ammo_group_count} bullets",
 | 
			
		||||
                    ammo_group_count: ammo_group_count
 | 
			
		||||
                  dgettext("actions", "Delete pack of %{pack_count} bullets",
 | 
			
		||||
                    pack_count: pack_count
 | 
			
		||||
                  )
 | 
			
		||||
                }
 | 
			
		||||
              >
 | 
			
		||||
@@ -204,38 +202,38 @@
 | 
			
		||||
 | 
			
		||||
<%= case @live_action do %>
 | 
			
		||||
  <% create when create in [:new, :edit, :clone] -> %>
 | 
			
		||||
    <.modal return_to={Routes.ammo_group_index_path(Endpoint, :index)}>
 | 
			
		||||
    <.modal return_to={Routes.pack_index_path(Endpoint, :index)}>
 | 
			
		||||
      <.live_component
 | 
			
		||||
        module={CanneryWeb.AmmoGroupLive.FormComponent}
 | 
			
		||||
        id={@ammo_group.id || :new}
 | 
			
		||||
        module={CanneryWeb.PackLive.FormComponent}
 | 
			
		||||
        id={@pack.id || :new}
 | 
			
		||||
        title={@page_title}
 | 
			
		||||
        action={@live_action}
 | 
			
		||||
        ammo_group={@ammo_group}
 | 
			
		||||
        return_to={Routes.ammo_group_index_path(Endpoint, :index)}
 | 
			
		||||
        pack={@pack}
 | 
			
		||||
        return_to={Routes.pack_index_path(Endpoint, :index)}
 | 
			
		||||
        current_user={@current_user}
 | 
			
		||||
      />
 | 
			
		||||
    </.modal>
 | 
			
		||||
  <% :add_shot_group -> %>
 | 
			
		||||
    <.modal return_to={Routes.ammo_group_index_path(Endpoint, :index)}>
 | 
			
		||||
  <% :add_shot_record -> %>
 | 
			
		||||
    <.modal return_to={Routes.pack_index_path(Endpoint, :index)}>
 | 
			
		||||
      <.live_component
 | 
			
		||||
        module={CanneryWeb.Components.AddShotGroupComponent}
 | 
			
		||||
        module={CanneryWeb.Components.AddShotRecordComponent}
 | 
			
		||||
        id={:new}
 | 
			
		||||
        title={@page_title}
 | 
			
		||||
        action={@live_action}
 | 
			
		||||
        ammo_group={@ammo_group}
 | 
			
		||||
        return_to={Routes.ammo_group_index_path(Endpoint, :index)}
 | 
			
		||||
        pack={@pack}
 | 
			
		||||
        return_to={Routes.pack_index_path(Endpoint, :index)}
 | 
			
		||||
        current_user={@current_user}
 | 
			
		||||
      />
 | 
			
		||||
    </.modal>
 | 
			
		||||
  <% :move -> %>
 | 
			
		||||
    <.modal return_to={Routes.ammo_group_index_path(Endpoint, :index)}>
 | 
			
		||||
    <.modal return_to={Routes.pack_index_path(Endpoint, :index)}>
 | 
			
		||||
      <.live_component
 | 
			
		||||
        module={CanneryWeb.Components.MoveAmmoGroupComponent}
 | 
			
		||||
        id={@ammo_group.id}
 | 
			
		||||
        module={CanneryWeb.Components.MovePackComponent}
 | 
			
		||||
        id={@pack.id}
 | 
			
		||||
        title={@page_title}
 | 
			
		||||
        action={@live_action}
 | 
			
		||||
        ammo_group={@ammo_group}
 | 
			
		||||
        return_to={Routes.ammo_group_index_path(Endpoint, :index)}
 | 
			
		||||
        pack={@pack}
 | 
			
		||||
        return_to={Routes.pack_index_path(Endpoint, :index)}
 | 
			
		||||
        current_user={@current_user}
 | 
			
		||||
      />
 | 
			
		||||
    </.modal>
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
defmodule CanneryWeb.AmmoGroupLive.Show do
 | 
			
		||||
defmodule CanneryWeb.PackLive.Show do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  Liveview for showing and editing an Cannery.Ammo.AmmoGroup
 | 
			
		||||
  Liveview for showing and editing an Cannery.Ammo.Pack
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb, :live_view
 | 
			
		||||
  alias Cannery.{ActivityLog, ActivityLog.ShotGroup}
 | 
			
		||||
  alias Cannery.{Ammo, Ammo.AmmoGroup}
 | 
			
		||||
  alias Cannery.{ActivityLog, ActivityLog.ShotRecord}
 | 
			
		||||
  alias Cannery.{Ammo, Ammo.Pack}
 | 
			
		||||
  alias Cannery.{ComparableDate, Containers}
 | 
			
		||||
  alias CanneryWeb.Endpoint
 | 
			
		||||
  alias Phoenix.LiveView.Socket
 | 
			
		||||
@@ -15,16 +15,16 @@ defmodule CanneryWeb.AmmoGroupLive.Show do
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_params(
 | 
			
		||||
        %{"id" => id, "shot_group_id" => shot_group_id},
 | 
			
		||||
        %{"id" => id, "shot_record_id" => shot_record_id},
 | 
			
		||||
        _url,
 | 
			
		||||
        %{assigns: %{live_action: live_action, current_user: current_user}} = socket
 | 
			
		||||
      ) do
 | 
			
		||||
    shot_group = ActivityLog.get_shot_group!(shot_group_id, current_user)
 | 
			
		||||
    shot_record = ActivityLog.get_shot_record!(shot_record_id, current_user)
 | 
			
		||||
 | 
			
		||||
    socket =
 | 
			
		||||
      socket
 | 
			
		||||
      |> assign(page_title: page_title(live_action), shot_group: shot_group)
 | 
			
		||||
      |> display_ammo_group(id)
 | 
			
		||||
      |> assign(page_title: page_title(live_action), shot_record: shot_record)
 | 
			
		||||
      |> display_pack(id)
 | 
			
		||||
 | 
			
		||||
    {:noreply, socket}
 | 
			
		||||
  end
 | 
			
		||||
@@ -33,13 +33,13 @@ defmodule CanneryWeb.AmmoGroupLive.Show do
 | 
			
		||||
    socket =
 | 
			
		||||
      socket
 | 
			
		||||
      |> assign(page_title: page_title(live_action))
 | 
			
		||||
      |> display_ammo_group(id)
 | 
			
		||||
      |> display_pack(id)
 | 
			
		||||
 | 
			
		||||
    {:noreply, socket}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp page_title(:add_shot_group), do: gettext("Record Shots")
 | 
			
		||||
  defp page_title(:edit_shot_group), do: gettext("Edit Shot Records")
 | 
			
		||||
  defp page_title(:add_shot_record), do: gettext("Record Shots")
 | 
			
		||||
  defp page_title(:edit_shot_record), do: gettext("Edit Shot Record")
 | 
			
		||||
  defp page_title(:move), do: gettext("Move Ammo")
 | 
			
		||||
  defp page_title(:show), do: gettext("Show Ammo")
 | 
			
		||||
  defp page_title(:edit), do: gettext("Edit Ammo")
 | 
			
		||||
@@ -48,12 +48,12 @@ defmodule CanneryWeb.AmmoGroupLive.Show do
 | 
			
		||||
  def handle_event(
 | 
			
		||||
        "delete",
 | 
			
		||||
        _params,
 | 
			
		||||
        %{assigns: %{ammo_group: ammo_group, current_user: current_user}} = socket
 | 
			
		||||
        %{assigns: %{pack: pack, current_user: current_user}} = socket
 | 
			
		||||
      ) do
 | 
			
		||||
    ammo_group |> Ammo.delete_ammo_group!(current_user)
 | 
			
		||||
    pack |> Ammo.delete_pack!(current_user)
 | 
			
		||||
 | 
			
		||||
    prompt = dgettext("prompts", "Ammo deleted succesfully")
 | 
			
		||||
    redirect_to = Routes.ammo_group_index_path(socket, :index)
 | 
			
		||||
    redirect_to = Routes.pack_index_path(socket, :index)
 | 
			
		||||
 | 
			
		||||
    {:noreply, socket |> put_flash(:info, prompt) |> push_navigate(to: redirect_to)}
 | 
			
		||||
  end
 | 
			
		||||
@@ -61,31 +61,30 @@ defmodule CanneryWeb.AmmoGroupLive.Show do
 | 
			
		||||
  def handle_event(
 | 
			
		||||
        "toggle_staged",
 | 
			
		||||
        _params,
 | 
			
		||||
        %{assigns: %{ammo_group: ammo_group, current_user: current_user}} = socket
 | 
			
		||||
        %{assigns: %{pack: pack, current_user: current_user}} = socket
 | 
			
		||||
      ) do
 | 
			
		||||
    {:ok, ammo_group} =
 | 
			
		||||
      ammo_group |> Ammo.update_ammo_group(%{"staged" => !ammo_group.staged}, current_user)
 | 
			
		||||
    {:ok, pack} = pack |> Ammo.update_pack(%{"staged" => !pack.staged}, current_user)
 | 
			
		||||
 | 
			
		||||
    {:noreply, socket |> display_ammo_group(ammo_group)}
 | 
			
		||||
    {:noreply, socket |> display_pack(pack)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event(
 | 
			
		||||
        "delete_shot_group",
 | 
			
		||||
        "delete_shot_record",
 | 
			
		||||
        %{"id" => id},
 | 
			
		||||
        %{assigns: %{ammo_group: %{id: ammo_group_id}, current_user: current_user}} = socket
 | 
			
		||||
        %{assigns: %{pack: %{id: pack_id}, current_user: current_user}} = socket
 | 
			
		||||
      ) do
 | 
			
		||||
    {:ok, _} =
 | 
			
		||||
      ActivityLog.get_shot_group!(id, current_user)
 | 
			
		||||
      |> ActivityLog.delete_shot_group(current_user)
 | 
			
		||||
      ActivityLog.get_shot_record!(id, current_user)
 | 
			
		||||
      |> ActivityLog.delete_shot_record(current_user)
 | 
			
		||||
 | 
			
		||||
    prompt = dgettext("prompts", "Shot records deleted succesfully")
 | 
			
		||||
    {:noreply, socket |> put_flash(:info, prompt) |> display_ammo_group(ammo_group_id)}
 | 
			
		||||
    {:noreply, socket |> put_flash(:info, prompt) |> display_pack(pack_id)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @spec display_ammo_group(Socket.t(), AmmoGroup.t() | AmmoGroup.id()) :: Socket.t()
 | 
			
		||||
  defp display_ammo_group(
 | 
			
		||||
  @spec display_pack(Socket.t(), Pack.t() | Pack.id()) :: Socket.t()
 | 
			
		||||
  defp display_pack(
 | 
			
		||||
         %{assigns: %{current_user: current_user}} = socket,
 | 
			
		||||
         %AmmoGroup{container_id: container_id} = ammo_group
 | 
			
		||||
         %Pack{container_id: container_id} = pack
 | 
			
		||||
       ) do
 | 
			
		||||
    columns = [
 | 
			
		||||
      %{label: gettext("Rounds shot"), key: :count},
 | 
			
		||||
@@ -94,35 +93,35 @@ defmodule CanneryWeb.AmmoGroupLive.Show do
 | 
			
		||||
      %{label: gettext("Actions"), key: :actions, sortable: false}
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    shot_groups = ActivityLog.list_shot_groups_for_ammo_group(ammo_group, current_user)
 | 
			
		||||
    shot_records = ActivityLog.list_shot_records_for_pack(pack, current_user)
 | 
			
		||||
 | 
			
		||||
    rows =
 | 
			
		||||
      shot_groups
 | 
			
		||||
      |> Enum.map(fn shot_group ->
 | 
			
		||||
        ammo_group |> get_table_row_for_shot_group(shot_group, columns)
 | 
			
		||||
      shot_records
 | 
			
		||||
      |> Enum.map(fn shot_record ->
 | 
			
		||||
        pack |> get_table_row_for_shot_record(shot_record, columns)
 | 
			
		||||
      end)
 | 
			
		||||
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      ammo_group: ammo_group,
 | 
			
		||||
      original_count: Ammo.get_original_count(ammo_group, current_user),
 | 
			
		||||
      percentage_remaining: Ammo.get_percentage_remaining(ammo_group, current_user),
 | 
			
		||||
      pack: pack,
 | 
			
		||||
      original_count: Ammo.get_original_count(pack, current_user),
 | 
			
		||||
      percentage_remaining: Ammo.get_percentage_remaining(pack, current_user),
 | 
			
		||||
      container: container_id && Containers.get_container!(container_id, current_user),
 | 
			
		||||
      shot_groups: shot_groups,
 | 
			
		||||
      shot_records: shot_records,
 | 
			
		||||
      columns: columns,
 | 
			
		||||
      rows: rows
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp display_ammo_group(%{assigns: %{current_user: current_user}} = socket, id),
 | 
			
		||||
    do: display_ammo_group(socket, Ammo.get_ammo_group!(id, current_user))
 | 
			
		||||
  defp display_pack(%{assigns: %{current_user: current_user}} = socket, id),
 | 
			
		||||
    do: display_pack(socket, Ammo.get_pack!(id, current_user))
 | 
			
		||||
 | 
			
		||||
  @spec display_currency(float()) :: String.t()
 | 
			
		||||
  defp display_currency(float), do: :erlang.float_to_binary(float, decimals: 2)
 | 
			
		||||
 | 
			
		||||
  @spec get_table_row_for_shot_group(AmmoGroup.t(), ShotGroup.t(), [map()]) :: map()
 | 
			
		||||
  defp get_table_row_for_shot_group(ammo_group, %{id: id, date: date} = shot_group, columns) do
 | 
			
		||||
    assigns = %{ammo_group: ammo_group, shot_group: shot_group}
 | 
			
		||||
  @spec get_table_row_for_shot_record(Pack.t(), ShotRecord.t(), [map()]) :: map()
 | 
			
		||||
  defp get_table_row_for_shot_record(pack, %{id: id, date: date} = shot_record, columns) do
 | 
			
		||||
    assigns = %{pack: pack, shot_record: shot_record}
 | 
			
		||||
 | 
			
		||||
    columns
 | 
			
		||||
    |> Map.new(fn %{key: key} ->
 | 
			
		||||
@@ -140,11 +139,11 @@ defmodule CanneryWeb.AmmoGroupLive.Show do
 | 
			
		||||
            ~H"""
 | 
			
		||||
            <div class="px-4 py-2 space-x-4 flex justify-center items-center">
 | 
			
		||||
              <.link
 | 
			
		||||
                patch={Routes.ammo_group_show_path(Endpoint, :edit_shot_group, @ammo_group, @shot_group)}
 | 
			
		||||
                patch={Routes.pack_show_path(Endpoint, :edit_shot_record, @pack, @shot_record)}
 | 
			
		||||
                class="text-primary-600 link"
 | 
			
		||||
                aria-label={
 | 
			
		||||
                  dgettext("actions", "Edit shot group of %{shot_group_count} shots",
 | 
			
		||||
                    shot_group_count: @shot_group.count
 | 
			
		||||
                  dgettext("actions", "Edit shot record of %{shot_record_count} shots",
 | 
			
		||||
                    shot_record_count: @shot_record.count
 | 
			
		||||
                  )
 | 
			
		||||
                }
 | 
			
		||||
              >
 | 
			
		||||
@@ -154,12 +153,12 @@ defmodule CanneryWeb.AmmoGroupLive.Show do
 | 
			
		||||
              <.link
 | 
			
		||||
                href="#"
 | 
			
		||||
                class="text-primary-600 link"
 | 
			
		||||
                phx-click="delete_shot_group"
 | 
			
		||||
                phx-value-id={@shot_group.id}
 | 
			
		||||
                phx-click="delete_shot_record"
 | 
			
		||||
                phx-value-id={@shot_record.id}
 | 
			
		||||
                data-confirm={dgettext("prompts", "Are you sure you want to delete this shot record?")}
 | 
			
		||||
                aria-label={
 | 
			
		||||
                  dgettext("actions", "Delete shot record of %{shot_group_count} shots",
 | 
			
		||||
                    shot_group_count: @shot_group.count
 | 
			
		||||
                  dgettext("actions", "Delete shot record of %{shot_record_count} shots",
 | 
			
		||||
                    shot_record_count: @shot_record.count
 | 
			
		||||
                  )
 | 
			
		||||
                }
 | 
			
		||||
              >
 | 
			
		||||
@@ -169,7 +168,7 @@ defmodule CanneryWeb.AmmoGroupLive.Show do
 | 
			
		||||
            """
 | 
			
		||||
 | 
			
		||||
          key ->
 | 
			
		||||
            shot_group |> Map.get(key)
 | 
			
		||||
            shot_record |> Map.get(key)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
      {key, value}
 | 
			
		||||
@@ -1,12 +1,12 @@
 | 
			
		||||
<div class="mx-auto space-y-4 max-w-3xl flex flex-col justify-center items-center">
 | 
			
		||||
  <h1 class="title text-2xl title-primary-500">
 | 
			
		||||
    <%= @ammo_group.ammo_type.name %>
 | 
			
		||||
    <%= @pack.type.name %>
 | 
			
		||||
  </h1>
 | 
			
		||||
 | 
			
		||||
  <div class="space-y-2 flex flex-col justify-center items-center">
 | 
			
		||||
    <span class="rounded-lg title text-lg">
 | 
			
		||||
      <%= gettext("Count:") %>
 | 
			
		||||
      <%= @ammo_group.count %>
 | 
			
		||||
      <%= @pack.count %>
 | 
			
		||||
    </span>
 | 
			
		||||
 | 
			
		||||
    <span class="rounded-lg title text-lg">
 | 
			
		||||
@@ -19,28 +19,28 @@
 | 
			
		||||
      <%= gettext("%{percentage}%", percentage: @percentage_remaining) %>
 | 
			
		||||
    </span>
 | 
			
		||||
 | 
			
		||||
    <%= if @ammo_group.notes do %>
 | 
			
		||||
    <%= if @pack.notes do %>
 | 
			
		||||
      <span class="rounded-lg title text-lg">
 | 
			
		||||
        <%= gettext("Notes:") %>
 | 
			
		||||
        <%= @ammo_group.notes %>
 | 
			
		||||
        <%= @pack.notes %>
 | 
			
		||||
      </span>
 | 
			
		||||
    <% end %>
 | 
			
		||||
 | 
			
		||||
    <span class="rounded-lg title text-lg">
 | 
			
		||||
      <%= gettext("Purchased on:") %>
 | 
			
		||||
      <.date id={"#{@ammo_group.id}-purchased-on"} date={@ammo_group.purchased_on} />
 | 
			
		||||
      <.date id={"#{@pack.id}-purchased-on"} date={@pack.purchased_on} />
 | 
			
		||||
    </span>
 | 
			
		||||
 | 
			
		||||
    <%= if @ammo_group.price_paid do %>
 | 
			
		||||
    <%= if @pack.price_paid do %>
 | 
			
		||||
      <span class="rounded-lg title text-lg">
 | 
			
		||||
        <%= gettext("Original cost:") %>
 | 
			
		||||
        <%= gettext("$%{amount}", amount: display_currency(@ammo_group.price_paid)) %>
 | 
			
		||||
        <%= gettext("$%{amount}", amount: display_currency(@pack.price_paid)) %>
 | 
			
		||||
      </span>
 | 
			
		||||
 | 
			
		||||
      <span class="rounded-lg title text-lg">
 | 
			
		||||
        <%= gettext("Current value:") %>
 | 
			
		||||
        <%= gettext("$%{amount}",
 | 
			
		||||
          amount: display_currency(@ammo_group.price_paid * @percentage_remaining / 100)
 | 
			
		||||
          amount: display_currency(@pack.price_paid * @percentage_remaining / 100)
 | 
			
		||||
        ) %>
 | 
			
		||||
      </span>
 | 
			
		||||
    <% end %>
 | 
			
		||||
@@ -49,19 +49,17 @@
 | 
			
		||||
  <div class="flex flex-col justify-center items-center">
 | 
			
		||||
    <div class="flex flex-wrap justify-center items-center text-primary-600">
 | 
			
		||||
      <.link
 | 
			
		||||
        navigate={Routes.ammo_type_show_path(Endpoint, :show, @ammo_group.ammo_type)}
 | 
			
		||||
        navigate={Routes.type_show_path(Endpoint, :show, @pack.type)}
 | 
			
		||||
        class="mx-4 my-2 btn btn-primary"
 | 
			
		||||
      >
 | 
			
		||||
        <%= dgettext("actions", "View in Catalog") %>
 | 
			
		||||
      </.link>
 | 
			
		||||
 | 
			
		||||
      <.link
 | 
			
		||||
        patch={Routes.ammo_group_show_path(Endpoint, :edit, @ammo_group)}
 | 
			
		||||
        patch={Routes.pack_show_path(Endpoint, :edit, @pack)}
 | 
			
		||||
        class="mx-4 my-2 text-primary-600 link"
 | 
			
		||||
        aria-label={
 | 
			
		||||
          dgettext("actions", "Edit ammo group of %{ammo_group_count} bullets",
 | 
			
		||||
            ammo_group_count: @ammo_group.count
 | 
			
		||||
          )
 | 
			
		||||
          dgettext("actions", "Edit pack of %{pack_count} bullets", pack_count: @pack.count)
 | 
			
		||||
        }
 | 
			
		||||
      >
 | 
			
		||||
        <i class="fa-fw fa-lg fas fa-edit"></i>
 | 
			
		||||
@@ -73,9 +71,7 @@
 | 
			
		||||
        phx-click="delete"
 | 
			
		||||
        data-confirm={dgettext("prompts", "Are you sure you want to delete this ammo?")}
 | 
			
		||||
        aria-label={
 | 
			
		||||
          dgettext("actions", "Delete ammo group of %{ammo_group_count} bullets",
 | 
			
		||||
            ammo_group_count: @ammo_group.count
 | 
			
		||||
          )
 | 
			
		||||
          dgettext("actions", "Delete pack of %{pack_count} bullets", pack_count: @pack.count)
 | 
			
		||||
        }
 | 
			
		||||
      >
 | 
			
		||||
        <i class="fa-fw fa-lg fas fa-trash"></i>
 | 
			
		||||
@@ -84,20 +80,17 @@
 | 
			
		||||
 | 
			
		||||
    <div class="flex flex-wrap justify-center items-center text-primary-600">
 | 
			
		||||
      <button type="button" class="mx-4 my-2 btn btn-primary" phx-click="toggle_staged">
 | 
			
		||||
        <%= if @ammo_group.staged,
 | 
			
		||||
        <%= if @pack.staged,
 | 
			
		||||
          do: dgettext("actions", "Unstage from range"),
 | 
			
		||||
          else: dgettext("actions", "Stage for range") %>
 | 
			
		||||
      </button>
 | 
			
		||||
 | 
			
		||||
      <.link
 | 
			
		||||
        patch={Routes.ammo_group_show_path(Endpoint, :move, @ammo_group)}
 | 
			
		||||
        class="btn btn-primary"
 | 
			
		||||
      >
 | 
			
		||||
      <.link patch={Routes.pack_show_path(Endpoint, :move, @pack)} class="btn btn-primary">
 | 
			
		||||
        <%= dgettext("actions", "Move ammo") %>
 | 
			
		||||
      </.link>
 | 
			
		||||
 | 
			
		||||
      <.link
 | 
			
		||||
        patch={Routes.ammo_group_show_path(Endpoint, :add_shot_group, @ammo_group)}
 | 
			
		||||
        patch={Routes.pack_show_path(Endpoint, :add_shot_record, @pack)}
 | 
			
		||||
        class="mx-4 my-2 btn btn-primary"
 | 
			
		||||
      >
 | 
			
		||||
        <%= dgettext("actions", "Record shots") %>
 | 
			
		||||
@@ -119,7 +112,7 @@
 | 
			
		||||
    <% end %>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <%= unless @shot_groups |> Enum.empty?() do %>
 | 
			
		||||
  <%= unless @shot_records |> Enum.empty?() do %>
 | 
			
		||||
    <hr class="mb-4 w-full" />
 | 
			
		||||
 | 
			
		||||
    <h1 class="mb-4 px-4 py-2 text-center rounded-lg title text-xl">
 | 
			
		||||
@@ -128,7 +121,7 @@
 | 
			
		||||
 | 
			
		||||
    <.live_component
 | 
			
		||||
      module={CanneryWeb.Components.TableComponent}
 | 
			
		||||
      id="ammo_group_shot_groups_table"
 | 
			
		||||
      id="pack_shot_records_table"
 | 
			
		||||
      columns={@columns}
 | 
			
		||||
      rows={@rows}
 | 
			
		||||
    />
 | 
			
		||||
@@ -137,50 +130,50 @@
 | 
			
		||||
 | 
			
		||||
<%= case @live_action do %>
 | 
			
		||||
  <% :edit -> %>
 | 
			
		||||
    <.modal return_to={Routes.ammo_group_show_path(Endpoint, :show, @ammo_group)}>
 | 
			
		||||
    <.modal return_to={Routes.pack_show_path(Endpoint, :show, @pack)}>
 | 
			
		||||
      <.live_component
 | 
			
		||||
        module={CanneryWeb.AmmoGroupLive.FormComponent}
 | 
			
		||||
        id={@ammo_group.id}
 | 
			
		||||
        module={CanneryWeb.PackLive.FormComponent}
 | 
			
		||||
        id={@pack.id}
 | 
			
		||||
        title={@page_title}
 | 
			
		||||
        action={@live_action}
 | 
			
		||||
        ammo_group={@ammo_group}
 | 
			
		||||
        return_to={Routes.ammo_group_show_path(Endpoint, :show, @ammo_group)}
 | 
			
		||||
        pack={@pack}
 | 
			
		||||
        return_to={Routes.pack_show_path(Endpoint, :show, @pack)}
 | 
			
		||||
        current_user={@current_user}
 | 
			
		||||
      />
 | 
			
		||||
    </.modal>
 | 
			
		||||
  <% :edit_shot_group -> %>
 | 
			
		||||
    <.modal return_to={Routes.ammo_group_show_path(Endpoint, :show, @ammo_group)}>
 | 
			
		||||
  <% :edit_shot_record -> %>
 | 
			
		||||
    <.modal return_to={Routes.pack_show_path(Endpoint, :show, @pack)}>
 | 
			
		||||
      <.live_component
 | 
			
		||||
        module={CanneryWeb.RangeLive.FormComponent}
 | 
			
		||||
        id={@shot_group.id}
 | 
			
		||||
        id={@shot_record.id}
 | 
			
		||||
        title={@page_title}
 | 
			
		||||
        action={@live_action}
 | 
			
		||||
        shot_group={@shot_group}
 | 
			
		||||
        return_to={Routes.ammo_group_show_path(Endpoint, :show, @ammo_group)}
 | 
			
		||||
        shot_record={@shot_record}
 | 
			
		||||
        return_to={Routes.pack_show_path(Endpoint, :show, @pack)}
 | 
			
		||||
        current_user={@current_user}
 | 
			
		||||
      />
 | 
			
		||||
    </.modal>
 | 
			
		||||
  <% :add_shot_group -> %>
 | 
			
		||||
    <.modal return_to={Routes.ammo_group_show_path(Endpoint, :show, @ammo_group)}>
 | 
			
		||||
  <% :add_shot_record -> %>
 | 
			
		||||
    <.modal return_to={Routes.pack_show_path(Endpoint, :show, @pack)}>
 | 
			
		||||
      <.live_component
 | 
			
		||||
        module={CanneryWeb.Components.AddShotGroupComponent}
 | 
			
		||||
        module={CanneryWeb.Components.AddShotRecordComponent}
 | 
			
		||||
        id={:new}
 | 
			
		||||
        title={@page_title}
 | 
			
		||||
        action={@live_action}
 | 
			
		||||
        ammo_group={@ammo_group}
 | 
			
		||||
        return_to={Routes.ammo_group_show_path(Endpoint, :show, @ammo_group)}
 | 
			
		||||
        pack={@pack}
 | 
			
		||||
        return_to={Routes.pack_show_path(Endpoint, :show, @pack)}
 | 
			
		||||
        current_user={@current_user}
 | 
			
		||||
      />
 | 
			
		||||
    </.modal>
 | 
			
		||||
  <% :move -> %>
 | 
			
		||||
    <.modal return_to={Routes.ammo_group_show_path(Endpoint, :show, @ammo_group)}>
 | 
			
		||||
    <.modal return_to={Routes.pack_show_path(Endpoint, :show, @pack)}>
 | 
			
		||||
      <.live_component
 | 
			
		||||
        module={CanneryWeb.Components.MoveAmmoGroupComponent}
 | 
			
		||||
        id={@ammo_group.id}
 | 
			
		||||
        module={CanneryWeb.Components.MovePackComponent}
 | 
			
		||||
        id={@pack.id}
 | 
			
		||||
        title={@page_title}
 | 
			
		||||
        action={@live_action}
 | 
			
		||||
        ammo_group={@ammo_group}
 | 
			
		||||
        return_to={Routes.ammo_group_show_path(Endpoint, :show, @ammo_group)}
 | 
			
		||||
        pack={@pack}
 | 
			
		||||
        return_to={Routes.pack_show_path(Endpoint, :show, @pack)}
 | 
			
		||||
        current_user={@current_user}
 | 
			
		||||
      />
 | 
			
		||||
    </.modal>
 | 
			
		||||
@@ -1,56 +1,56 @@
 | 
			
		||||
defmodule CanneryWeb.RangeLive.FormComponent do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  Livecomponent that can update a ShotGroup
 | 
			
		||||
  Livecomponent that can update a ShotRecord
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb, :live_component
 | 
			
		||||
  alias Cannery.{Accounts.User, ActivityLog, ActivityLog.ShotGroup, Ammo, Ammo.AmmoGroup}
 | 
			
		||||
  alias Cannery.{Accounts.User, ActivityLog, ActivityLog.ShotRecord, Ammo, Ammo.Pack}
 | 
			
		||||
  alias Ecto.Changeset
 | 
			
		||||
  alias Phoenix.LiveView.Socket
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def mount(socket), do: {:ok, socket |> assign(:ammo_group, nil)}
 | 
			
		||||
  def mount(socket), do: {:ok, socket |> assign(:pack, nil)}
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  @spec update(
 | 
			
		||||
          %{
 | 
			
		||||
            required(:shot_group) => ShotGroup.t(),
 | 
			
		||||
            required(:shot_record) => ShotRecord.t(),
 | 
			
		||||
            required(:current_user) => User.t(),
 | 
			
		||||
            optional(:ammo_group) => AmmoGroup.t(),
 | 
			
		||||
            optional(:pack) => Pack.t(),
 | 
			
		||||
            optional(any()) => any()
 | 
			
		||||
          },
 | 
			
		||||
          Socket.t()
 | 
			
		||||
        ) :: {:ok, Socket.t()}
 | 
			
		||||
  def update(
 | 
			
		||||
        %{
 | 
			
		||||
          shot_group: %ShotGroup{ammo_group_id: ammo_group_id},
 | 
			
		||||
          shot_record: %ShotRecord{pack_id: pack_id},
 | 
			
		||||
          current_user: current_user
 | 
			
		||||
        } = assigns,
 | 
			
		||||
        socket
 | 
			
		||||
      )
 | 
			
		||||
      when is_binary(ammo_group_id) do
 | 
			
		||||
    ammo_group = Ammo.get_ammo_group!(ammo_group_id, current_user)
 | 
			
		||||
    {:ok, socket |> assign(assigns) |> assign(:ammo_group, ammo_group) |> assign_changeset(%{})}
 | 
			
		||||
      when is_binary(pack_id) do
 | 
			
		||||
    pack = Ammo.get_pack!(pack_id, current_user)
 | 
			
		||||
    {:ok, socket |> assign(assigns) |> assign(:pack, pack) |> assign_changeset(%{})}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def update(%{shot_group: %ShotGroup{}} = assigns, socket) do
 | 
			
		||||
  def update(%{shot_record: %ShotRecord{}} = assigns, socket) do
 | 
			
		||||
    {:ok, socket |> assign(assigns) |> assign_changeset(%{})}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_event("validate", %{"shot_group" => shot_group_params}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign_changeset(shot_group_params, :validate)}
 | 
			
		||||
  def handle_event("validate", %{"shot_record" => shot_record_params}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign_changeset(shot_record_params, :validate)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event(
 | 
			
		||||
        "save",
 | 
			
		||||
        %{"shot_group" => shot_group_params},
 | 
			
		||||
        %{assigns: %{shot_group: shot_group, current_user: current_user, return_to: return_to}} =
 | 
			
		||||
        %{"shot_record" => shot_record_params},
 | 
			
		||||
        %{assigns: %{shot_record: shot_record, current_user: current_user, return_to: return_to}} =
 | 
			
		||||
          socket
 | 
			
		||||
      ) do
 | 
			
		||||
    socket =
 | 
			
		||||
      case ActivityLog.update_shot_group(shot_group, shot_group_params, current_user) do
 | 
			
		||||
        {:ok, _shot_group} ->
 | 
			
		||||
      case ActivityLog.update_shot_record(shot_record, shot_record_params, current_user) do
 | 
			
		||||
        {:ok, _shot_record} ->
 | 
			
		||||
          prompt = dgettext("prompts", "Shot records updated successfully")
 | 
			
		||||
          socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
 | 
			
		||||
 | 
			
		||||
@@ -66,23 +66,23 @@ defmodule CanneryWeb.RangeLive.FormComponent do
 | 
			
		||||
           assigns: %{
 | 
			
		||||
             action: live_action,
 | 
			
		||||
             current_user: user,
 | 
			
		||||
             ammo_group: ammo_group,
 | 
			
		||||
             shot_group: shot_group
 | 
			
		||||
             pack: pack,
 | 
			
		||||
             shot_record: shot_record
 | 
			
		||||
           }
 | 
			
		||||
         } = socket,
 | 
			
		||||
         shot_group_params,
 | 
			
		||||
         shot_record_params,
 | 
			
		||||
         action \\ nil
 | 
			
		||||
       ) do
 | 
			
		||||
    default_action =
 | 
			
		||||
      case live_action do
 | 
			
		||||
        :add_shot_group -> :insert
 | 
			
		||||
        editing when editing in [:edit, :edit_shot_group] -> :update
 | 
			
		||||
        :add_shot_record -> :insert
 | 
			
		||||
        editing when editing in [:edit, :edit_shot_record] -> :update
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
    changeset =
 | 
			
		||||
      case default_action do
 | 
			
		||||
        :insert -> shot_group |> ShotGroup.create_changeset(user, ammo_group, shot_group_params)
 | 
			
		||||
        :update -> shot_group |> ShotGroup.update_changeset(user, shot_group_params)
 | 
			
		||||
        :insert -> shot_record |> ShotRecord.create_changeset(user, pack, shot_record_params)
 | 
			
		||||
        :update -> shot_record |> ShotRecord.update_changeset(user, shot_record_params)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
    changeset =
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
  <.form
 | 
			
		||||
    :let={f}
 | 
			
		||||
    for={@changeset}
 | 
			
		||||
    id="shot-group-form"
 | 
			
		||||
    id="shot-record-form"
 | 
			
		||||
    class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
 | 
			
		||||
    phx-target={@myself}
 | 
			
		||||
    phx-change="validate"
 | 
			
		||||
@@ -22,14 +22,14 @@
 | 
			
		||||
    <%= label(f, :count, gettext("Shots fired"), class: "title text-lg text-primary-600") %>
 | 
			
		||||
    <%= number_input(f, :count,
 | 
			
		||||
      min: 1,
 | 
			
		||||
      max: @shot_group.count + @ammo_group.count,
 | 
			
		||||
      max: @shot_record.count + @pack.count,
 | 
			
		||||
      class: "input input-primary col-span-2"
 | 
			
		||||
    ) %>
 | 
			
		||||
    <%= error_tag(f, :count, "col-span-3") %>
 | 
			
		||||
 | 
			
		||||
    <%= label(f, :notes, gettext("Notes"), class: "title text-lg text-primary-600") %>
 | 
			
		||||
    <%= textarea(f, :notes,
 | 
			
		||||
      id: "shot-group-form-notes",
 | 
			
		||||
      id: "shot-record-form-notes",
 | 
			
		||||
      class: "input input-primary col-span-2",
 | 
			
		||||
      maxlength: 255,
 | 
			
		||||
      placeholder: gettext("Really great weather"),
 | 
			
		||||
 
 | 
			
		||||
@@ -1,20 +1,20 @@
 | 
			
		||||
defmodule CanneryWeb.RangeLive.Index do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  Main page for range day mode, where `AmmoGroup`s can be used up.
 | 
			
		||||
  Main page for range day mode, where `Pack`s can be used up.
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb, :live_view
 | 
			
		||||
  alias Cannery.{ActivityLog, ActivityLog.ShotGroup, Ammo}
 | 
			
		||||
  alias Cannery.{ActivityLog, ActivityLog.ShotRecord, Ammo}
 | 
			
		||||
  alias CanneryWeb.Endpoint
 | 
			
		||||
  alias Phoenix.LiveView.Socket
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def mount(%{"search" => search}, _session, socket) do
 | 
			
		||||
    {:ok, socket |> assign(type: :all, search: search) |> display_shot_groups()}
 | 
			
		||||
    {:ok, socket |> assign(class: :all, search: search) |> display_shot_records()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def mount(_params, _session, socket) do
 | 
			
		||||
    {:ok, socket |> assign(type: :all, search: nil) |> display_shot_groups()}
 | 
			
		||||
    {:ok, socket |> assign(class: :all, search: nil) |> display_shot_records()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
@@ -24,21 +24,21 @@ defmodule CanneryWeb.RangeLive.Index do
 | 
			
		||||
 | 
			
		||||
  defp apply_action(
 | 
			
		||||
         %{assigns: %{current_user: current_user}} = socket,
 | 
			
		||||
         :add_shot_group,
 | 
			
		||||
         :add_shot_record,
 | 
			
		||||
         %{"id" => id}
 | 
			
		||||
       ) do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("Record Shots"),
 | 
			
		||||
      ammo_group: Ammo.get_ammo_group!(id, current_user)
 | 
			
		||||
      pack: Ammo.get_pack!(id, current_user)
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit, %{"id" => id}) do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("Edit Shot Records"),
 | 
			
		||||
      shot_group: ActivityLog.get_shot_group!(id, current_user)
 | 
			
		||||
      page_title: gettext("Edit Shot Record"),
 | 
			
		||||
      shot_record: ActivityLog.get_shot_record!(id, current_user)
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -46,7 +46,7 @@ defmodule CanneryWeb.RangeLive.Index do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("New Shot Records"),
 | 
			
		||||
      shot_group: %ShotGroup{}
 | 
			
		||||
      shot_record: %ShotRecord{}
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -55,9 +55,9 @@ defmodule CanneryWeb.RangeLive.Index do
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("Shot Records"),
 | 
			
		||||
      search: nil,
 | 
			
		||||
      shot_group: nil
 | 
			
		||||
      shot_record: nil
 | 
			
		||||
    )
 | 
			
		||||
    |> display_shot_groups()
 | 
			
		||||
    |> display_shot_records()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp apply_action(socket, :search, %{"search" => search}) do
 | 
			
		||||
@@ -65,33 +65,32 @@ defmodule CanneryWeb.RangeLive.Index do
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("Shot Records"),
 | 
			
		||||
      search: search,
 | 
			
		||||
      shot_group: nil
 | 
			
		||||
      shot_record: nil
 | 
			
		||||
    )
 | 
			
		||||
    |> display_shot_groups()
 | 
			
		||||
    |> display_shot_records()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_event("delete", %{"id" => id}, %{assigns: %{current_user: current_user}} = socket) do
 | 
			
		||||
    {:ok, _} =
 | 
			
		||||
      ActivityLog.get_shot_group!(id, current_user)
 | 
			
		||||
      |> ActivityLog.delete_shot_group(current_user)
 | 
			
		||||
      ActivityLog.get_shot_record!(id, current_user)
 | 
			
		||||
      |> ActivityLog.delete_shot_record(current_user)
 | 
			
		||||
 | 
			
		||||
    prompt = dgettext("prompts", "Shot records deleted succesfully")
 | 
			
		||||
    {:noreply, socket |> put_flash(:info, prompt) |> display_shot_groups()}
 | 
			
		||||
    {:noreply, socket |> put_flash(:info, prompt) |> display_shot_records()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event(
 | 
			
		||||
        "toggle_staged",
 | 
			
		||||
        %{"ammo_group_id" => ammo_group_id},
 | 
			
		||||
        %{"pack_id" => pack_id},
 | 
			
		||||
        %{assigns: %{current_user: current_user}} = socket
 | 
			
		||||
      ) do
 | 
			
		||||
    ammo_group = Ammo.get_ammo_group!(ammo_group_id, current_user)
 | 
			
		||||
    pack = Ammo.get_pack!(pack_id, current_user)
 | 
			
		||||
 | 
			
		||||
    {:ok, _ammo_group} =
 | 
			
		||||
      ammo_group |> Ammo.update_ammo_group(%{"staged" => !ammo_group.staged}, current_user)
 | 
			
		||||
    {:ok, _pack} = pack |> Ammo.update_pack(%{"staged" => !pack.staged}, current_user)
 | 
			
		||||
 | 
			
		||||
    prompt = dgettext("prompts", "Ammo unstaged succesfully")
 | 
			
		||||
    {:noreply, socket |> put_flash(:info, prompt) |> display_shot_groups()}
 | 
			
		||||
    {:noreply, socket |> put_flash(:info, prompt) |> display_shot_records()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("search", %{"search" => %{"search_term" => ""}}, socket) do
 | 
			
		||||
@@ -102,47 +101,49 @@ defmodule CanneryWeb.RangeLive.Index do
 | 
			
		||||
    {:noreply, socket |> push_patch(to: Routes.range_index_path(Endpoint, :search, search_term))}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_type", %{"ammo_type" => %{"type" => "rifle"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:type, :rifle) |> display_shot_groups()}
 | 
			
		||||
  def handle_event("change_class", %{"type" => %{"class" => "rifle"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:class, :rifle) |> display_shot_records()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_type", %{"ammo_type" => %{"type" => "shotgun"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:type, :shotgun) |> display_shot_groups()}
 | 
			
		||||
  def handle_event("change_class", %{"type" => %{"class" => "shotgun"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:class, :shotgun) |> display_shot_records()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_type", %{"ammo_type" => %{"type" => "pistol"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:type, :pistol) |> display_shot_groups()}
 | 
			
		||||
  def handle_event("change_class", %{"type" => %{"class" => "pistol"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:class, :pistol) |> display_shot_records()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_type", %{"ammo_type" => %{"type" => _all}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:type, :all) |> display_shot_groups()}
 | 
			
		||||
  def handle_event("change_class", %{"type" => %{"class" => _all}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:class, :all) |> display_shot_records()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @spec display_shot_groups(Socket.t()) :: Socket.t()
 | 
			
		||||
  defp display_shot_groups(
 | 
			
		||||
         %{assigns: %{type: type, search: search, current_user: current_user}} = socket
 | 
			
		||||
  @spec display_shot_records(Socket.t()) :: Socket.t()
 | 
			
		||||
  defp display_shot_records(
 | 
			
		||||
         %{assigns: %{class: class, search: search, current_user: current_user}} = socket
 | 
			
		||||
       ) do
 | 
			
		||||
    shot_groups = ActivityLog.list_shot_groups(search, type, current_user)
 | 
			
		||||
    ammo_groups = Ammo.list_staged_ammo_groups(current_user)
 | 
			
		||||
    chart_data = shot_groups |> get_chart_data_for_shot_group()
 | 
			
		||||
    original_counts = ammo_groups |> Ammo.get_original_counts(current_user)
 | 
			
		||||
    cprs = ammo_groups |> Ammo.get_cprs(current_user)
 | 
			
		||||
    last_used_dates = ammo_groups |> ActivityLog.get_last_used_dates(current_user)
 | 
			
		||||
    shot_records = ActivityLog.list_shot_records(search, class, current_user)
 | 
			
		||||
    packs = Ammo.list_staged_packs(current_user)
 | 
			
		||||
    chart_data = shot_records |> get_chart_data_for_shot_record()
 | 
			
		||||
    original_counts = packs |> Ammo.get_original_counts(current_user)
 | 
			
		||||
    cprs = packs |> Ammo.get_cprs(current_user)
 | 
			
		||||
    last_used_dates = packs |> ActivityLog.get_last_used_dates(current_user)
 | 
			
		||||
    shot_record_count = ActivityLog.get_shot_record_count!(current_user)
 | 
			
		||||
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      ammo_groups: ammo_groups,
 | 
			
		||||
      packs: packs,
 | 
			
		||||
      original_counts: original_counts,
 | 
			
		||||
      cprs: cprs,
 | 
			
		||||
      last_used_dates: last_used_dates,
 | 
			
		||||
      chart_data: chart_data,
 | 
			
		||||
      shot_groups: shot_groups
 | 
			
		||||
      shot_records: shot_records,
 | 
			
		||||
      shot_record_count: shot_record_count
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @spec get_chart_data_for_shot_group([ShotGroup.t()]) :: [map()]
 | 
			
		||||
  defp get_chart_data_for_shot_group(shot_groups) do
 | 
			
		||||
    shot_groups
 | 
			
		||||
  @spec get_chart_data_for_shot_record([ShotRecord.t()]) :: [map()]
 | 
			
		||||
  defp get_chart_data_for_shot_record(shot_records) do
 | 
			
		||||
    shot_records
 | 
			
		||||
    |> Enum.group_by(fn %{date: date} -> date end, fn %{count: count} -> count end)
 | 
			
		||||
    |> Enum.map(fn {date, rounds} ->
 | 
			
		||||
      sum = Enum.sum(rounds)
 | 
			
		||||
 
 | 
			
		||||
@@ -3,54 +3,54 @@
 | 
			
		||||
    <%= gettext("Range day") %>
 | 
			
		||||
  </h1>
 | 
			
		||||
 | 
			
		||||
  <%= if @ammo_groups |> Enum.empty?() do %>
 | 
			
		||||
  <%= if @packs |> Enum.empty?() do %>
 | 
			
		||||
    <h1 class="title text-xl text-primary-600">
 | 
			
		||||
      <%= gettext("No ammo staged") %>
 | 
			
		||||
      <%= display_emoji("😔") %>
 | 
			
		||||
    </h1>
 | 
			
		||||
 | 
			
		||||
    <.link navigate={Routes.ammo_group_index_path(Endpoint, :index)} class="btn btn-primary">
 | 
			
		||||
    <.link navigate={Routes.pack_index_path(Endpoint, :index)} class="btn btn-primary">
 | 
			
		||||
      <%= dgettext("actions", "Why not get some ready to shoot?") %>
 | 
			
		||||
    </.link>
 | 
			
		||||
  <% else %>
 | 
			
		||||
    <.link navigate={Routes.ammo_group_index_path(Endpoint, :index)} class="btn btn-primary">
 | 
			
		||||
    <.link navigate={Routes.pack_index_path(Endpoint, :index)} class="btn btn-primary">
 | 
			
		||||
      <%= dgettext("actions", "Stage ammo") %>
 | 
			
		||||
    </.link>
 | 
			
		||||
 | 
			
		||||
    <div class="w-full flex flex-row flex-wrap justify-center items-stretch">
 | 
			
		||||
      <.ammo_group_card
 | 
			
		||||
        :for={%{id: ammo_group_id} = ammo_group <- @ammo_groups}
 | 
			
		||||
        ammo_group={ammo_group}
 | 
			
		||||
        original_count={Map.fetch!(@original_counts, ammo_group_id)}
 | 
			
		||||
        cpr={Map.get(@cprs, ammo_group_id)}
 | 
			
		||||
        last_used_date={Map.get(@last_used_dates, ammo_group_id)}
 | 
			
		||||
      <.pack_card
 | 
			
		||||
        :for={%{id: pack_id} = pack <- @packs}
 | 
			
		||||
        pack={pack}
 | 
			
		||||
        original_count={Map.fetch!(@original_counts, pack_id)}
 | 
			
		||||
        cpr={Map.get(@cprs, pack_id)}
 | 
			
		||||
        last_used_date={Map.get(@last_used_dates, pack_id)}
 | 
			
		||||
        current_user={@current_user}
 | 
			
		||||
      >
 | 
			
		||||
        <button
 | 
			
		||||
          type="button"
 | 
			
		||||
          class="btn btn-primary"
 | 
			
		||||
          phx-click="toggle_staged"
 | 
			
		||||
          phx-value-ammo_group_id={ammo_group.id}
 | 
			
		||||
          phx-value-pack_id={pack.id}
 | 
			
		||||
          data-confirm={"#{dgettext("prompts", "Are you sure you want to unstage this ammo?")}"}
 | 
			
		||||
        >
 | 
			
		||||
          <%= if ammo_group.staged,
 | 
			
		||||
          <%= if pack.staged,
 | 
			
		||||
            do: dgettext("actions", "Unstage from range"),
 | 
			
		||||
            else: dgettext("actions", "Stage for range") %>
 | 
			
		||||
        </button>
 | 
			
		||||
 | 
			
		||||
        <.link
 | 
			
		||||
          patch={Routes.range_index_path(Endpoint, :add_shot_group, ammo_group)}
 | 
			
		||||
          patch={Routes.range_index_path(Endpoint, :add_shot_record, pack)}
 | 
			
		||||
          class="btn btn-primary"
 | 
			
		||||
        >
 | 
			
		||||
          <%= dgettext("actions", "Record shots") %>
 | 
			
		||||
        </.link>
 | 
			
		||||
      </.ammo_group_card>
 | 
			
		||||
      </.pack_card>
 | 
			
		||||
    </div>
 | 
			
		||||
  <% end %>
 | 
			
		||||
 | 
			
		||||
  <hr class="hr" />
 | 
			
		||||
 | 
			
		||||
  <%= if @shot_groups |> Enum.empty?() and @search |> is_nil() do %>
 | 
			
		||||
  <%= if @shot_record_count == 0 do %>
 | 
			
		||||
    <h1 class="title text-xl text-primary-600">
 | 
			
		||||
      <%= gettext("No shots recorded") %>
 | 
			
		||||
      <%= display_emoji("😔") %>
 | 
			
		||||
@@ -78,16 +78,16 @@
 | 
			
		||||
      <.form
 | 
			
		||||
        :let={f}
 | 
			
		||||
        for={%{}}
 | 
			
		||||
        as={:ammo_type}
 | 
			
		||||
        phx-change="change_type"
 | 
			
		||||
        phx-submit="change_type"
 | 
			
		||||
        as={:type}
 | 
			
		||||
        phx-change="change_class"
 | 
			
		||||
        phx-submit="change_class"
 | 
			
		||||
        class="flex items-center"
 | 
			
		||||
      >
 | 
			
		||||
        <%= label(f, :type, gettext("Type"), class: "title text-primary-600 text-lg text-center") %>
 | 
			
		||||
        <%= label(f, :class, gettext("Class"), class: "title text-primary-600 text-lg text-center") %>
 | 
			
		||||
 | 
			
		||||
        <%= select(
 | 
			
		||||
          f,
 | 
			
		||||
          :type,
 | 
			
		||||
          :class,
 | 
			
		||||
          [
 | 
			
		||||
            {gettext("All"), :all},
 | 
			
		||||
            {gettext("Rifle"), :rifle},
 | 
			
		||||
@@ -95,7 +95,7 @@
 | 
			
		||||
            {gettext("Pistol"), :pistol}
 | 
			
		||||
          ],
 | 
			
		||||
          class: "mx-2 my-1 min-w-md input input-primary",
 | 
			
		||||
          value: @type
 | 
			
		||||
          value: @class
 | 
			
		||||
        ) %>
 | 
			
		||||
      </.form>
 | 
			
		||||
 | 
			
		||||
@@ -117,26 +117,26 @@
 | 
			
		||||
      </.form>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <%= if @shot_groups |> Enum.empty?() do %>
 | 
			
		||||
    <%= if @shot_records |> Enum.empty?() do %>
 | 
			
		||||
      <h1 class="title text-xl text-primary-600">
 | 
			
		||||
        <%= gettext("No shots recorded") %>
 | 
			
		||||
        <%= display_emoji("😔") %>
 | 
			
		||||
      </h1>
 | 
			
		||||
    <% else %>
 | 
			
		||||
      <.live_component
 | 
			
		||||
        module={CanneryWeb.Components.ShotGroupTableComponent}
 | 
			
		||||
        id="shot_groups_index_table"
 | 
			
		||||
        shot_groups={@shot_groups}
 | 
			
		||||
        module={CanneryWeb.Components.ShotRecordTableComponent}
 | 
			
		||||
        id="shot_records_index_table"
 | 
			
		||||
        shot_records={@shot_records}
 | 
			
		||||
        current_user={@current_user}
 | 
			
		||||
      >
 | 
			
		||||
        <:actions :let={shot_group}>
 | 
			
		||||
        <:actions :let={shot_record}>
 | 
			
		||||
          <div class="px-4 py-2 space-x-4 flex justify-center items-center">
 | 
			
		||||
            <.link
 | 
			
		||||
              patch={Routes.range_index_path(Endpoint, :edit, shot_group)}
 | 
			
		||||
              patch={Routes.range_index_path(Endpoint, :edit, shot_record)}
 | 
			
		||||
              class="text-primary-600 link"
 | 
			
		||||
              aria-label={
 | 
			
		||||
                dgettext("actions", "Edit shot record of %{shot_group_count} shots",
 | 
			
		||||
                  shot_group_count: shot_group.count
 | 
			
		||||
                dgettext("actions", "Edit shot record of %{shot_record_count} shots",
 | 
			
		||||
                  shot_record_count: shot_record.count
 | 
			
		||||
                )
 | 
			
		||||
              }
 | 
			
		||||
            >
 | 
			
		||||
@@ -147,13 +147,13 @@
 | 
			
		||||
              href="#"
 | 
			
		||||
              class="text-primary-600 link"
 | 
			
		||||
              phx-click="delete"
 | 
			
		||||
              phx-value-id={shot_group.id}
 | 
			
		||||
              phx-value-id={shot_record.id}
 | 
			
		||||
              data-confirm={
 | 
			
		||||
                dgettext("prompts", "Are you sure you want to delete this shot record?")
 | 
			
		||||
              }
 | 
			
		||||
              aria-label={
 | 
			
		||||
                dgettext("actions", "Delete shot record of %{shot_group_count} shots",
 | 
			
		||||
                  shot_group_count: shot_group.count
 | 
			
		||||
                dgettext("actions", "Delete shot record of %{shot_record_count} shots",
 | 
			
		||||
                  shot_record_count: shot_record.count
 | 
			
		||||
                )
 | 
			
		||||
              }
 | 
			
		||||
            >
 | 
			
		||||
@@ -171,22 +171,22 @@
 | 
			
		||||
    <.modal return_to={Routes.range_index_path(Endpoint, :index)}>
 | 
			
		||||
      <.live_component
 | 
			
		||||
        module={CanneryWeb.RangeLive.FormComponent}
 | 
			
		||||
        id={@shot_group.id}
 | 
			
		||||
        id={@shot_record.id}
 | 
			
		||||
        title={@page_title}
 | 
			
		||||
        action={@live_action}
 | 
			
		||||
        shot_group={@shot_group}
 | 
			
		||||
        shot_record={@shot_record}
 | 
			
		||||
        return_to={Routes.range_index_path(Endpoint, :index)}
 | 
			
		||||
        current_user={@current_user}
 | 
			
		||||
      />
 | 
			
		||||
    </.modal>
 | 
			
		||||
  <% :add_shot_group -> %>
 | 
			
		||||
  <% :add_shot_record -> %>
 | 
			
		||||
    <.modal return_to={Routes.range_index_path(Endpoint, :index)}>
 | 
			
		||||
      <.live_component
 | 
			
		||||
        module={CanneryWeb.Components.AddShotGroupComponent}
 | 
			
		||||
        module={CanneryWeb.Components.AddShotRecordComponent}
 | 
			
		||||
        id={:new}
 | 
			
		||||
        title={@page_title}
 | 
			
		||||
        action={@live_action}
 | 
			
		||||
        ammo_group={@ammo_group}
 | 
			
		||||
        pack={@pack}
 | 
			
		||||
        return_to={Routes.range_index_path(Endpoint, :index)}
 | 
			
		||||
        current_user={@current_user}
 | 
			
		||||
      />
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,16 @@
 | 
			
		||||
defmodule CanneryWeb.AmmoTypeLive.FormComponent do
 | 
			
		||||
defmodule CanneryWeb.TypeLive.FormComponent do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  Livecomponent that can update or create an Cannery.Ammo.AmmoType
 | 
			
		||||
  Livecomponent that can update or create an Cannery.Ammo.Type
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb, :live_component
 | 
			
		||||
  alias Cannery.{Accounts.User, Ammo, Ammo.AmmoType}
 | 
			
		||||
  alias Cannery.{Accounts.User, Ammo, Ammo.Type}
 | 
			
		||||
  alias Ecto.Changeset
 | 
			
		||||
  alias Phoenix.LiveView.Socket
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  @spec update(
 | 
			
		||||
          %{:ammo_type => AmmoType.t(), :current_user => User.t(), optional(any) => any},
 | 
			
		||||
          %{:type => Type.t(), :current_user => User.t(), optional(any) => any},
 | 
			
		||||
          Socket.t()
 | 
			
		||||
        ) :: {:ok, Socket.t()}
 | 
			
		||||
  def update(%{current_user: _current_user} = assigns, socket) do
 | 
			
		||||
@@ -18,21 +18,21 @@ defmodule CanneryWeb.AmmoTypeLive.FormComponent do
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_event("validate", %{"ammo_type" => ammo_type_params}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign_changeset(ammo_type_params)}
 | 
			
		||||
  def handle_event("validate", %{"type" => type_params}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign_changeset(type_params)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event(
 | 
			
		||||
        "save",
 | 
			
		||||
        %{"ammo_type" => ammo_type_params},
 | 
			
		||||
        %{"type" => type_params},
 | 
			
		||||
        %{assigns: %{action: action}} = socket
 | 
			
		||||
      ) do
 | 
			
		||||
    save_ammo_type(socket, action, ammo_type_params)
 | 
			
		||||
    save_type(socket, action, type_params)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp assign_changeset(
 | 
			
		||||
         %{assigns: %{action: action, ammo_type: ammo_type, current_user: user}} = socket,
 | 
			
		||||
         ammo_type_params
 | 
			
		||||
         %{assigns: %{action: action, type: type, current_user: user}} = socket,
 | 
			
		||||
         type_params
 | 
			
		||||
       ) do
 | 
			
		||||
    changeset_action =
 | 
			
		||||
      case action do
 | 
			
		||||
@@ -43,10 +43,10 @@ defmodule CanneryWeb.AmmoTypeLive.FormComponent do
 | 
			
		||||
    changeset =
 | 
			
		||||
      case action do
 | 
			
		||||
        create when create in [:new, :clone] ->
 | 
			
		||||
          ammo_type |> AmmoType.create_changeset(user, ammo_type_params)
 | 
			
		||||
          type |> Type.create_changeset(user, type_params)
 | 
			
		||||
 | 
			
		||||
        :edit ->
 | 
			
		||||
          ammo_type |> AmmoType.update_changeset(ammo_type_params)
 | 
			
		||||
          type |> Type.update_changeset(type_params)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
    changeset =
 | 
			
		||||
@@ -58,16 +58,15 @@ defmodule CanneryWeb.AmmoTypeLive.FormComponent do
 | 
			
		||||
    socket |> assign(changeset: changeset)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp save_ammo_type(
 | 
			
		||||
         %{assigns: %{ammo_type: ammo_type, current_user: current_user, return_to: return_to}} =
 | 
			
		||||
           socket,
 | 
			
		||||
  defp save_type(
 | 
			
		||||
         %{assigns: %{type: type, current_user: current_user, return_to: return_to}} = socket,
 | 
			
		||||
         :edit,
 | 
			
		||||
         ammo_type_params
 | 
			
		||||
         type_params
 | 
			
		||||
       ) do
 | 
			
		||||
    socket =
 | 
			
		||||
      case Ammo.update_ammo_type(ammo_type, ammo_type_params, current_user) do
 | 
			
		||||
        {:ok, %{name: ammo_type_name}} ->
 | 
			
		||||
          prompt = dgettext("prompts", "%{name} updated successfully", name: ammo_type_name)
 | 
			
		||||
      case Ammo.update_type(type, type_params, current_user) do
 | 
			
		||||
        {:ok, %{name: type_name}} ->
 | 
			
		||||
          prompt = dgettext("prompts", "%{name} updated successfully", name: type_name)
 | 
			
		||||
          socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
 | 
			
		||||
 | 
			
		||||
        {:error, %Changeset{} = changeset} ->
 | 
			
		||||
@@ -77,16 +76,16 @@ defmodule CanneryWeb.AmmoTypeLive.FormComponent do
 | 
			
		||||
    {:noreply, socket}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp save_ammo_type(
 | 
			
		||||
  defp save_type(
 | 
			
		||||
         %{assigns: %{current_user: current_user, return_to: return_to}} = socket,
 | 
			
		||||
         action,
 | 
			
		||||
         ammo_type_params
 | 
			
		||||
         type_params
 | 
			
		||||
       )
 | 
			
		||||
       when action in [:new, :clone] do
 | 
			
		||||
    socket =
 | 
			
		||||
      case Ammo.create_ammo_type(ammo_type_params, current_user) do
 | 
			
		||||
        {:ok, %{name: ammo_type_name}} ->
 | 
			
		||||
          prompt = dgettext("prompts", "%{name} created successfully", name: ammo_type_name)
 | 
			
		||||
      case Ammo.create_type(type_params, current_user) do
 | 
			
		||||
        {:ok, %{name: type_name}} ->
 | 
			
		||||
          prompt = dgettext("prompts", "%{name} created successfully", name: type_name)
 | 
			
		||||
          socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
 | 
			
		||||
 | 
			
		||||
        {:error, %Changeset{} = changeset} ->
 | 
			
		||||
@@ -5,7 +5,7 @@
 | 
			
		||||
  <.form
 | 
			
		||||
    :let={f}
 | 
			
		||||
    for={@changeset}
 | 
			
		||||
    id="ammo_type-form"
 | 
			
		||||
    id="type-form"
 | 
			
		||||
    phx-target={@myself}
 | 
			
		||||
    phx-change="validate"
 | 
			
		||||
    phx-submit="save"
 | 
			
		||||
@@ -18,15 +18,15 @@
 | 
			
		||||
      <%= dgettext("errors", "Oops, something went wrong! Please check the errors below.") %>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <%= label(f, :type, gettext("Type"), class: "title text-lg text-primary-600") %>
 | 
			
		||||
    <%= label(f, :class, gettext("Class"), class: "title text-lg text-primary-600") %>
 | 
			
		||||
    <%= select(
 | 
			
		||||
      f,
 | 
			
		||||
      :type,
 | 
			
		||||
      :class,
 | 
			
		||||
      [{gettext("Rifle"), :rifle}, {gettext("Shotgun"), :shotgun}, {gettext("Pistol"), :pistol}],
 | 
			
		||||
      class: "text-center col-span-2 input input-primary",
 | 
			
		||||
      maxlength: 255
 | 
			
		||||
    ) %>
 | 
			
		||||
    <%= error_tag(f, :type, "col-span-3 text-center") %>
 | 
			
		||||
    <%= error_tag(f, :class, "col-span-3 text-center") %>
 | 
			
		||||
 | 
			
		||||
    <%= label(f, :name, gettext("Name"), class: "title text-lg text-primary-600") %>
 | 
			
		||||
    <%= text_input(f, :name,
 | 
			
		||||
@@ -37,7 +37,7 @@
 | 
			
		||||
 | 
			
		||||
    <%= label(f, :desc, gettext("Description"), class: "title text-lg text-primary-600") %>
 | 
			
		||||
    <%= textarea(f, :desc,
 | 
			
		||||
      id: "ammo-type-form-desc",
 | 
			
		||||
      id: "type-form-desc",
 | 
			
		||||
      class: "text-center col-span-2 input input-primary",
 | 
			
		||||
      phx_hook: "MaintainAttrs",
 | 
			
		||||
      phx_update: "ignore"
 | 
			
		||||
@@ -48,7 +48,7 @@
 | 
			
		||||
      <%= gettext("Dimensions") %>
 | 
			
		||||
    </h2>
 | 
			
		||||
 | 
			
		||||
    <%= if Changeset.get_field(@changeset, :type) in [:rifle, :pistol] do %>
 | 
			
		||||
    <%= if Changeset.get_field(@changeset, :class) in [:rifle, :pistol] do %>
 | 
			
		||||
      <%= label(f, :cartridge, gettext("Cartridge"), class: "title text-lg text-primary-600") %>
 | 
			
		||||
      <%= text_input(f, :cartridge,
 | 
			
		||||
        class: "text-center col-span-2 input input-primary",
 | 
			
		||||
@@ -63,7 +63,7 @@
 | 
			
		||||
    <%= label(
 | 
			
		||||
      f,
 | 
			
		||||
      :caliber,
 | 
			
		||||
      if(Changeset.get_field(@changeset, :type) == :shotgun,
 | 
			
		||||
      if(Changeset.get_field(@changeset, :class) == :shotgun,
 | 
			
		||||
        do: gettext("Gauge"),
 | 
			
		||||
        else: gettext("Caliber")
 | 
			
		||||
      ),
 | 
			
		||||
@@ -76,7 +76,7 @@
 | 
			
		||||
    ) %>
 | 
			
		||||
    <%= error_tag(f, :caliber, "col-span-3 text-center") %>
 | 
			
		||||
 | 
			
		||||
    <%= if Changeset.get_field(@changeset, :type) == :shotgun do %>
 | 
			
		||||
    <%= if Changeset.get_field(@changeset, :class) == :shotgun do %>
 | 
			
		||||
      <%= label(f, :unfired_length, gettext("Unfired shell length"),
 | 
			
		||||
        class: "title text-lg text-primary-600"
 | 
			
		||||
      ) %>
 | 
			
		||||
@@ -139,7 +139,7 @@
 | 
			
		||||
    <%= label(
 | 
			
		||||
      f,
 | 
			
		||||
      :bullet_core,
 | 
			
		||||
      if(Changeset.get_field(@changeset, :type) == :shotgun,
 | 
			
		||||
      if(Changeset.get_field(@changeset, :class) == :shotgun,
 | 
			
		||||
        do: gettext("Slug core"),
 | 
			
		||||
        else: gettext("Bullet core")
 | 
			
		||||
      ),
 | 
			
		||||
@@ -152,7 +152,7 @@
 | 
			
		||||
    ) %>
 | 
			
		||||
    <%= error_tag(f, :bullet_core, "col-span-3 text-center") %>
 | 
			
		||||
 | 
			
		||||
    <%= if Changeset.get_field(@changeset, :type) in [:rifle, :pistol] do %>
 | 
			
		||||
    <%= if Changeset.get_field(@changeset, :class) in [:rifle, :pistol] do %>
 | 
			
		||||
      <%= label(f, :jacket_type, gettext("Jacket type"), class: "title text-lg text-primary-600") %>
 | 
			
		||||
      <%= text_input(f, :jacket_type,
 | 
			
		||||
        class: "text-center col-span-2 input input-primary",
 | 
			
		||||
@@ -172,7 +172,7 @@
 | 
			
		||||
    ) %>
 | 
			
		||||
    <%= error_tag(f, :case_material, "col-span-3 text-center") %>
 | 
			
		||||
 | 
			
		||||
    <%= if Changeset.get_field(@changeset, :type) == :shotgun do %>
 | 
			
		||||
    <%= if Changeset.get_field(@changeset, :class) == :shotgun do %>
 | 
			
		||||
      <%= label(f, :wadding, gettext("Wadding"), class: "title text-lg text-primary-600") %>
 | 
			
		||||
      <%= text_input(f, :wadding,
 | 
			
		||||
        class: "text-center col-span-2 input input-primary",
 | 
			
		||||
@@ -240,7 +240,7 @@
 | 
			
		||||
    ) %>
 | 
			
		||||
    <%= error_tag(f, :powder_type, "col-span-3 text-center") %>
 | 
			
		||||
 | 
			
		||||
    <%= if Changeset.get_field(@changeset, :type) in [:rifle, :pistol] do %>
 | 
			
		||||
    <%= if Changeset.get_field(@changeset, :class) in [:rifle, :pistol] do %>
 | 
			
		||||
      <%= label(f, :powder_grains_per_charge, gettext("Powder grains per charge"),
 | 
			
		||||
        class: "title text-lg text-primary-600"
 | 
			
		||||
      ) %>
 | 
			
		||||
@@ -262,7 +262,7 @@
 | 
			
		||||
    ) %>
 | 
			
		||||
    <%= error_tag(f, :pressure, "col-span-3 text-center") %>
 | 
			
		||||
 | 
			
		||||
    <%= if Changeset.get_field(@changeset, :type) == :shotgun do %>
 | 
			
		||||
    <%= if Changeset.get_field(@changeset, :class) == :shotgun do %>
 | 
			
		||||
      <%= label(f, :dram_equivalent, gettext("Dram equivalent"),
 | 
			
		||||
        class: "title text-lg text-primary-600"
 | 
			
		||||
      ) %>
 | 
			
		||||
@@ -275,7 +275,7 @@
 | 
			
		||||
      <%= hidden_input(f, :dram_equivalent, value: nil) %>
 | 
			
		||||
    <% end %>
 | 
			
		||||
 | 
			
		||||
    <%= if Changeset.get_field(@changeset, :type) in [:rifle, :pistol] do %>
 | 
			
		||||
    <%= if Changeset.get_field(@changeset, :class) in [:rifle, :pistol] do %>
 | 
			
		||||
      <%= label(f, :muzzle_velocity, gettext("Muzzle velocity"),
 | 
			
		||||
        class: "title text-lg text-primary-600"
 | 
			
		||||
      ) %>
 | 
			
		||||
							
								
								
									
										114
									
								
								lib/cannery_web/live/type_live/index.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								lib/cannery_web/live/type_live/index.ex
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,114 @@
 | 
			
		||||
defmodule CanneryWeb.TypeLive.Index do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  Liveview for showing a Cannery.Ammo.Type index
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb, :live_view
 | 
			
		||||
  alias Cannery.{Ammo, Ammo.Type}
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def mount(%{"search" => search}, _session, socket) do
 | 
			
		||||
    {:ok, socket |> assign(class: :all, show_used: false, search: search) |> list_types()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def mount(_params, _session, socket) do
 | 
			
		||||
    {:ok, socket |> assign(class: :all, show_used: false, search: nil) |> list_types()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_params(params, _url, %{assigns: %{live_action: live_action}} = socket) do
 | 
			
		||||
    {:noreply, apply_action(socket, live_action, params)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit, %{"id" => id}) do
 | 
			
		||||
    %{name: type_name} = type = Ammo.get_type!(id, current_user)
 | 
			
		||||
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("Edit %{type_name}", type_name: type_name),
 | 
			
		||||
      type: type
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp apply_action(%{assigns: %{current_user: current_user}} = socket, :clone, %{"id" => id}) do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("New Type"),
 | 
			
		||||
      type: %{Ammo.get_type!(id, current_user) | id: nil}
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp apply_action(socket, :new, _params) do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("New Type"),
 | 
			
		||||
      type: %Type{}
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp apply_action(socket, :index, _params) do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("Catalog"),
 | 
			
		||||
      search: nil,
 | 
			
		||||
      type: nil
 | 
			
		||||
    )
 | 
			
		||||
    |> list_types()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp apply_action(socket, :search, %{"search" => search}) do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: gettext("Catalog"),
 | 
			
		||||
      search: search,
 | 
			
		||||
      type: nil
 | 
			
		||||
    )
 | 
			
		||||
    |> list_types()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_event("delete", %{"id" => id}, %{assigns: %{current_user: current_user}} = socket) do
 | 
			
		||||
    %{name: name} = Ammo.get_type!(id, current_user) |> Ammo.delete_type!(current_user)
 | 
			
		||||
    prompt = dgettext("prompts", "%{name} deleted succesfully", name: name)
 | 
			
		||||
    {:noreply, socket |> put_flash(:info, prompt) |> list_types()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("toggle_show_used", _params, %{assigns: %{show_used: show_used}} = socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:show_used, !show_used) |> list_types()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("search", %{"search" => %{"search_term" => ""}}, socket) do
 | 
			
		||||
    {:noreply, socket |> push_patch(to: Routes.type_index_path(Endpoint, :index))}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("search", %{"search" => %{"search_term" => search_term}}, socket) do
 | 
			
		||||
    search_path = Routes.type_index_path(Endpoint, :search, search_term)
 | 
			
		||||
    {:noreply, socket |> push_patch(to: search_path)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_class", %{"type" => %{"class" => "rifle"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:class, :rifle) |> list_types()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_class", %{"type" => %{"class" => "shotgun"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:class, :shotgun) |> list_types()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_class", %{"type" => %{"class" => "pistol"}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:class, :pistol) |> list_types()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("change_class", %{"type" => %{"class" => _all}}, socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:class, :all) |> list_types()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp list_types(
 | 
			
		||||
         %{assigns: %{class: class, search: search, current_user: current_user}} = socket
 | 
			
		||||
       ) do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      types: Ammo.list_types(search, current_user, class),
 | 
			
		||||
      types_count: Ammo.get_types_count!(current_user)
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -3,34 +3,34 @@
 | 
			
		||||
    <%= gettext("Catalog") %>
 | 
			
		||||
  </h1>
 | 
			
		||||
 | 
			
		||||
  <%= if @ammo_types_count == 0 do %>
 | 
			
		||||
  <%= if @types_count == 0 do %>
 | 
			
		||||
    <h2 class="title text-xl text-primary-600">
 | 
			
		||||
      <%= gettext("No Ammo types") %>
 | 
			
		||||
      <%= gettext("No Types") %>
 | 
			
		||||
      <%= display_emoji("😔") %>
 | 
			
		||||
    </h2>
 | 
			
		||||
 | 
			
		||||
    <.link patch={Routes.ammo_type_index_path(Endpoint, :new)} class="btn btn-primary">
 | 
			
		||||
    <.link patch={Routes.type_index_path(Endpoint, :new)} class="btn btn-primary">
 | 
			
		||||
      <%= dgettext("actions", "Add your first type!") %>
 | 
			
		||||
    </.link>
 | 
			
		||||
  <% else %>
 | 
			
		||||
    <.link patch={Routes.ammo_type_index_path(Endpoint, :new)} class="btn btn-primary">
 | 
			
		||||
      <%= dgettext("actions", "New Ammo type") %>
 | 
			
		||||
    <.link patch={Routes.type_index_path(Endpoint, :new)} class="btn btn-primary">
 | 
			
		||||
      <%= dgettext("actions", "New Type") %>
 | 
			
		||||
    </.link>
 | 
			
		||||
 | 
			
		||||
    <div class="w-full flex flex-col sm:flex-row justify-center items-center space-y-4 sm:space-y-0 sm:space-x-4 max-w-2xl">
 | 
			
		||||
      <.form
 | 
			
		||||
        :let={f}
 | 
			
		||||
        for={%{}}
 | 
			
		||||
        as={:ammo_type}
 | 
			
		||||
        phx-change="change_type"
 | 
			
		||||
        phx-submit="change_type"
 | 
			
		||||
        as={:type}
 | 
			
		||||
        phx-change="change_class"
 | 
			
		||||
        phx-submit="change_class"
 | 
			
		||||
        class="flex items-center"
 | 
			
		||||
      >
 | 
			
		||||
        <%= label(f, :type, gettext("Type"), class: "title text-primary-600 text-lg text-center") %>
 | 
			
		||||
        <%= label(f, :class, gettext("Class"), class: "title text-primary-600 text-lg text-center") %>
 | 
			
		||||
 | 
			
		||||
        <%= select(
 | 
			
		||||
          f,
 | 
			
		||||
          :type,
 | 
			
		||||
          :class,
 | 
			
		||||
          [
 | 
			
		||||
            {gettext("All"), :all},
 | 
			
		||||
            {gettext("Rifle"), :rifle},
 | 
			
		||||
@@ -38,7 +38,7 @@
 | 
			
		||||
            {gettext("Pistol"), :pistol}
 | 
			
		||||
          ],
 | 
			
		||||
          class: "mx-2 my-1 min-w-md input input-primary",
 | 
			
		||||
          value: @type
 | 
			
		||||
          value: @class
 | 
			
		||||
        ) %>
 | 
			
		||||
      </.form>
 | 
			
		||||
 | 
			
		||||
@@ -66,49 +66,43 @@
 | 
			
		||||
      </.toggle_button>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <%= if @ammo_types |> Enum.empty?() do %>
 | 
			
		||||
    <%= if @types |> Enum.empty?() do %>
 | 
			
		||||
      <h2 class="title text-xl text-primary-600">
 | 
			
		||||
        <%= gettext("No Ammo types") %>
 | 
			
		||||
        <%= gettext("No Types") %>
 | 
			
		||||
        <%= display_emoji("😔") %>
 | 
			
		||||
      </h2>
 | 
			
		||||
    <% else %>
 | 
			
		||||
      <.live_component
 | 
			
		||||
        module={CanneryWeb.Components.AmmoTypeTableComponent}
 | 
			
		||||
        id="ammo_types_index_table"
 | 
			
		||||
        module={CanneryWeb.Components.TypeTableComponent}
 | 
			
		||||
        id="types_index_table"
 | 
			
		||||
        action={@live_action}
 | 
			
		||||
        ammo_types={@ammo_types}
 | 
			
		||||
        types={@types}
 | 
			
		||||
        current_user={@current_user}
 | 
			
		||||
        show_used={@show_used}
 | 
			
		||||
        type={@type}
 | 
			
		||||
        class={@class}
 | 
			
		||||
      >
 | 
			
		||||
        <:actions :let={ammo_type}>
 | 
			
		||||
        <:actions :let={type}>
 | 
			
		||||
          <div class="px-4 py-2 space-x-4 flex justify-center items-center">
 | 
			
		||||
            <.link
 | 
			
		||||
              navigate={Routes.ammo_type_show_path(Endpoint, :show, ammo_type)}
 | 
			
		||||
              navigate={Routes.type_show_path(Endpoint, :show, type)}
 | 
			
		||||
              class="text-primary-600 link"
 | 
			
		||||
              aria-label={
 | 
			
		||||
                dgettext("actions", "View %{ammo_type_name}", ammo_type_name: ammo_type.name)
 | 
			
		||||
              }
 | 
			
		||||
              aria-label={dgettext("actions", "View %{type_name}", type_name: type.name)}
 | 
			
		||||
            >
 | 
			
		||||
              <i class="fa-fw fa-lg fas fa-eye"></i>
 | 
			
		||||
            </.link>
 | 
			
		||||
 | 
			
		||||
            <.link
 | 
			
		||||
              patch={Routes.ammo_type_index_path(Endpoint, :edit, ammo_type)}
 | 
			
		||||
              patch={Routes.type_index_path(Endpoint, :edit, type)}
 | 
			
		||||
              class="text-primary-600 link"
 | 
			
		||||
              aria-label={
 | 
			
		||||
                dgettext("actions", "Edit %{ammo_type_name}", ammo_type_name: ammo_type.name)
 | 
			
		||||
              }
 | 
			
		||||
              aria-label={dgettext("actions", "Edit %{type_name}", type_name: type.name)}
 | 
			
		||||
            >
 | 
			
		||||
              <i class="fa-fw fa-lg fas fa-edit"></i>
 | 
			
		||||
            </.link>
 | 
			
		||||
 | 
			
		||||
            <.link
 | 
			
		||||
              patch={Routes.ammo_type_index_path(Endpoint, :clone, ammo_type)}
 | 
			
		||||
              patch={Routes.type_index_path(Endpoint, :clone, type)}
 | 
			
		||||
              class="text-primary-600 link"
 | 
			
		||||
              aria-label={
 | 
			
		||||
                dgettext("actions", "Clone %{ammo_type_name}", ammo_type_name: ammo_type.name)
 | 
			
		||||
              }
 | 
			
		||||
              aria-label={dgettext("actions", "Clone %{type_name}", type_name: type.name)}
 | 
			
		||||
            >
 | 
			
		||||
              <i class="fa-fw fa-lg fas fa-copy"></i>
 | 
			
		||||
            </.link>
 | 
			
		||||
@@ -117,17 +111,15 @@
 | 
			
		||||
              href="#"
 | 
			
		||||
              class="text-primary-600 link"
 | 
			
		||||
              phx-click="delete"
 | 
			
		||||
              phx-value-id={ammo_type.id}
 | 
			
		||||
              phx-value-id={type.id}
 | 
			
		||||
              data-confirm={
 | 
			
		||||
                dgettext(
 | 
			
		||||
                  "prompts",
 | 
			
		||||
                  "Are you sure you want to delete %{name}? This will delete all %{name} type ammo as well!",
 | 
			
		||||
                  name: ammo_type.name
 | 
			
		||||
                  name: type.name
 | 
			
		||||
                )
 | 
			
		||||
              }
 | 
			
		||||
              aria-label={
 | 
			
		||||
                dgettext("actions", "Delete %{ammo_type_name}", ammo_type_name: ammo_type.name)
 | 
			
		||||
              }
 | 
			
		||||
              aria-label={dgettext("actions", "Delete %{type_name}", type_name: type.name)}
 | 
			
		||||
            >
 | 
			
		||||
              <i class="fa-lg fas fa-trash"></i>
 | 
			
		||||
            </.link>
 | 
			
		||||
@@ -140,15 +132,15 @@
 | 
			
		||||
 | 
			
		||||
<.modal
 | 
			
		||||
  :if={@live_action in [:new, :edit, :clone]}
 | 
			
		||||
  return_to={Routes.ammo_type_index_path(Endpoint, :index)}
 | 
			
		||||
  return_to={Routes.type_index_path(Endpoint, :index)}
 | 
			
		||||
>
 | 
			
		||||
  <.live_component
 | 
			
		||||
    module={CanneryWeb.AmmoTypeLive.FormComponent}
 | 
			
		||||
    id={@ammo_type.id || :new}
 | 
			
		||||
    module={CanneryWeb.TypeLive.FormComponent}
 | 
			
		||||
    id={@type.id || :new}
 | 
			
		||||
    title={@page_title}
 | 
			
		||||
    action={@live_action}
 | 
			
		||||
    ammo_type={@ammo_type}
 | 
			
		||||
    return_to={Routes.ammo_type_index_path(Endpoint, :index)}
 | 
			
		||||
    type={@type}
 | 
			
		||||
    return_to={Routes.type_index_path(Endpoint, :index)}
 | 
			
		||||
    current_user={@current_user}
 | 
			
		||||
    }
 | 
			
		||||
  />
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
defmodule CanneryWeb.AmmoTypeLive.Show do
 | 
			
		||||
defmodule CanneryWeb.TypeLive.Show do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  Liveview for showing and editing an Cannery.Ammo.AmmoType
 | 
			
		||||
  Liveview for showing and editing an Cannery.Ammo.Type
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb, :live_view
 | 
			
		||||
  alias Cannery.{ActivityLog, Ammo, Ammo.AmmoType, Containers}
 | 
			
		||||
  alias Cannery.{ActivityLog, Ammo, Ammo.Type, Containers}
 | 
			
		||||
  alias CanneryWeb.Endpoint
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
@@ -13,49 +13,49 @@ defmodule CanneryWeb.AmmoTypeLive.Show do
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_params(%{"id" => id}, _params, socket) do
 | 
			
		||||
    {:noreply, socket |> display_ammo_type(id)}
 | 
			
		||||
    {:noreply, socket |> display_type(id)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_event(
 | 
			
		||||
        "delete",
 | 
			
		||||
        _params,
 | 
			
		||||
        %{assigns: %{ammo_type: ammo_type, current_user: current_user}} = socket
 | 
			
		||||
        %{assigns: %{type: type, current_user: current_user}} = socket
 | 
			
		||||
      ) do
 | 
			
		||||
    %{name: ammo_type_name} = ammo_type |> Ammo.delete_ammo_type!(current_user)
 | 
			
		||||
    %{name: type_name} = type |> Ammo.delete_type!(current_user)
 | 
			
		||||
 | 
			
		||||
    prompt = dgettext("prompts", "%{name} deleted succesfully", name: ammo_type_name)
 | 
			
		||||
    redirect_to = Routes.ammo_type_index_path(socket, :index)
 | 
			
		||||
    prompt = dgettext("prompts", "%{name} deleted succesfully", name: type_name)
 | 
			
		||||
    redirect_to = Routes.type_index_path(socket, :index)
 | 
			
		||||
 | 
			
		||||
    {:noreply, socket |> put_flash(:info, prompt) |> push_navigate(to: redirect_to)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("toggle_show_used", _params, %{assigns: %{show_used: show_used}} = socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:show_used, !show_used) |> display_ammo_type()}
 | 
			
		||||
    {:noreply, socket |> assign(:show_used, !show_used) |> display_type()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("toggle_table", _params, %{assigns: %{view_table: view_table}} = socket) do
 | 
			
		||||
    {:noreply, socket |> assign(:view_table, !view_table)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp display_ammo_type(
 | 
			
		||||
  defp display_type(
 | 
			
		||||
         %{assigns: %{live_action: live_action, current_user: current_user, show_used: show_used}} =
 | 
			
		||||
           socket,
 | 
			
		||||
         %AmmoType{name: ammo_type_name} = ammo_type
 | 
			
		||||
         %Type{name: type_name} = type
 | 
			
		||||
       ) do
 | 
			
		||||
    custom_fields? =
 | 
			
		||||
      fields_to_display(ammo_type)
 | 
			
		||||
      |> Enum.any?(fn %{key: field, type: type} ->
 | 
			
		||||
      fields_to_display(type)
 | 
			
		||||
      |> Enum.any?(fn %{key: field, type: column_type} ->
 | 
			
		||||
        default_value =
 | 
			
		||||
          case type do
 | 
			
		||||
          case column_type do
 | 
			
		||||
            :boolean -> false
 | 
			
		||||
            _other_type -> nil
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
        ammo_type |> Map.get(field) != default_value
 | 
			
		||||
        type |> Map.get(field) != default_value
 | 
			
		||||
      end)
 | 
			
		||||
 | 
			
		||||
    ammo_groups = ammo_type |> Ammo.list_ammo_groups_for_type(current_user, show_used)
 | 
			
		||||
    packs = type |> Ammo.list_packs_for_type(current_user, show_used)
 | 
			
		||||
 | 
			
		||||
    [
 | 
			
		||||
      original_counts,
 | 
			
		||||
@@ -66,11 +66,11 @@ defmodule CanneryWeb.AmmoTypeLive.Show do
 | 
			
		||||
    ] =
 | 
			
		||||
      if show_used do
 | 
			
		||||
        [
 | 
			
		||||
          ammo_groups |> Ammo.get_original_counts(current_user),
 | 
			
		||||
          ammo_type |> Ammo.get_used_ammo_groups_count_for_type(current_user),
 | 
			
		||||
          ammo_type |> Ammo.get_ammo_groups_count_for_type(current_user, true),
 | 
			
		||||
          ammo_type |> ActivityLog.get_used_count_for_ammo_type(current_user),
 | 
			
		||||
          ammo_type |> Ammo.get_historical_count_for_ammo_type(current_user)
 | 
			
		||||
          packs |> Ammo.get_original_counts(current_user),
 | 
			
		||||
          type |> Ammo.get_used_packs_count_for_type(current_user),
 | 
			
		||||
          type |> Ammo.get_packs_count_for_type(current_user, true),
 | 
			
		||||
          type |> ActivityLog.get_used_count_for_type(current_user),
 | 
			
		||||
          type |> Ammo.get_historical_count_for_type(current_user)
 | 
			
		||||
        ]
 | 
			
		||||
      else
 | 
			
		||||
        [nil, nil, nil, nil, nil]
 | 
			
		||||
@@ -78,49 +78,49 @@ defmodule CanneryWeb.AmmoTypeLive.Show do
 | 
			
		||||
 | 
			
		||||
    page_title =
 | 
			
		||||
      case live_action do
 | 
			
		||||
        :show -> ammo_type_name
 | 
			
		||||
        :edit -> gettext("Edit %{ammo_type_name}", ammo_type_name: ammo_type_name)
 | 
			
		||||
        :show -> type_name
 | 
			
		||||
        :edit -> gettext("Edit %{type_name}", type_name: type_name)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
    containers =
 | 
			
		||||
      ammo_groups
 | 
			
		||||
      packs
 | 
			
		||||
      |> Enum.map(fn %{container_id: container_id} -> container_id end)
 | 
			
		||||
      |> Containers.get_containers(current_user)
 | 
			
		||||
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(
 | 
			
		||||
      page_title: page_title,
 | 
			
		||||
      ammo_type: ammo_type,
 | 
			
		||||
      ammo_groups: ammo_groups,
 | 
			
		||||
      type: type,
 | 
			
		||||
      packs: packs,
 | 
			
		||||
      containers: containers,
 | 
			
		||||
      cprs: ammo_groups |> Ammo.get_cprs(current_user),
 | 
			
		||||
      last_used_dates: ammo_groups |> ActivityLog.get_last_used_dates(current_user),
 | 
			
		||||
      avg_cost_per_round: ammo_type |> Ammo.get_average_cost_for_ammo_type(current_user),
 | 
			
		||||
      rounds: ammo_type |> Ammo.get_round_count_for_ammo_type(current_user),
 | 
			
		||||
      cprs: packs |> Ammo.get_cprs(current_user),
 | 
			
		||||
      last_used_dates: packs |> ActivityLog.get_last_used_dates(current_user),
 | 
			
		||||
      avg_cost_per_round: type |> Ammo.get_average_cost_for_type(current_user),
 | 
			
		||||
      rounds: type |> Ammo.get_round_count_for_type(current_user),
 | 
			
		||||
      original_counts: original_counts,
 | 
			
		||||
      used_rounds: used_rounds,
 | 
			
		||||
      historical_round_count: historical_round_count,
 | 
			
		||||
      packs_count: ammo_type |> Ammo.get_ammo_groups_count_for_type(current_user),
 | 
			
		||||
      packs_count: type |> Ammo.get_packs_count_for_type(current_user),
 | 
			
		||||
      used_packs_count: used_packs_count,
 | 
			
		||||
      historical_packs_count: historical_packs_count,
 | 
			
		||||
      fields_to_display: fields_to_display(ammo_type),
 | 
			
		||||
      fields_to_display: fields_to_display(type),
 | 
			
		||||
      custom_fields?: custom_fields?
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp display_ammo_type(%{assigns: %{current_user: current_user}} = socket, ammo_type_id) do
 | 
			
		||||
    socket |> display_ammo_type(Ammo.get_ammo_type!(ammo_type_id, current_user))
 | 
			
		||||
  defp display_type(%{assigns: %{current_user: current_user}} = socket, type_id) do
 | 
			
		||||
    socket |> display_type(Ammo.get_type!(type_id, current_user))
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp display_ammo_type(%{assigns: %{ammo_type: ammo_type}} = socket) do
 | 
			
		||||
    socket |> display_ammo_type(ammo_type)
 | 
			
		||||
  defp display_type(%{assigns: %{type: type}} = socket) do
 | 
			
		||||
    socket |> display_type(type)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp fields_to_display(%AmmoType{type: type}) do
 | 
			
		||||
  defp fields_to_display(%Type{class: class}) do
 | 
			
		||||
    [
 | 
			
		||||
      %{label: gettext("Cartridge:"), key: :cartridge, type: :string},
 | 
			
		||||
      %{
 | 
			
		||||
        label: if(type == :shotgun, do: gettext("Gauge:"), else: gettext("Caliber:")),
 | 
			
		||||
        label: if(class == :shotgun, do: gettext("Gauge:"), else: gettext("Caliber:")),
 | 
			
		||||
        key: :caliber,
 | 
			
		||||
        type: :string
 | 
			
		||||
      },
 | 
			
		||||
@@ -1,22 +1,22 @@
 | 
			
		||||
<div class="space-y-4 flex flex-col justify-center items-center">
 | 
			
		||||
  <h1 class="title text-2xl title-primary-500">
 | 
			
		||||
    <%= @ammo_type.name %>
 | 
			
		||||
    <%= @type.name %>
 | 
			
		||||
  </h1>
 | 
			
		||||
 | 
			
		||||
  <span
 | 
			
		||||
    :if={@ammo_type.desc}
 | 
			
		||||
    :if={@type.desc}
 | 
			
		||||
    class="max-w-2xl w-full px-8 py-4 rounded-lg
 | 
			
		||||
      text-center title text-lg
 | 
			
		||||
      border border-primary-600"
 | 
			
		||||
  >
 | 
			
		||||
    <%= @ammo_type.desc %>
 | 
			
		||||
    <%= @type.desc %>
 | 
			
		||||
  </span>
 | 
			
		||||
 | 
			
		||||
  <div class="flex space-x-4 justify-center items-center text-primary-600">
 | 
			
		||||
    <.link
 | 
			
		||||
      patch={Routes.ammo_type_show_path(Endpoint, :edit, @ammo_type)}
 | 
			
		||||
      patch={Routes.type_show_path(Endpoint, :edit, @type)}
 | 
			
		||||
      class="text-primary-600 link"
 | 
			
		||||
      aria-label={dgettext("actions", "Edit %{ammo_type_name}", ammo_type_name: @ammo_type.name)}
 | 
			
		||||
      aria-label={dgettext("actions", "Edit %{type_name}", type_name: @type.name)}
 | 
			
		||||
    >
 | 
			
		||||
      <i class="fa-fw fa-lg fas fa-edit"></i>
 | 
			
		||||
    </.link>
 | 
			
		||||
@@ -29,12 +29,10 @@
 | 
			
		||||
        dgettext(
 | 
			
		||||
          "prompts",
 | 
			
		||||
          "Are you sure you want to delete %{name}? This will delete all %{name} type ammo as well!",
 | 
			
		||||
          name: @ammo_type.name
 | 
			
		||||
          name: @type.name
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
      aria-label={
 | 
			
		||||
        dgettext("actions", "Delete %{ammo_type_name}", ammo_type_name: @ammo_type.name)
 | 
			
		||||
      }
 | 
			
		||||
      aria-label={dgettext("actions", "Delete %{type_name}", type_name: @type.name)}
 | 
			
		||||
    >
 | 
			
		||||
      <i class="fa-fw fa-lg fas fa-trash"></i>
 | 
			
		||||
    </.link>
 | 
			
		||||
@@ -42,14 +40,14 @@
 | 
			
		||||
 | 
			
		||||
  <hr class="hr" />
 | 
			
		||||
 | 
			
		||||
  <%= if @ammo_type.type || @custom_fields? do %>
 | 
			
		||||
  <%= if @type.class || @custom_fields? do %>
 | 
			
		||||
    <div class="grid sm:grid-cols-2 gap-4 text-center justify-center items-center">
 | 
			
		||||
      <h3 class="title text-lg">
 | 
			
		||||
        <%= gettext("Type") %>
 | 
			
		||||
        <%= gettext("Class") %>
 | 
			
		||||
      </h3>
 | 
			
		||||
 | 
			
		||||
      <span class="text-primary-600">
 | 
			
		||||
        <%= case @ammo_type.type do %>
 | 
			
		||||
        <%= case @type.class do %>
 | 
			
		||||
          <% :shotgun -> %>
 | 
			
		||||
            <%= gettext("Shotgun") %>
 | 
			
		||||
          <% :rifle -> %>
 | 
			
		||||
@@ -62,7 +60,7 @@
 | 
			
		||||
      </span>
 | 
			
		||||
 | 
			
		||||
      <%= for %{label: label, key: key, type: type} <- @fields_to_display do %>
 | 
			
		||||
        <%= if @ammo_type |> Map.get(key) do %>
 | 
			
		||||
        <%= if @type |> Map.get(key) do %>
 | 
			
		||||
          <h3 class="title text-lg">
 | 
			
		||||
            <%= label %>
 | 
			
		||||
          </h3>
 | 
			
		||||
@@ -70,9 +68,9 @@
 | 
			
		||||
          <span class="text-primary-600">
 | 
			
		||||
            <%= case type do %>
 | 
			
		||||
              <% :boolean -> %>
 | 
			
		||||
                <%= @ammo_type |> Map.get(key) |> humanize() %>
 | 
			
		||||
                <%= @type |> Map.get(key) |> humanize() %>
 | 
			
		||||
              <% _ -> %>
 | 
			
		||||
                <%= @ammo_type |> Map.get(key) %>
 | 
			
		||||
                <%= @type |> Map.get(key) %>
 | 
			
		||||
            <% end %>
 | 
			
		||||
          </span>
 | 
			
		||||
        <% end %>
 | 
			
		||||
@@ -140,7 +138,7 @@
 | 
			
		||||
    </h3>
 | 
			
		||||
 | 
			
		||||
    <span class="text-primary-600">
 | 
			
		||||
      <.datetime id={"#{@ammo_type.id}-inserted-at"} datetime={@ammo_type.inserted_at} />
 | 
			
		||||
      <.datetime id={"#{@type.id}-inserted-at"} datetime={@type.inserted_at} />
 | 
			
		||||
    </span>
 | 
			
		||||
 | 
			
		||||
    <%= if @avg_cost_per_round do %>
 | 
			
		||||
@@ -175,7 +173,7 @@
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div class="w-full p-4">
 | 
			
		||||
    <%= if @ammo_groups |> Enum.empty?() do %>
 | 
			
		||||
    <%= if @packs |> Enum.empty?() do %>
 | 
			
		||||
      <h2 class="px-4 title text-lg text-primary-600">
 | 
			
		||||
        <%= gettext("No ammo for this type") %>
 | 
			
		||||
        <%= display_emoji("😔") %>
 | 
			
		||||
@@ -183,13 +181,13 @@
 | 
			
		||||
    <% else %>
 | 
			
		||||
      <%= if @view_table do %>
 | 
			
		||||
        <.live_component
 | 
			
		||||
          module={CanneryWeb.Components.AmmoGroupTableComponent}
 | 
			
		||||
          id="ammo-type-show-table"
 | 
			
		||||
          ammo_groups={@ammo_groups}
 | 
			
		||||
          module={CanneryWeb.Components.PackTableComponent}
 | 
			
		||||
          id="type-show-table"
 | 
			
		||||
          packs={@packs}
 | 
			
		||||
          current_user={@current_user}
 | 
			
		||||
          show_used={@show_used}
 | 
			
		||||
        >
 | 
			
		||||
          <:container :let={{_ammo_group, %{name: container_name} = container}}>
 | 
			
		||||
          <:container :let={{_pack, %{name: container_name} = container}}>
 | 
			
		||||
            <.link
 | 
			
		||||
              navigate={Routes.container_show_path(Endpoint, :show, container)}
 | 
			
		||||
              class="mx-2 my-1 link"
 | 
			
		||||
@@ -197,15 +195,13 @@
 | 
			
		||||
              <%= container_name %>
 | 
			
		||||
            </.link>
 | 
			
		||||
          </:container>
 | 
			
		||||
          <:actions :let={%{count: ammo_group_count} = ammo_group}>
 | 
			
		||||
          <:actions :let={%{count: pack_count} = pack}>
 | 
			
		||||
            <div class="py-2 px-4 h-full space-x-4 flex justify-center items-center">
 | 
			
		||||
              <.link
 | 
			
		||||
                navigate={Routes.ammo_group_show_path(Endpoint, :show, ammo_group)}
 | 
			
		||||
                navigate={Routes.pack_show_path(Endpoint, :show, pack)}
 | 
			
		||||
                class="text-primary-600 link"
 | 
			
		||||
                aria-label={
 | 
			
		||||
                  dgettext("actions", "View ammo group of %{ammo_group_count} bullets",
 | 
			
		||||
                    ammo_group_count: ammo_group_count
 | 
			
		||||
                  )
 | 
			
		||||
                  dgettext("actions", "View pack of %{pack_count} bullets", pack_count: pack_count)
 | 
			
		||||
                }
 | 
			
		||||
              >
 | 
			
		||||
                <i class="fa-fw fa-lg fas fa-eye"></i>
 | 
			
		||||
@@ -215,12 +211,12 @@
 | 
			
		||||
        </.live_component>
 | 
			
		||||
      <% else %>
 | 
			
		||||
        <div class="flex flex-wrap justify-center items-stretch">
 | 
			
		||||
          <.ammo_group_card
 | 
			
		||||
            :for={%{id: ammo_group_id, container_id: container_id} = ammo_group <- @ammo_groups}
 | 
			
		||||
            ammo_group={ammo_group}
 | 
			
		||||
            original_count={@original_counts && Map.fetch!(@original_counts, ammo_group_id)}
 | 
			
		||||
            cpr={Map.get(@cprs, ammo_group_id)}
 | 
			
		||||
            last_used_date={Map.get(@last_used_dates, ammo_group_id)}
 | 
			
		||||
          <.pack_card
 | 
			
		||||
            :for={%{id: pack_id, container_id: container_id} = pack <- @packs}
 | 
			
		||||
            pack={pack}
 | 
			
		||||
            original_count={@original_counts && Map.fetch!(@original_counts, pack_id)}
 | 
			
		||||
            cpr={Map.get(@cprs, pack_id)}
 | 
			
		||||
            last_used_date={Map.get(@last_used_dates, pack_id)}
 | 
			
		||||
            current_user={@current_user}
 | 
			
		||||
            container={Map.fetch!(@containers, container_id)}
 | 
			
		||||
          />
 | 
			
		||||
@@ -230,17 +226,14 @@
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<.modal
 | 
			
		||||
  :if={@live_action == :edit}
 | 
			
		||||
  return_to={Routes.ammo_type_show_path(Endpoint, :show, @ammo_type)}
 | 
			
		||||
>
 | 
			
		||||
<.modal :if={@live_action == :edit} return_to={Routes.type_show_path(Endpoint, :show, @type)}>
 | 
			
		||||
  <.live_component
 | 
			
		||||
    module={CanneryWeb.AmmoTypeLive.FormComponent}
 | 
			
		||||
    id={@ammo_type.id}
 | 
			
		||||
    module={CanneryWeb.TypeLive.FormComponent}
 | 
			
		||||
    id={@type.id}
 | 
			
		||||
    title={@page_title}
 | 
			
		||||
    action={@live_action}
 | 
			
		||||
    ammo_type={@ammo_type}
 | 
			
		||||
    return_to={Routes.ammo_type_show_path(Endpoint, :show, @ammo_type)}
 | 
			
		||||
    type={@type}
 | 
			
		||||
    return_to={Routes.type_show_path(Endpoint, :show, @type)}
 | 
			
		||||
    current_user={@current_user}
 | 
			
		||||
  />
 | 
			
		||||
</.modal>
 | 
			
		||||
@@ -69,14 +69,14 @@ defmodule CanneryWeb.Router do
 | 
			
		||||
    live "/tags/edit/:id", TagLive.Index, :edit
 | 
			
		||||
    live "/tags/search/:search", TagLive.Index, :search
 | 
			
		||||
 | 
			
		||||
    live "/catalog", AmmoTypeLive.Index, :index
 | 
			
		||||
    live "/catalog/new", AmmoTypeLive.Index, :new
 | 
			
		||||
    live "/catalog/clone/:id", AmmoTypeLive.Index, :clone
 | 
			
		||||
    live "/catalog/edit/:id", AmmoTypeLive.Index, :edit
 | 
			
		||||
    live "/catalog/search/:search", AmmoTypeLive.Index, :search
 | 
			
		||||
    live "/catalog", TypeLive.Index, :index
 | 
			
		||||
    live "/catalog/new", TypeLive.Index, :new
 | 
			
		||||
    live "/catalog/clone/:id", TypeLive.Index, :clone
 | 
			
		||||
    live "/catalog/edit/:id", TypeLive.Index, :edit
 | 
			
		||||
    live "/catalog/search/:search", TypeLive.Index, :search
 | 
			
		||||
 | 
			
		||||
    live "/type/:id", AmmoTypeLive.Show, :show
 | 
			
		||||
    live "/type/:id/edit", AmmoTypeLive.Show, :edit
 | 
			
		||||
    live "/type/:id", TypeLive.Show, :show
 | 
			
		||||
    live "/type/:id/edit", TypeLive.Show, :edit
 | 
			
		||||
 | 
			
		||||
    live "/containers", ContainerLive.Index, :index
 | 
			
		||||
    live "/containers/new", ContainerLive.Index, :new
 | 
			
		||||
@@ -89,23 +89,23 @@ defmodule CanneryWeb.Router do
 | 
			
		||||
    live "/container/edit/:id", ContainerLive.Show, :edit
 | 
			
		||||
    live "/container/edit_tags/:id", ContainerLive.Show, :edit_tags
 | 
			
		||||
 | 
			
		||||
    live "/ammo", AmmoGroupLive.Index, :index
 | 
			
		||||
    live "/ammo/new", AmmoGroupLive.Index, :new
 | 
			
		||||
    live "/ammo/edit/:id", AmmoGroupLive.Index, :edit
 | 
			
		||||
    live "/ammo/clone/:id", AmmoGroupLive.Index, :clone
 | 
			
		||||
    live "/ammo/add_shot_group/:id", AmmoGroupLive.Index, :add_shot_group
 | 
			
		||||
    live "/ammo/move/:id", AmmoGroupLive.Index, :move
 | 
			
		||||
    live "/ammo/search/:search", AmmoGroupLive.Index, :search
 | 
			
		||||
    live "/ammo", PackLive.Index, :index
 | 
			
		||||
    live "/ammo/new", PackLive.Index, :new
 | 
			
		||||
    live "/ammo/edit/:id", PackLive.Index, :edit
 | 
			
		||||
    live "/ammo/clone/:id", PackLive.Index, :clone
 | 
			
		||||
    live "/ammo/add_shot_record/:id", PackLive.Index, :add_shot_record
 | 
			
		||||
    live "/ammo/move/:id", PackLive.Index, :move
 | 
			
		||||
    live "/ammo/search/:search", PackLive.Index, :search
 | 
			
		||||
 | 
			
		||||
    live "/ammo/show/:id", AmmoGroupLive.Show, :show
 | 
			
		||||
    live "/ammo/show/edit/:id", AmmoGroupLive.Show, :edit
 | 
			
		||||
    live "/ammo/show/add_shot_group/:id", AmmoGroupLive.Show, :add_shot_group
 | 
			
		||||
    live "/ammo/show/move/:id", AmmoGroupLive.Show, :move
 | 
			
		||||
    live "/ammo/show/:id/edit/:shot_group_id", AmmoGroupLive.Show, :edit_shot_group
 | 
			
		||||
    live "/ammo/show/:id", PackLive.Show, :show
 | 
			
		||||
    live "/ammo/show/edit/:id", PackLive.Show, :edit
 | 
			
		||||
    live "/ammo/show/add_shot_record/:id", PackLive.Show, :add_shot_record
 | 
			
		||||
    live "/ammo/show/move/:id", PackLive.Show, :move
 | 
			
		||||
    live "/ammo/show/:id/edit/:shot_record_id", PackLive.Show, :edit_shot_record
 | 
			
		||||
 | 
			
		||||
    live "/range", RangeLive.Index, :index
 | 
			
		||||
    live "/range/edit/:id", RangeLive.Index, :edit
 | 
			
		||||
    live "/range/add_shot_group/:id", RangeLive.Index, :add_shot_group
 | 
			
		||||
    live "/range/add_shot_record/:id", RangeLive.Index, :add_shot_record
 | 
			
		||||
    live "/range/search/:search", RangeLive.Index, :search
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								mix.exs
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								mix.exs
									
									
									
									
									
								
							@@ -4,7 +4,7 @@ defmodule Cannery.MixProject do
 | 
			
		||||
  def project do
 | 
			
		||||
    [
 | 
			
		||||
      app: :cannery,
 | 
			
		||||
      version: "0.9.0",
 | 
			
		||||
      version: "0.9.2",
 | 
			
		||||
      elixir: "1.14.1",
 | 
			
		||||
      elixirc_paths: elixirc_paths(Mix.env()),
 | 
			
		||||
      compilers: Mix.compilers(),
 | 
			
		||||
 
 | 
			
		||||
@@ -10,14 +10,14 @@
 | 
			
		||||
msgid ""
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.ex:54
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.ex:62
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:38
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.ex:59
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.ex:67
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:38
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add Ammo"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:34
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:34
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add your first box!"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -27,7 +27,7 @@ msgstr ""
 | 
			
		||||
msgid "Add your first container!"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:13
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:13
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add your first type!"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -82,11 +82,6 @@ msgstr ""
 | 
			
		||||
msgid "Make your first tag!"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:17
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "New Ammo type"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:17
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "New Container"
 | 
			
		||||
@@ -120,13 +115,13 @@ msgstr ""
 | 
			
		||||
msgid "Reset password"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/add_shot_group_component.html.heex:57
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:84
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:350
 | 
			
		||||
#: lib/cannery_web/components/add_shot_record_component.html.heex:57
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.html.heex:57
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.html.heex:35
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:91
 | 
			
		||||
#: lib/cannery_web/live/range_live/form_component.html.heex:45
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.html.heex:37
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.html.heex:350
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Save"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -156,19 +151,19 @@ msgstr ""
 | 
			
		||||
msgid "Why not get some ready to shoot?"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:125
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:103
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:127
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:96
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:45
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Record shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:90
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:88
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add another container!"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:126
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:124
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Select"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -178,12 +173,12 @@ msgstr ""
 | 
			
		||||
msgid "Copy to clipboard"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:14
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:14
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "add a container first"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:77
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:84
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Create"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -198,19 +193,14 @@ msgstr ""
 | 
			
		||||
msgid "Change language"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:55
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:55
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "View in Catalog"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:24
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "add an ammo type first"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:80
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:142
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:96
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:78
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:144
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:89
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Move ammo"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -220,13 +210,13 @@ msgstr ""
 | 
			
		||||
msgid "Set Unlimited"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:89
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:85
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:38
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Stage for range"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:88
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:84
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:37
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Unstage from range"
 | 
			
		||||
@@ -237,11 +227,6 @@ msgstr ""
 | 
			
		||||
msgid "Export Data as JSON"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:110
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Clone %{ammo_type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:87
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:145
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
@@ -253,12 +238,6 @@ msgstr ""
 | 
			
		||||
msgid "Copy invite link for %{invite_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:129
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:36
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Delete %{ammo_type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:104
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:162
 | 
			
		||||
#: lib/cannery_web/live/container_live/show.html.heex:48
 | 
			
		||||
@@ -276,18 +255,6 @@ msgstr ""
 | 
			
		||||
msgid "Delete invite for %{invite_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:161
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:155
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Delete shot record of %{shot_group_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:100
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:19
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit %{ammo_type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:77
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:135
 | 
			
		||||
#: lib/cannery_web/live/container_live/show.html.heex:35
 | 
			
		||||
@@ -300,28 +267,12 @@ msgstr ""
 | 
			
		||||
msgid "Edit %{tag_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:164
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:62
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/invite_live/index.html.heex:46
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit invite for %{invite_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:146
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit shot group of %{shot_group_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:138
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit shot record of %{shot_group_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:118
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:120
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Stage"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -332,29 +283,74 @@ msgstr ""
 | 
			
		||||
msgid "Tag %{container_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:117
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:119
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Unstage"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:90
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:174
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "View %{ammo_type_name}"
 | 
			
		||||
msgid "Clone pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:176
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:189
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:74
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Clone ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgid "Delete pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:191
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:76
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:164
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:62
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Delete ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgid "Edit pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:152
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:206
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:154
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:204
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "View ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgid "View pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:160
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:155
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Delete shot record of %{shot_record_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:145
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:138
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit shot record of %{shot_record_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:105
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Clone %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:122
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:35
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Delete %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:97
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:19
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:17
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "New Type"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:89
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "View %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:24
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "add a type first"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 
 | 
			
		||||
@@ -23,14 +23,14 @@ msgstr ""
 | 
			
		||||
## 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.
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.ex:54
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.ex:62
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:38
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.ex:59
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.ex:67
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:38
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add Ammo"
 | 
			
		||||
msgstr "Munition hinzufügen"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:34
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:34
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add your first box!"
 | 
			
		||||
msgstr "Fügen Sie ihre erste Box hinzu!"
 | 
			
		||||
@@ -40,7 +40,7 @@ msgstr "Fügen Sie ihre erste Box hinzu!"
 | 
			
		||||
msgid "Add your first container!"
 | 
			
		||||
msgstr "Fügen Sie ihren ersten Behälter hinzu!"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:13
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:13
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add your first type!"
 | 
			
		||||
msgstr "Fügen Sie ihre erste Munitionsart hinzu!"
 | 
			
		||||
@@ -95,11 +95,6 @@ msgstr "Einloggen"
 | 
			
		||||
msgid "Make your first tag!"
 | 
			
		||||
msgstr "Erstellen Sie ihren ersten Tag!"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:17
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "New Ammo type"
 | 
			
		||||
msgstr "Neue Munitionsart"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:17
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "New Container"
 | 
			
		||||
@@ -133,13 +128,13 @@ msgstr "Bestätigungsmail erneut senden"
 | 
			
		||||
msgid "Reset password"
 | 
			
		||||
msgstr "Passwort zurücksetzen"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/add_shot_group_component.html.heex:57
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:84
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:350
 | 
			
		||||
#: lib/cannery_web/components/add_shot_record_component.html.heex:57
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.html.heex:57
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.html.heex:35
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:91
 | 
			
		||||
#: lib/cannery_web/live/range_live/form_component.html.heex:45
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.html.heex:37
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.html.heex:350
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Save"
 | 
			
		||||
msgstr "Speichern"
 | 
			
		||||
@@ -169,19 +164,19 @@ msgstr "Munition markieren"
 | 
			
		||||
msgid "Why not get some ready to shoot?"
 | 
			
		||||
msgstr "Warum nicht einige für den Schießstand auswählen?"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:125
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:103
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:127
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:96
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:45
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Record shots"
 | 
			
		||||
msgstr "Schüsse dokumentieren"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:90
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:88
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add another container!"
 | 
			
		||||
msgstr "Einen weiteren Behälter hinzufügen!"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:126
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:124
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Select"
 | 
			
		||||
msgstr "Markieren"
 | 
			
		||||
@@ -191,12 +186,12 @@ msgstr "Markieren"
 | 
			
		||||
msgid "Copy to clipboard"
 | 
			
		||||
msgstr "In die Zwischenablage kopieren"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:14
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:14
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "add a container first"
 | 
			
		||||
msgstr "Zuerst einen Behälter hinzufügen"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:77
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:84
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Create"
 | 
			
		||||
msgstr "Erstellen"
 | 
			
		||||
@@ -211,19 +206,14 @@ msgstr "Sprache wechseln"
 | 
			
		||||
msgid "Change language"
 | 
			
		||||
msgstr "Sprache wechseln"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:55
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:55
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "View in Catalog"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:24
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "add an ammo type first"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:80
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:142
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:96
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:78
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:144
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:89
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Move ammo"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -233,13 +223,13 @@ msgstr ""
 | 
			
		||||
msgid "Set Unlimited"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:89
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:85
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:38
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Stage for range"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:88
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:84
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:37
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Unstage from range"
 | 
			
		||||
@@ -250,11 +240,6 @@ msgstr ""
 | 
			
		||||
msgid "Export Data as JSON"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:110
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Clone %{ammo_type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:87
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:145
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
@@ -266,12 +251,6 @@ msgstr ""
 | 
			
		||||
msgid "Copy invite link for %{invite_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:129
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:36
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Delete %{ammo_type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:104
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:162
 | 
			
		||||
#: lib/cannery_web/live/container_live/show.html.heex:48
 | 
			
		||||
@@ -289,18 +268,6 @@ msgstr ""
 | 
			
		||||
msgid "Delete invite for %{invite_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:161
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:155
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Delete shot record of %{shot_group_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:100
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:19
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit %{ammo_type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:77
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:135
 | 
			
		||||
#: lib/cannery_web/live/container_live/show.html.heex:35
 | 
			
		||||
@@ -313,28 +280,12 @@ msgstr ""
 | 
			
		||||
msgid "Edit %{tag_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:164
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:62
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/invite_live/index.html.heex:46
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit invite for %{invite_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:146
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit shot group of %{shot_group_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:138
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit shot record of %{shot_group_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:118
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:120
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Stage"
 | 
			
		||||
msgstr "Munition markieren"
 | 
			
		||||
@@ -345,29 +296,74 @@ msgstr "Munition markieren"
 | 
			
		||||
msgid "Tag %{container_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:117
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:119
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Unstage"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:90
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "View %{ammo_type_name}"
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:174
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Clone pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:176
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:189
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:74
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Clone ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgid "Delete pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:191
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:76
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:164
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:62
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Delete ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgid "Edit pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:152
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:206
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:154
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:204
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "View ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgid "View pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:160
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:155
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Delete shot record of %{shot_record_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:145
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:138
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Edit shot record of %{shot_record_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:105
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Clone %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:122
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:35
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Delete %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:97
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:19
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Edit %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:17
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "New Type"
 | 
			
		||||
msgstr "Neue Munitionsart"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:89
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "View %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:24
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "add a type first"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -69,7 +69,7 @@ msgstr "Ungültige Mailadresse oder Passwort"
 | 
			
		||||
msgid "Not found"
 | 
			
		||||
msgstr "Nicht gefunden"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:18
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.html.heex:18
 | 
			
		||||
#: lib/cannery_web/templates/user_registration/new.html.heex:13
 | 
			
		||||
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:13
 | 
			
		||||
#: lib/cannery_web/templates/user_settings/edit.html.heex:22
 | 
			
		||||
@@ -161,54 +161,54 @@ msgstr ""
 | 
			
		||||
msgid "Tag could not be removed"
 | 
			
		||||
msgstr "Tag konnte nicht gelöscht werden"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:160
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:159
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Could not parse number of copies"
 | 
			
		||||
msgstr "Konnte die Anzahl der Kopien nicht verstehen"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:150
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:149
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Ungültige Nummer an Kopien. Muss zwischen 1 and %{max} liegen. War "
 | 
			
		||||
"%{multiplier}"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/ammo.ex:1114
 | 
			
		||||
#: lib/cannery/ammo.ex:1121
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Invalid multiplier"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/ammo/ammo_group.ex:92
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Please select an ammo type and container"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:74
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Your browser does not support the canvas element."
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:74
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:74
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Please select a valid user and ammo pack"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:88
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:88
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo left can be at most %{count} rounds"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:84
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:84
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo left must be at least 0"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:122
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:119
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Count can be at most %{count} shots"
 | 
			
		||||
msgstr "Anzahl muss weniger als %{count} betragen"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:80
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:80
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "can't be blank"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/ammo/pack.ex:100
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Please select a type and container"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 
 | 
			
		||||
@@ -23,17 +23,17 @@ msgstr ""
 | 
			
		||||
## 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.
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.ex:89
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.ex:89
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.ex:80
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.ex:78
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.ex:88
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "%{name} created successfully"
 | 
			
		||||
msgstr "%{name} erfolgreich erstellt"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.ex:72
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.ex:27
 | 
			
		||||
#: lib/cannery_web/live/tag_live/index.ex:65
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.ex:72
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.ex:27
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "%{name} deleted succesfully"
 | 
			
		||||
msgstr "%{name} erfolgreich gelöscht"
 | 
			
		||||
@@ -44,10 +44,10 @@ msgstr "%{name} erfolgreich gelöscht"
 | 
			
		||||
msgid "%{name} has been deleted"
 | 
			
		||||
msgstr "%{name} wurde gelöscht"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.ex:70
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.ex:70
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.ex:62
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.ex:60
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.ex:69
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "%{name} updated successfully"
 | 
			
		||||
msgstr "%{name} erfolgreich aktualisiert"
 | 
			
		||||
@@ -73,8 +73,8 @@ msgstr ""
 | 
			
		||||
msgid "Are you sure you want to delete %{name}?"
 | 
			
		||||
msgstr "Sind Sie sicher, dass sie %{name} löschen möchten?"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:189
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:74
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:187
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:72
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Are you sure you want to delete this ammo?"
 | 
			
		||||
msgstr "Sind Sie sicher, dass sie diese Munition löschen möchten?"
 | 
			
		||||
@@ -128,13 +128,13 @@ msgstr "Passwort erfolgreich geändert."
 | 
			
		||||
msgid "Please check your email to verify your account"
 | 
			
		||||
msgstr "Bitte überprüfen Sie ihre Mailbox und bestätigen Sie das Nutzerkonto"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/add_shot_group_component.html.heex:59
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:85
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:351
 | 
			
		||||
#: lib/cannery_web/components/add_shot_record_component.html.heex:59
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.html.heex:59
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.html.heex:37
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:92
 | 
			
		||||
#: lib/cannery_web/live/range_live/form_component.html.heex:47
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.html.heex:39
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.html.heex:351
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Saving..."
 | 
			
		||||
msgstr "Speichere..."
 | 
			
		||||
@@ -166,7 +166,7 @@ msgstr "%{tag_name} wurde von %{container_name} entfernt"
 | 
			
		||||
msgid "Adding..."
 | 
			
		||||
msgstr "Füge hinzu..."
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/add_shot_group_component.ex:60
 | 
			
		||||
#: lib/cannery_web/components/add_shot_record_component.ex:60
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Shots recorded successfully"
 | 
			
		||||
msgstr "Schüsse erfolgreich dokumentiert"
 | 
			
		||||
@@ -176,13 +176,13 @@ msgstr "Schüsse erfolgreich dokumentiert"
 | 
			
		||||
msgid "Are you sure you want to unstage this ammo?"
 | 
			
		||||
msgstr "Sind sie sicher, dass Sie diese Munition demarkieren möchten?"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:159
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:158
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:152
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Are you sure you want to delete this shot record?"
 | 
			
		||||
msgstr "Sind sie sicher, dass sie die Schießkladde löschen möchten?"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:81
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:80
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.ex:79
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Shot records deleted succesfully"
 | 
			
		||||
@@ -198,7 +198,7 @@ msgstr "Schießkladde erfolgreich aktualisiert"
 | 
			
		||||
msgid "%{email} confirmed successfully."
 | 
			
		||||
msgstr "%{email} erfolgreich bestätigt."
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:54
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:52
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo moved to %{name} successfully"
 | 
			
		||||
msgstr "Munition erfolgreich zu %{name} verschoben"
 | 
			
		||||
@@ -213,13 +213,13 @@ msgstr "Der Zwischenablage hinzugefügt"
 | 
			
		||||
msgid "%{name} removed successfully"
 | 
			
		||||
msgstr "%{name} erfolgreich entfernt"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:10
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:20
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:10
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:20
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "You'll need to"
 | 
			
		||||
msgstr "Sie müssen"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:78
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:85
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Creating..."
 | 
			
		||||
msgstr "Erstellen..."
 | 
			
		||||
@@ -234,31 +234,31 @@ msgstr "Möchten Sie die Sprache wechseln?"
 | 
			
		||||
msgid "Language updated successfully."
 | 
			
		||||
msgstr "Spracheinstellung gespeichert."
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.ex:89
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:55
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.ex:94
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:55
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Ammo deleted succesfully"
 | 
			
		||||
msgstr "Munitionsgruppe erfolgreich gelöscht"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.ex:93
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.ex:92
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Ammo unstaged succesfully"
 | 
			
		||||
msgstr "Munition erfolgreich demarkiert"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:126
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:125
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Ammo updated successfully"
 | 
			
		||||
msgstr "Munitionsgruppe erfolgreich aktualisiert"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:185
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:184
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Ammo added successfully"
 | 
			
		||||
msgid_plural "Ammo added successfully"
 | 
			
		||||
msgstr[0] "Munitionsgruppe erfolgreich aktualisiert"
 | 
			
		||||
msgstr[1] "Munitionsgruppe erfolgreich aktualisiert"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:122
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:29
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:116
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:29
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Are you sure you want to delete %{name}? This will delete all %{name} type ammo as well!"
 | 
			
		||||
msgstr "Sind Sie sicher, dass sie %{name} löschen möchten?"
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -10,14 +10,14 @@ msgid ""
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Language: en\n"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.ex:54
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.ex:62
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:38
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.ex:59
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.ex:67
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:38
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add Ammo"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:34
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:34
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add your first box!"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -27,7 +27,7 @@ msgstr ""
 | 
			
		||||
msgid "Add your first container!"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:13
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:13
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add your first type!"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -82,11 +82,6 @@ msgstr ""
 | 
			
		||||
msgid "Make your first tag!"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:17
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "New Ammo type"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:17
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "New Container"
 | 
			
		||||
@@ -120,13 +115,13 @@ msgstr ""
 | 
			
		||||
msgid "Reset password"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/add_shot_group_component.html.heex:57
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:84
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:350
 | 
			
		||||
#: lib/cannery_web/components/add_shot_record_component.html.heex:57
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.html.heex:57
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.html.heex:35
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:91
 | 
			
		||||
#: lib/cannery_web/live/range_live/form_component.html.heex:45
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.html.heex:37
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.html.heex:350
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Save"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -156,19 +151,19 @@ msgstr ""
 | 
			
		||||
msgid "Why not get some ready to shoot?"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:125
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:103
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:127
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:96
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:45
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Record shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:90
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:88
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add another container!"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:126
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:124
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Select"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -178,12 +173,12 @@ msgstr ""
 | 
			
		||||
msgid "Copy to clipboard"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:14
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:14
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "add a container first"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:77
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:84
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Create"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -198,19 +193,14 @@ msgstr ""
 | 
			
		||||
msgid "Change language"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:55
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:55
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "View in Catalog"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:24
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "add an ammo type first"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:80
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:142
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:96
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:78
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:144
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:89
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Move ammo"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -220,13 +210,13 @@ msgstr ""
 | 
			
		||||
msgid "Set Unlimited"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:89
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:85
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:38
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Stage for range"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:88
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:84
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:37
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Unstage from range"
 | 
			
		||||
@@ -237,11 +227,6 @@ msgstr ""
 | 
			
		||||
msgid "Export Data as JSON"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:110
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Clone %{ammo_type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:87
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:145
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
@@ -253,12 +238,6 @@ msgstr ""
 | 
			
		||||
msgid "Copy invite link for %{invite_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:129
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:36
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Delete %{ammo_type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:104
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:162
 | 
			
		||||
#: lib/cannery_web/live/container_live/show.html.heex:48
 | 
			
		||||
@@ -276,18 +255,6 @@ msgstr ""
 | 
			
		||||
msgid "Delete invite for %{invite_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:161
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:155
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Delete shot record of %{shot_group_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:100
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:19
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit %{ammo_type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:77
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:135
 | 
			
		||||
#: lib/cannery_web/live/container_live/show.html.heex:35
 | 
			
		||||
@@ -300,28 +267,12 @@ msgstr ""
 | 
			
		||||
msgid "Edit %{tag_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:164
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:62
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/invite_live/index.html.heex:46
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit invite for %{invite_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:146
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit shot group of %{shot_group_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:138
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit shot record of %{shot_group_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:118
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:120
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Stage"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -332,29 +283,74 @@ msgstr ""
 | 
			
		||||
msgid "Tag %{container_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:117
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:119
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Unstage"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:90
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "View %{ammo_type_name}"
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:174
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Clone pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:176
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:189
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:74
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Clone ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgid "Delete pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:191
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:76
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:164
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:62
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Delete ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgid "Edit pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:152
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:206
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:154
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:204
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "View ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgid "View pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:160
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:155
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Delete shot record of %{shot_record_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:145
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:138
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Edit shot record of %{shot_record_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:105
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Clone %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:122
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:35
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Delete %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:97
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:19
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Edit %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:17
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "New Type"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:89
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "View %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:24
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "add a type first"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -56,7 +56,7 @@ msgstr ""
 | 
			
		||||
msgid "Not found"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:18
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.html.heex:18
 | 
			
		||||
#: lib/cannery_web/templates/user_registration/new.html.heex:13
 | 
			
		||||
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:13
 | 
			
		||||
#: lib/cannery_web/templates/user_settings/edit.html.heex:22
 | 
			
		||||
@@ -146,52 +146,52 @@ msgstr ""
 | 
			
		||||
msgid "Tag could not be removed"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:160
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:159
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Could not parse number of copies"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:150
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:149
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/ammo.ex:1114
 | 
			
		||||
#: lib/cannery/ammo.ex:1121
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Invalid multiplier"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/ammo/ammo_group.ex:92
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Please select an ammo type and container"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:74
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Your browser does not support the canvas element."
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:74
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:74
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Please select a valid user and ammo pack"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:88
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:88
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo left can be at most %{count} rounds"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:84
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:84
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo left must be at least 0"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:122
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:119
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Count can be at most %{count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:80
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:80
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "can't be blank"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/ammo/pack.ex:100
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Please select a type and container"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 
 | 
			
		||||
@@ -10,17 +10,17 @@ msgid ""
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Language: en\n"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.ex:89
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.ex:89
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.ex:80
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.ex:78
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.ex:88
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "%{name} created successfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.ex:72
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.ex:27
 | 
			
		||||
#: lib/cannery_web/live/tag_live/index.ex:65
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.ex:72
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.ex:27
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "%{name} deleted succesfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -31,10 +31,10 @@ msgstr ""
 | 
			
		||||
msgid "%{name} has been deleted"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.ex:70
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.ex:70
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.ex:62
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.ex:60
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.ex:69
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "%{name} updated successfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -58,8 +58,8 @@ msgstr ""
 | 
			
		||||
msgid "Are you sure you want to delete %{name}?"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:189
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:74
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:187
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:72
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Are you sure you want to delete this ammo?"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -109,13 +109,13 @@ msgstr ""
 | 
			
		||||
msgid "Please check your email to verify your account"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/add_shot_group_component.html.heex:59
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:85
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:351
 | 
			
		||||
#: lib/cannery_web/components/add_shot_record_component.html.heex:59
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.html.heex:59
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.html.heex:37
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:92
 | 
			
		||||
#: lib/cannery_web/live/range_live/form_component.html.heex:47
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.html.heex:39
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.html.heex:351
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Saving..."
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -145,7 +145,7 @@ msgstr ""
 | 
			
		||||
msgid "Adding..."
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/add_shot_group_component.ex:60
 | 
			
		||||
#: lib/cannery_web/components/add_shot_record_component.ex:60
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Shots recorded successfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -155,13 +155,13 @@ msgstr ""
 | 
			
		||||
msgid "Are you sure you want to unstage this ammo?"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:159
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:158
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:152
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Are you sure you want to delete this shot record?"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:81
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:80
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.ex:79
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Shot records deleted succesfully"
 | 
			
		||||
@@ -177,7 +177,7 @@ msgstr ""
 | 
			
		||||
msgid "%{email} confirmed successfully."
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:54
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:52
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo moved to %{name} successfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -192,13 +192,13 @@ msgstr ""
 | 
			
		||||
msgid "%{name} removed successfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:10
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:20
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:10
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:20
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "You'll need to"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:78
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:85
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Creating..."
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -213,31 +213,31 @@ msgstr ""
 | 
			
		||||
msgid "Language updated successfully."
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.ex:89
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:55
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.ex:94
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:55
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Ammo deleted succesfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.ex:93
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.ex:92
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Ammo unstaged succesfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:126
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:125
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Ammo updated successfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:185
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:184
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Ammo added successfully"
 | 
			
		||||
msgid_plural "Ammo added successfully"
 | 
			
		||||
msgstr[0] ""
 | 
			
		||||
msgstr[1] ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:122
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:29
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:116
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:29
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Are you sure you want to delete %{name}? This will delete all %{name} type ammo as well!"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 
 | 
			
		||||
@@ -56,7 +56,7 @@ msgstr ""
 | 
			
		||||
msgid "Not found"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:18
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.html.heex:18
 | 
			
		||||
#: lib/cannery_web/templates/user_registration/new.html.heex:13
 | 
			
		||||
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:13
 | 
			
		||||
#: lib/cannery_web/templates/user_settings/edit.html.heex:22
 | 
			
		||||
@@ -145,52 +145,52 @@ msgstr ""
 | 
			
		||||
msgid "Tag could not be removed"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:160
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:159
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Could not parse number of copies"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:150
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:149
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/ammo.ex:1114
 | 
			
		||||
#: lib/cannery/ammo.ex:1121
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Invalid multiplier"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/ammo/ammo_group.ex:92
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Please select an ammo type and container"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:74
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Your browser does not support the canvas element."
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:74
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:74
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Please select a valid user and ammo pack"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:88
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:88
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo left can be at most %{count} rounds"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:84
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:84
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo left must be at least 0"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:122
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:119
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Count can be at most %{count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:80
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:80
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "can't be blank"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/ammo/pack.ex:100
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Please select a type and container"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 
 | 
			
		||||
@@ -23,14 +23,14 @@ msgstr ""
 | 
			
		||||
## 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.
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.ex:54
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.ex:62
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:38
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.ex:59
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.ex:67
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:38
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add Ammo"
 | 
			
		||||
msgstr "Añadir Munición"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:34
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:34
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add your first box!"
 | 
			
		||||
msgstr "¡Añade tu primera caja!"
 | 
			
		||||
@@ -40,7 +40,7 @@ msgstr "¡Añade tu primera caja!"
 | 
			
		||||
msgid "Add your first container!"
 | 
			
		||||
msgstr "¡Añade tu primer contenedor!"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:13
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:13
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add your first type!"
 | 
			
		||||
msgstr "¡Añade tu primer tipo!"
 | 
			
		||||
@@ -95,11 +95,6 @@ msgstr "Entrar"
 | 
			
		||||
msgid "Make your first tag!"
 | 
			
		||||
msgstr "¡Aplica tu primera etiqueta!"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:17
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "New Ammo type"
 | 
			
		||||
msgstr "Nuevo tipo de Munición"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:17
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "New Container"
 | 
			
		||||
@@ -133,13 +128,13 @@ msgstr "Reenviar instrucciones de confirmación"
 | 
			
		||||
msgid "Reset password"
 | 
			
		||||
msgstr "Resetear contraseña"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/add_shot_group_component.html.heex:57
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:84
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:350
 | 
			
		||||
#: lib/cannery_web/components/add_shot_record_component.html.heex:57
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.html.heex:57
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.html.heex:35
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:91
 | 
			
		||||
#: lib/cannery_web/live/range_live/form_component.html.heex:45
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.html.heex:37
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.html.heex:350
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Save"
 | 
			
		||||
msgstr "Guardar"
 | 
			
		||||
@@ -169,19 +164,19 @@ msgstr "Preparar munición"
 | 
			
		||||
msgid "Why not get some ready to shoot?"
 | 
			
		||||
msgstr "¿Por qué no preparar parte para disparar?"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:125
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:103
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:127
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:96
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:45
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Record shots"
 | 
			
		||||
msgstr "Tiros récord"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:90
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:88
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add another container!"
 | 
			
		||||
msgstr "¡Añade otro contenedor!"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:126
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:124
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Select"
 | 
			
		||||
msgstr "Seleccionar"
 | 
			
		||||
@@ -191,12 +186,12 @@ msgstr "Seleccionar"
 | 
			
		||||
msgid "Copy to clipboard"
 | 
			
		||||
msgstr "Copiar al portapapeles"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:14
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:14
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "add a container first"
 | 
			
		||||
msgstr "añade primero un contenedor"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:77
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:84
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Create"
 | 
			
		||||
msgstr "Crear"
 | 
			
		||||
@@ -211,19 +206,14 @@ msgstr "Cambiar Lenguaje"
 | 
			
		||||
msgid "Change language"
 | 
			
		||||
msgstr "Cambiar lenguaje"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:55
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:55
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "View in Catalog"
 | 
			
		||||
msgstr "Ver en Catalogo"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:24
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "add an ammo type first"
 | 
			
		||||
msgstr "añade primero un tipo de munición"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:80
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:142
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:96
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:78
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:144
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:89
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Move ammo"
 | 
			
		||||
msgstr "Mover munición"
 | 
			
		||||
@@ -233,13 +223,13 @@ msgstr "Mover munición"
 | 
			
		||||
msgid "Set Unlimited"
 | 
			
		||||
msgstr "Activar ilimitados"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:89
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:85
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:38
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Stage for range"
 | 
			
		||||
msgstr "Preparar para el campo de tiro"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:88
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:84
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:37
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Unstage from range"
 | 
			
		||||
@@ -250,11 +240,6 @@ msgstr "Desmontar del campo de tiro"
 | 
			
		||||
msgid "Export Data as JSON"
 | 
			
		||||
msgstr "Exportar datos como JSON"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:110
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Clone %{ammo_type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:87
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:145
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
@@ -266,12 +251,6 @@ msgstr ""
 | 
			
		||||
msgid "Copy invite link for %{invite_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:129
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:36
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Delete %{ammo_type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:104
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:162
 | 
			
		||||
#: lib/cannery_web/live/container_live/show.html.heex:48
 | 
			
		||||
@@ -289,18 +268,6 @@ msgstr ""
 | 
			
		||||
msgid "Delete invite for %{invite_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:161
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:155
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Delete shot record of %{shot_group_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:100
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:19
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit %{ammo_type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:77
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:135
 | 
			
		||||
#: lib/cannery_web/live/container_live/show.html.heex:35
 | 
			
		||||
@@ -313,28 +280,12 @@ msgstr ""
 | 
			
		||||
msgid "Edit %{tag_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:164
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:62
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/invite_live/index.html.heex:46
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit invite for %{invite_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:146
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit shot group of %{shot_group_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:138
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit shot record of %{shot_group_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:118
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:120
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Stage"
 | 
			
		||||
msgstr "Preparar munición"
 | 
			
		||||
@@ -345,29 +296,74 @@ msgstr "Preparar munición"
 | 
			
		||||
msgid "Tag %{container_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:117
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:119
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Unstage"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:90
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "View %{ammo_type_name}"
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:174
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Clone pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:176
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:189
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:74
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Clone ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgid "Delete pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:191
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:76
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:164
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:62
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Delete ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgid "Edit pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:152
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:206
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:154
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:204
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "View ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgid "View pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:160
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:155
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Delete shot record of %{shot_record_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:145
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:138
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Edit shot record of %{shot_record_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:105
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Clone %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:122
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:35
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Delete %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:97
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:19
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Edit %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:17
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "New Type"
 | 
			
		||||
msgstr "Nuevo tipo de Munición"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:89
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "View %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:24
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "add a type first"
 | 
			
		||||
msgstr "añade primero un tipo de munición"
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -69,7 +69,7 @@ msgstr "Correo o contraseña incorrecta"
 | 
			
		||||
msgid "Not found"
 | 
			
		||||
msgstr "No se encontró"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:18
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.html.heex:18
 | 
			
		||||
#: lib/cannery_web/templates/user_registration/new.html.heex:13
 | 
			
		||||
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:13
 | 
			
		||||
#: lib/cannery_web/templates/user_settings/edit.html.heex:22
 | 
			
		||||
@@ -161,52 +161,52 @@ msgstr "Debe confirmar su cuenta e iniciar sesión para acceder a esta página."
 | 
			
		||||
msgid "Tag could not be removed"
 | 
			
		||||
msgstr "La etiqueta no pudo ser eliminada"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:160
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:159
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Could not parse number of copies"
 | 
			
		||||
msgstr "No se ha podido procesar el número de copias"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:150
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:149
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}"
 | 
			
		||||
msgstr "Número inválido de copias, debe ser entre 1 y %{max}. Fue %{multiplier"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/ammo.ex:1114
 | 
			
		||||
#: lib/cannery/ammo.ex:1121
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Invalid multiplier"
 | 
			
		||||
msgstr "Multiplicador inválido"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/ammo/ammo_group.ex:92
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Please select an ammo type and container"
 | 
			
		||||
msgstr "Por favor escoja un tipo de munición y un contenedor"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:74
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Your browser does not support the canvas element."
 | 
			
		||||
msgstr "Su navegador no es compatible con el elemento lienzo."
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:74
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:74
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Please select a valid user and ammo pack"
 | 
			
		||||
msgstr "Por favor escoja un usuario y tipo de munición valido"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:88
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:88
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo left can be at most %{count} rounds"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:84
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:84
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo left must be at least 0"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:122
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:119
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Count can be at most %{count} shots"
 | 
			
		||||
msgstr "El recuento debe ser menos de %{count}"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:80
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:80
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "can't be blank"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/ammo/pack.ex:100
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Please select a type and container"
 | 
			
		||||
msgstr "Por favor escoja un tipo de munición y un contenedor"
 | 
			
		||||
 
 | 
			
		||||
@@ -23,17 +23,17 @@ msgstr ""
 | 
			
		||||
## 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.
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.ex:89
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.ex:89
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.ex:80
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.ex:78
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.ex:88
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "%{name} created successfully"
 | 
			
		||||
msgstr "%{name} creado exitosamente"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.ex:72
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.ex:27
 | 
			
		||||
#: lib/cannery_web/live/tag_live/index.ex:65
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.ex:72
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.ex:27
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "%{name} deleted succesfully"
 | 
			
		||||
msgstr "%{name} borrado exitosamente"
 | 
			
		||||
@@ -44,10 +44,10 @@ msgstr "%{name} borrado exitosamente"
 | 
			
		||||
msgid "%{name} has been deleted"
 | 
			
		||||
msgstr "%{name} ha sido borrado"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.ex:70
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.ex:70
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.ex:62
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.ex:60
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.ex:69
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "%{name} updated successfully"
 | 
			
		||||
msgstr "%{name} actualizado exitosamente"
 | 
			
		||||
@@ -73,8 +73,8 @@ msgstr "Está seguro que desea eliminar %{email}? Esta acción es permanente!"
 | 
			
		||||
msgid "Are you sure you want to delete %{name}?"
 | 
			
		||||
msgstr "Está seguro que desea eliminar %{name}?"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:189
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:74
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:187
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:72
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Are you sure you want to delete this ammo?"
 | 
			
		||||
msgstr "Está seguro que desea eliminar esta munición?"
 | 
			
		||||
@@ -128,13 +128,13 @@ msgstr "Contraseña cambiada exitosamente."
 | 
			
		||||
msgid "Please check your email to verify your account"
 | 
			
		||||
msgstr "Por favor chequea el correo para verificar tu cuenta"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/add_shot_group_component.html.heex:59
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:85
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:351
 | 
			
		||||
#: lib/cannery_web/components/add_shot_record_component.html.heex:59
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.html.heex:59
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.html.heex:37
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:92
 | 
			
		||||
#: lib/cannery_web/live/range_live/form_component.html.heex:47
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.html.heex:39
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.html.heex:351
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Saving..."
 | 
			
		||||
msgstr "Guardando..."
 | 
			
		||||
@@ -165,7 +165,7 @@ msgstr "se ha removido %{tag_name} de %{container_name}"
 | 
			
		||||
msgid "Adding..."
 | 
			
		||||
msgstr "Añadiendo..."
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/add_shot_group_component.ex:60
 | 
			
		||||
#: lib/cannery_web/components/add_shot_record_component.ex:60
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Shots recorded successfully"
 | 
			
		||||
msgstr "Tiros registrados exitosamente"
 | 
			
		||||
@@ -175,13 +175,13 @@ msgstr "Tiros registrados exitosamente"
 | 
			
		||||
msgid "Are you sure you want to unstage this ammo?"
 | 
			
		||||
msgstr "Está seguro que desea desmontar esta munición?"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:159
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:158
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:152
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Are you sure you want to delete this shot record?"
 | 
			
		||||
msgstr "¿Está segure que quiere borrar este récord de disparos?"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:81
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:80
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.ex:79
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Shot records deleted succesfully"
 | 
			
		||||
@@ -197,7 +197,7 @@ msgstr "Récord de disparos actualizado exitosamente"
 | 
			
		||||
msgid "%{email} confirmed successfully."
 | 
			
		||||
msgstr "%{email} confirmado exitosamente."
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:54
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:52
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo moved to %{name} successfully"
 | 
			
		||||
msgstr "Munición movida a %{name} exitosamente"
 | 
			
		||||
@@ -212,13 +212,13 @@ msgstr "Copiado al portapapeles"
 | 
			
		||||
msgid "%{name} removed successfully"
 | 
			
		||||
msgstr "%{name} eliminado exitosamente"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:10
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:20
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:10
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:20
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "You'll need to"
 | 
			
		||||
msgstr "Necesitará hacerlo"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:78
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:85
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Creating..."
 | 
			
		||||
msgstr "Creando..."
 | 
			
		||||
@@ -233,31 +233,31 @@ msgstr "¿Está segure de que quiere cambiar el idioma?"
 | 
			
		||||
msgid "Language updated successfully."
 | 
			
		||||
msgstr "Idioma cambiado exitosamente."
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.ex:89
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:55
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.ex:94
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:55
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo deleted succesfully"
 | 
			
		||||
msgstr "Munición borrada exitosamente"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.ex:93
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.ex:92
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Ammo unstaged succesfully"
 | 
			
		||||
msgstr "Munición descargada exitosamente"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:126
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:125
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo updated successfully"
 | 
			
		||||
msgstr "Munición actualizada exitosamente"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:185
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:184
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo added successfully"
 | 
			
		||||
msgid_plural "Ammo added successfully"
 | 
			
		||||
msgstr[0] "Munición añadida exitosamente"
 | 
			
		||||
msgstr[1] "Municiones añadidas exitosamente"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:122
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:29
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:116
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:29
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Are you sure you want to delete %{name}? This will delete all %{name} type ammo as well!"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 
 | 
			
		||||
@@ -23,14 +23,14 @@ msgstr ""
 | 
			
		||||
# # 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.
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.ex:54
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.ex:62
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:38
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.ex:59
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.ex:67
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:38
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add Ammo"
 | 
			
		||||
msgstr "ajouter munition"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:34
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:34
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add your first box!"
 | 
			
		||||
msgstr "Ajoutez votre première caisse !"
 | 
			
		||||
@@ -40,7 +40,7 @@ msgstr "Ajoutez votre première caisse !"
 | 
			
		||||
msgid "Add your first container!"
 | 
			
		||||
msgstr "Ajoutez votre premier conteneur !"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:13
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:13
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add your first type!"
 | 
			
		||||
msgstr "Ajoutez votre premier type !"
 | 
			
		||||
@@ -95,11 +95,6 @@ msgstr "Se connecter"
 | 
			
		||||
msgid "Make your first tag!"
 | 
			
		||||
msgstr "Faîtes votre premier tag !"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:17
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "New Ammo type"
 | 
			
		||||
msgstr "Nouveau type de munition"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:17
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "New Container"
 | 
			
		||||
@@ -133,13 +128,13 @@ msgstr "Renvoyer les instructions de confirmation"
 | 
			
		||||
msgid "Reset password"
 | 
			
		||||
msgstr "Réinitialisé le mot de passe"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/add_shot_group_component.html.heex:57
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:84
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:350
 | 
			
		||||
#: lib/cannery_web/components/add_shot_record_component.html.heex:57
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.html.heex:57
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.html.heex:35
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:91
 | 
			
		||||
#: lib/cannery_web/live/range_live/form_component.html.heex:45
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.html.heex:37
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.html.heex:350
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Save"
 | 
			
		||||
msgstr "Sauvegarder"
 | 
			
		||||
@@ -169,19 +164,19 @@ msgstr "Munition préparée"
 | 
			
		||||
msgid "Why not get some ready to shoot?"
 | 
			
		||||
msgstr "Pourquoi pas en préparer pour tirer ?"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:125
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:103
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:127
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:96
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:45
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Record shots"
 | 
			
		||||
msgstr "Enregistrer des tirs"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:90
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:88
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add another container!"
 | 
			
		||||
msgstr "Ajoutez un autre conteneur !"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:126
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:124
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Select"
 | 
			
		||||
msgstr "Sélectionner"
 | 
			
		||||
@@ -191,12 +186,12 @@ msgstr "Sélectionner"
 | 
			
		||||
msgid "Copy to clipboard"
 | 
			
		||||
msgstr "Copier dans le presse-papier"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:14
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:14
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "add a container first"
 | 
			
		||||
msgstr "ajouter un conteneur en premier"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:77
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:84
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Create"
 | 
			
		||||
msgstr "Créer"
 | 
			
		||||
@@ -211,19 +206,14 @@ msgstr "Changer la langue"
 | 
			
		||||
msgid "Change language"
 | 
			
		||||
msgstr "Changer la langue"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:55
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:55
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "View in Catalog"
 | 
			
		||||
msgstr "Voir en catalogue"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:24
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "add an ammo type first"
 | 
			
		||||
msgstr "Ajoutez d'abord un type de munitions"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:80
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:142
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:96
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:78
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:144
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:89
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Move ammo"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -233,13 +223,13 @@ msgstr ""
 | 
			
		||||
msgid "Set Unlimited"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:89
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:85
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:38
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Stage for range"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:88
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:84
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:37
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Unstage from range"
 | 
			
		||||
@@ -250,11 +240,6 @@ msgstr ""
 | 
			
		||||
msgid "Export Data as JSON"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:110
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Clone %{ammo_type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:87
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:145
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
@@ -266,12 +251,6 @@ msgstr ""
 | 
			
		||||
msgid "Copy invite link for %{invite_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:129
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:36
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Delete %{ammo_type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:104
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:162
 | 
			
		||||
#: lib/cannery_web/live/container_live/show.html.heex:48
 | 
			
		||||
@@ -289,18 +268,6 @@ msgstr ""
 | 
			
		||||
msgid "Delete invite for %{invite_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:161
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:155
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Delete shot record of %{shot_group_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:100
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:19
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit %{ammo_type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:77
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:135
 | 
			
		||||
#: lib/cannery_web/live/container_live/show.html.heex:35
 | 
			
		||||
@@ -313,28 +280,12 @@ msgstr ""
 | 
			
		||||
msgid "Edit %{tag_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:164
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:62
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/invite_live/index.html.heex:46
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit invite for %{invite_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:146
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit shot group of %{shot_group_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:138
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit shot record of %{shot_group_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:118
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:120
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Stage"
 | 
			
		||||
msgstr "Munition préparée"
 | 
			
		||||
@@ -345,29 +296,74 @@ msgstr "Munition préparée"
 | 
			
		||||
msgid "Tag %{container_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:117
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:119
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Unstage"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:90
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "View %{ammo_type_name}"
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:174
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Clone pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:176
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:189
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:74
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Clone ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgid "Delete pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:191
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:76
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:164
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:62
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Delete ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgid "Edit pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:152
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:206
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:154
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:204
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "View ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgid "View pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:160
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:155
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Delete shot record of %{shot_record_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:145
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:138
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Edit shot record of %{shot_record_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:105
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Clone %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:122
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:35
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Delete %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:97
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:19
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Edit %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:17
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "New Type"
 | 
			
		||||
msgstr "Nouveau type de munition"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:89
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "View %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:24
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "add a type first"
 | 
			
		||||
msgstr "Ajoutez d'abord un type de munitions"
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -69,7 +69,7 @@ msgstr "Mél ou mot de passe invalide"
 | 
			
		||||
msgid "Not found"
 | 
			
		||||
msgstr "Pas trouvé"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:18
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.html.heex:18
 | 
			
		||||
#: lib/cannery_web/templates/user_registration/new.html.heex:13
 | 
			
		||||
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:13
 | 
			
		||||
#: lib/cannery_web/templates/user_settings/edit.html.heex:22
 | 
			
		||||
@@ -162,52 +162,52 @@ msgstr ""
 | 
			
		||||
msgid "Tag could not be removed"
 | 
			
		||||
msgstr "Le tag n’a pas pu être retiré"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:160
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:159
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Could not parse number of copies"
 | 
			
		||||
msgstr "Impossible d'analyser le nombre de copies"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:150
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:149
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}"
 | 
			
		||||
msgstr "Nombre de copies invalide, doit être 1 et %{max}. Été %{multiplier}"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/ammo.ex:1114
 | 
			
		||||
#: lib/cannery/ammo.ex:1121
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Invalid multiplier"
 | 
			
		||||
msgstr "Multiplicateur invalide"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/ammo/ammo_group.ex:92
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Please select an ammo type and container"
 | 
			
		||||
msgstr "Veuillez choisir un type de munitions et un conteneur"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:74
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Your browser does not support the canvas element."
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:74
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:74
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Please select a valid user and ammo pack"
 | 
			
		||||
msgstr "Veuillez choisir un utilisateur valide et un groupe de munitions"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:88
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:88
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo left can be at most %{count} rounds"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:84
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:84
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo left must be at least 0"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:122
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:119
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Count can be at most %{count} shots"
 | 
			
		||||
msgstr "La quantité doit être inférieur à %{count}"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:80
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:80
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "can't be blank"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/ammo/pack.ex:100
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Please select a type and container"
 | 
			
		||||
msgstr "Veuillez choisir un type de munitions et un conteneur"
 | 
			
		||||
 
 | 
			
		||||
@@ -23,17 +23,17 @@ msgstr ""
 | 
			
		||||
## 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.
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.ex:89
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.ex:89
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.ex:80
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.ex:78
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.ex:88
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "%{name} created successfully"
 | 
			
		||||
msgstr "%{name} créé· avec succès"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.ex:72
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.ex:27
 | 
			
		||||
#: lib/cannery_web/live/tag_live/index.ex:65
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.ex:72
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.ex:27
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "%{name} deleted succesfully"
 | 
			
		||||
msgstr "%{name} supprimé· avec succès"
 | 
			
		||||
@@ -44,10 +44,10 @@ msgstr "%{name} supprimé· avec succès"
 | 
			
		||||
msgid "%{name} has been deleted"
 | 
			
		||||
msgstr "%{name} a été supprimé·e"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.ex:70
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.ex:70
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.ex:62
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.ex:60
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.ex:69
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "%{name} updated successfully"
 | 
			
		||||
msgstr "%{name} mis à jour avec succès"
 | 
			
		||||
@@ -74,8 +74,8 @@ msgstr ""
 | 
			
		||||
msgid "Are you sure you want to delete %{name}?"
 | 
			
		||||
msgstr "Êtes-vous certain·e de supprimer %{name} ?"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:189
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:74
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:187
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:72
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Are you sure you want to delete this ammo?"
 | 
			
		||||
msgstr "Êtes-vous certain·e de supprimer cette munition ?"
 | 
			
		||||
@@ -129,13 +129,13 @@ msgstr "Mot de passe mis à jour avec succès."
 | 
			
		||||
msgid "Please check your email to verify your account"
 | 
			
		||||
msgstr "Veuillez vérifier votre mél pour confirmer votre compte"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/add_shot_group_component.html.heex:59
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:85
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:351
 | 
			
		||||
#: lib/cannery_web/components/add_shot_record_component.html.heex:59
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.html.heex:59
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.html.heex:37
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:92
 | 
			
		||||
#: lib/cannery_web/live/range_live/form_component.html.heex:47
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.html.heex:39
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.html.heex:351
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Saving..."
 | 
			
		||||
msgstr "Sauvegarde en cours…"
 | 
			
		||||
@@ -167,7 +167,7 @@ msgstr "%{tag_name} a été retiré de %{container_name}"
 | 
			
		||||
msgid "Adding..."
 | 
			
		||||
msgstr "Ajout en cours…"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/add_shot_group_component.ex:60
 | 
			
		||||
#: lib/cannery_web/components/add_shot_record_component.ex:60
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Shots recorded successfully"
 | 
			
		||||
msgstr "Tirs enregistré avec succès"
 | 
			
		||||
@@ -177,13 +177,13 @@ msgstr "Tirs enregistré avec succès"
 | 
			
		||||
msgid "Are you sure you want to unstage this ammo?"
 | 
			
		||||
msgstr "Êtes-vous certain·e de vouloir désélectionner cette munition ?"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:159
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:158
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:152
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Are you sure you want to delete this shot record?"
 | 
			
		||||
msgstr "Êtes-vous certain·e de vouloir supprimer cet enregistrement de tir ?"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:81
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:80
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.ex:79
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Shot records deleted succesfully"
 | 
			
		||||
@@ -199,7 +199,7 @@ msgstr "Enregistrements de tir mis à jour avec succès"
 | 
			
		||||
msgid "%{email} confirmed successfully."
 | 
			
		||||
msgstr "%{email} confirmé avec succès."
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:54
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:52
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo moved to %{name} successfully"
 | 
			
		||||
msgstr "Munition déplacée à %{name} avec succès"
 | 
			
		||||
@@ -214,13 +214,13 @@ msgstr "Copié dans le presse-papier"
 | 
			
		||||
msgid "%{name} removed successfully"
 | 
			
		||||
msgstr "%{name} retiré avec succès"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:10
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:20
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:10
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:20
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "You'll need to"
 | 
			
		||||
msgstr "Vous aurez besoin de"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:78
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:85
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Creating..."
 | 
			
		||||
msgstr "Création en cours…"
 | 
			
		||||
@@ -235,31 +235,31 @@ msgstr "Êtes-vous certain·e de vouloir changer votre langue ?"
 | 
			
		||||
msgid "Language updated successfully."
 | 
			
		||||
msgstr "Langue mise à jour avec succès."
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.ex:89
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:55
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.ex:94
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:55
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Ammo deleted succesfully"
 | 
			
		||||
msgstr "Groupe de munition supprimé avec succès"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.ex:93
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.ex:92
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Ammo unstaged succesfully"
 | 
			
		||||
msgstr "Groupe de munition désélectionner avec succès"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:126
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:125
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Ammo updated successfully"
 | 
			
		||||
msgstr "Groupe de munition mis à jour avec succès"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:185
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:184
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Ammo added successfully"
 | 
			
		||||
msgid_plural "Ammo added successfully"
 | 
			
		||||
msgstr[0] "Groupe de munition mis à jour avec succès"
 | 
			
		||||
msgstr[1] "Groupe de munition mis à jour avec succès"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:122
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:29
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:116
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:29
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Are you sure you want to delete %{name}? This will delete all %{name} type ammo as well!"
 | 
			
		||||
msgstr "Êtes-vous certain·e de supprimer %{name} ?"
 | 
			
		||||
 
 | 
			
		||||
@@ -21,14 +21,14 @@ msgstr ""
 | 
			
		||||
## 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.
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.ex:54
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.ex:62
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:38
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.ex:59
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.ex:67
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:38
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add Ammo"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:34
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:34
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add your first box!"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -38,7 +38,7 @@ msgstr ""
 | 
			
		||||
msgid "Add your first container!"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:13
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:13
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add your first type!"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -93,11 +93,6 @@ msgstr ""
 | 
			
		||||
msgid "Make your first tag!"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:17
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "New Ammo type"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:17
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "New Container"
 | 
			
		||||
@@ -131,13 +126,13 @@ msgstr ""
 | 
			
		||||
msgid "Reset password"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/add_shot_group_component.html.heex:57
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:84
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:350
 | 
			
		||||
#: lib/cannery_web/components/add_shot_record_component.html.heex:57
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.html.heex:57
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.html.heex:35
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:91
 | 
			
		||||
#: lib/cannery_web/live/range_live/form_component.html.heex:45
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.html.heex:37
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.html.heex:350
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Save"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -167,19 +162,19 @@ msgstr ""
 | 
			
		||||
msgid "Why not get some ready to shoot?"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:125
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:103
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:127
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:96
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:45
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Record shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:90
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:88
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Add another container!"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:126
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:124
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Select"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -189,12 +184,12 @@ msgstr ""
 | 
			
		||||
msgid "Copy to clipboard"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:14
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:14
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "add a container first"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:77
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:84
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Create"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -209,19 +204,14 @@ msgstr ""
 | 
			
		||||
msgid "Change language"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:55
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:55
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "View in Catalog"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:24
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "add an ammo type first"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:80
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:142
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:96
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:78
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:144
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:89
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Move ammo"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -231,13 +221,13 @@ msgstr ""
 | 
			
		||||
msgid "Set Unlimited"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:89
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:85
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:38
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Stage for range"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:88
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:84
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:37
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Unstage from range"
 | 
			
		||||
@@ -248,11 +238,6 @@ msgstr ""
 | 
			
		||||
msgid "Export Data as JSON"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:110
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Clone %{ammo_type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:87
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:145
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
@@ -264,12 +249,6 @@ msgstr ""
 | 
			
		||||
msgid "Copy invite link for %{invite_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:129
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:36
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Delete %{ammo_type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:104
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:162
 | 
			
		||||
#: lib/cannery_web/live/container_live/show.html.heex:48
 | 
			
		||||
@@ -287,18 +266,6 @@ msgstr ""
 | 
			
		||||
msgid "Delete invite for %{invite_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:161
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:155
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Delete shot record of %{shot_group_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:100
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:19
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit %{ammo_type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:77
 | 
			
		||||
#: lib/cannery_web/live/container_live/index.html.heex:135
 | 
			
		||||
#: lib/cannery_web/live/container_live/show.html.heex:35
 | 
			
		||||
@@ -311,28 +278,12 @@ msgstr ""
 | 
			
		||||
msgid "Edit %{tag_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:164
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:62
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/invite_live/index.html.heex:46
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit invite for %{invite_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:146
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit shot group of %{shot_group_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:138
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Edit shot record of %{shot_group_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:118
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:120
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Stage"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -343,29 +294,74 @@ msgstr ""
 | 
			
		||||
msgid "Tag %{container_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:117
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:119
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Unstage"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:90
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "View %{ammo_type_name}"
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:174
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Clone pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:176
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:189
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:74
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Clone ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgid "Delete pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:191
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:76
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:164
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:62
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Delete ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgid "Edit pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:152
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:206
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:154
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:204
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "View ammo group of %{ammo_group_count} bullets"
 | 
			
		||||
msgid "View pack of %{pack_count} bullets"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:160
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:155
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Delete shot record of %{shot_record_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:145
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:138
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Edit shot record of %{shot_record_count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:105
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Clone %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:122
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:35
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Delete %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:97
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:19
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Edit %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:17
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "New Type"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:89
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "View %{type_name}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:24
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "add a type first"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -70,7 +70,7 @@ msgstr "Seoladh email nó pasfhocal neamhbhailí"
 | 
			
		||||
msgid "Not found"
 | 
			
		||||
msgstr "Ní feidir é a fáil"
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:18
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.html.heex:18
 | 
			
		||||
#: lib/cannery_web/templates/user_registration/new.html.heex:13
 | 
			
		||||
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:13
 | 
			
		||||
#: lib/cannery_web/templates/user_settings/edit.html.heex:22
 | 
			
		||||
@@ -161,52 +161,52 @@ msgstr ""
 | 
			
		||||
msgid "Tag could not be removed"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:160
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:159
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Could not parse number of copies"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:150
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:149
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/ammo.ex:1114
 | 
			
		||||
#: lib/cannery/ammo.ex:1121
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Invalid multiplier"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/ammo/ammo_group.ex:92
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Please select an ammo type and container"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:74
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Your browser does not support the canvas element."
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:74
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:74
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Please select a valid user and ammo pack"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:88
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:88
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo left can be at most %{count} rounds"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:84
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:84
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo left must be at least 0"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:122
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:119
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Count can be at most %{count} shots"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/activity_log/shot_group.ex:80
 | 
			
		||||
#: lib/cannery/activity_log/shot_record.ex:80
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "can't be blank"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery/ammo/pack.ex:100
 | 
			
		||||
#, elixir-autogen, elixir-format, fuzzy
 | 
			
		||||
msgid "Please select a type and container"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 
 | 
			
		||||
@@ -21,17 +21,17 @@ msgstr ""
 | 
			
		||||
## 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.
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.ex:89
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.ex:89
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.ex:80
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.ex:78
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.ex:88
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "%{name} created successfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.ex:72
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.ex:27
 | 
			
		||||
#: lib/cannery_web/live/tag_live/index.ex:65
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.ex:72
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.ex:27
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "%{name} deleted succesfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -42,10 +42,10 @@ msgstr ""
 | 
			
		||||
msgid "%{name} has been deleted"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.ex:70
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.ex:70
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.ex:62
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.ex:60
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.ex:69
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "%{name} updated successfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -69,8 +69,8 @@ msgstr ""
 | 
			
		||||
msgid "Are you sure you want to delete %{name}?"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:189
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:74
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:187
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:72
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Are you sure you want to delete this ammo?"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -120,13 +120,13 @@ msgstr ""
 | 
			
		||||
msgid "Please check your email to verify your account"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/add_shot_group_component.html.heex:59
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:85
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:351
 | 
			
		||||
#: lib/cannery_web/components/add_shot_record_component.html.heex:59
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.html.heex:59
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.html.heex:37
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:92
 | 
			
		||||
#: lib/cannery_web/live/range_live/form_component.html.heex:47
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.html.heex:39
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.html.heex:351
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Saving..."
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -156,7 +156,7 @@ msgstr ""
 | 
			
		||||
msgid "Adding..."
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/add_shot_group_component.ex:60
 | 
			
		||||
#: lib/cannery_web/components/add_shot_record_component.ex:60
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Shots recorded successfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -166,13 +166,13 @@ msgstr ""
 | 
			
		||||
msgid "Are you sure you want to unstage this ammo?"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:159
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:158
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:152
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Are you sure you want to delete this shot record?"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:81
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:80
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.ex:79
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Shot records deleted succesfully"
 | 
			
		||||
@@ -188,7 +188,7 @@ msgstr ""
 | 
			
		||||
msgid "%{email} confirmed successfully."
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:54
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:52
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo moved to %{name} successfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -203,13 +203,13 @@ msgstr ""
 | 
			
		||||
msgid "%{name} removed successfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:10
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:20
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:10
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:20
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "You'll need to"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:78
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:85
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Creating..."
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -224,23 +224,23 @@ msgstr ""
 | 
			
		||||
msgid "Language updated successfully."
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.ex:89
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:55
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.ex:94
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:55
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo deleted succesfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.ex:93
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.ex:92
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo unstaged succesfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:126
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:125
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo updated successfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:185
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:184
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo added successfully"
 | 
			
		||||
msgid_plural "Ammo added successfully"
 | 
			
		||||
@@ -250,8 +250,8 @@ msgstr[2] ""
 | 
			
		||||
msgstr[3] ""
 | 
			
		||||
msgstr[4] ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:122
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:29
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:116
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:29
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Are you sure you want to delete %{name}? This will delete all %{name} type ammo as well!"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 
 | 
			
		||||
@@ -10,17 +10,17 @@
 | 
			
		||||
msgid ""
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.ex:89
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.ex:89
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.ex:80
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.ex:78
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.ex:88
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "%{name} created successfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.ex:72
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.ex:27
 | 
			
		||||
#: lib/cannery_web/live/tag_live/index.ex:65
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.ex:72
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.ex:27
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "%{name} deleted succesfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -31,10 +31,10 @@ msgstr ""
 | 
			
		||||
msgid "%{name} has been deleted"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.ex:70
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.ex:70
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.ex:62
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.ex:60
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.ex:69
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "%{name} updated successfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -58,8 +58,8 @@ msgstr ""
 | 
			
		||||
msgid "Are you sure you want to delete %{name}?"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:189
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:74
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:187
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.html.heex:72
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Are you sure you want to delete this ammo?"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -109,13 +109,13 @@ msgstr ""
 | 
			
		||||
msgid "Please check your email to verify your account"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/add_shot_group_component.html.heex:59
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:85
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:351
 | 
			
		||||
#: lib/cannery_web/components/add_shot_record_component.html.heex:59
 | 
			
		||||
#: lib/cannery_web/live/container_live/form_component.html.heex:59
 | 
			
		||||
#: lib/cannery_web/live/invite_live/form_component.html.heex:37
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:92
 | 
			
		||||
#: lib/cannery_web/live/range_live/form_component.html.heex:47
 | 
			
		||||
#: lib/cannery_web/live/tag_live/form_component.html.heex:39
 | 
			
		||||
#: lib/cannery_web/live/type_live/form_component.html.heex:351
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Saving..."
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -145,7 +145,7 @@ msgstr ""
 | 
			
		||||
msgid "Adding..."
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/add_shot_group_component.ex:60
 | 
			
		||||
#: lib/cannery_web/components/add_shot_record_component.ex:60
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Shots recorded successfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -155,13 +155,13 @@ msgstr ""
 | 
			
		||||
msgid "Are you sure you want to unstage this ammo?"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:159
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:158
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.html.heex:152
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Are you sure you want to delete this shot record?"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:81
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:80
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.ex:79
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Shot records deleted succesfully"
 | 
			
		||||
@@ -177,7 +177,7 @@ msgstr ""
 | 
			
		||||
msgid "%{email} confirmed successfully."
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/components/move_ammo_group_component.ex:54
 | 
			
		||||
#: lib/cannery_web/components/move_pack_component.ex:52
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo moved to %{name} successfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -192,13 +192,13 @@ msgstr ""
 | 
			
		||||
msgid "%{name} removed successfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:10
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:20
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:10
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.html.heex:20
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "You'll need to"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:78
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.html.heex:85
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Creating..."
 | 
			
		||||
msgstr ""
 | 
			
		||||
@@ -213,31 +213,31 @@ msgstr ""
 | 
			
		||||
msgid "Language updated successfully."
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/index.ex:89
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/show.ex:55
 | 
			
		||||
#: lib/cannery_web/live/pack_live/index.ex:94
 | 
			
		||||
#: lib/cannery_web/live/pack_live/show.ex:55
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo deleted succesfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.ex:93
 | 
			
		||||
#: lib/cannery_web/live/range_live/index.ex:92
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo unstaged succesfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:126
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:125
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo updated successfully"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:185
 | 
			
		||||
#: lib/cannery_web/live/pack_live/form_component.ex:184
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Ammo added successfully"
 | 
			
		||||
msgid_plural "Ammo added successfully"
 | 
			
		||||
msgstr[0] ""
 | 
			
		||||
msgstr[1] ""
 | 
			
		||||
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:122
 | 
			
		||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:29
 | 
			
		||||
#: lib/cannery_web/live/type_live/index.html.heex:116
 | 
			
		||||
#: lib/cannery_web/live/type_live/show.html.heex:29
 | 
			
		||||
#, elixir-autogen, elixir-format
 | 
			
		||||
msgid "Are you sure you want to delete %{name}? This will delete all %{name} type ammo as well!"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										105
									
								
								priv/repo/migrations/20230329025816_rename_type_to_class.exs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								priv/repo/migrations/20230329025816_rename_type_to_class.exs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,105 @@
 | 
			
		||||
defmodule Cannery.Repo.Migrations.RenameTypeToClass do
 | 
			
		||||
  use Ecto.Migration
 | 
			
		||||
 | 
			
		||||
  def up do
 | 
			
		||||
    rename table(:ammo_types), :type, to: :class
 | 
			
		||||
 | 
			
		||||
    alter table(:ammo_types) do
 | 
			
		||||
      remove_if_exists :search, :tsvector
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    flush()
 | 
			
		||||
 | 
			
		||||
    execute """
 | 
			
		||||
    ALTER TABLE ammo_types
 | 
			
		||||
      ADD COLUMN search tsvector
 | 
			
		||||
        GENERATED ALWAYS AS (
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("name", '')), 'A') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("desc", '')), 'B') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("class", '')), 'B') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("manufacturer", '')), 'C') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("upc", '')), 'C') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("bullet_type", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("bullet_core", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("cartridge", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("caliber", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("case_material", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("jacket_type", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', immutable_to_string("muzzle_velocity", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("powder_type", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', immutable_to_string("powder_grains_per_charge", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', immutable_to_string("grains", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("pressure", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("primer_type", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("firing_type", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("wadding", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("shot_type", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("shot_material", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("shot_size", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("unfired_length", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("brass_height", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("chamber_size", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', immutable_to_string("load_grains", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("shot_charge_weight", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("dram_equivalent", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', boolean_to_string("tracer", 'tracer', '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', boolean_to_string("incendiary", 'incendiary', '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', boolean_to_string("blank", 'blank', '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', boolean_to_string("corrosive", 'corrosive', '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("manufacturer", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("upc", '')), 'D')
 | 
			
		||||
      ) STORED
 | 
			
		||||
    """
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def down do
 | 
			
		||||
    rename table(:ammo_types), :class, to: :type
 | 
			
		||||
 | 
			
		||||
    alter table(:ammo_types) do
 | 
			
		||||
      remove_if_exists :search, :tsvector
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    flush()
 | 
			
		||||
 | 
			
		||||
    execute """
 | 
			
		||||
    ALTER TABLE ammo_types
 | 
			
		||||
      ADD COLUMN search TSVECTOR
 | 
			
		||||
        GENERATED ALWAYS AS (
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("name", '')), 'A') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("desc", '')), 'B') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("type", '')), 'B') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("manufacturer", '')), 'C') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("upc", '')), 'C') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("bullet_type", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("bullet_core", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("cartridge", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("caliber", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("case_material", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("jacket_type", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', immutable_to_string("muzzle_velocity", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("powder_type", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', immutable_to_string("powder_grains_per_charge", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', immutable_to_string("grains", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("pressure", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("primer_type", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("firing_type", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("wadding", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("shot_type", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("shot_material", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("shot_size", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("unfired_length", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("brass_height", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("chamber_size", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', immutable_to_string("load_grains", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("shot_charge_weight", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("dram_equivalent", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', boolean_to_string("tracer", 'tracer', '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', boolean_to_string("incendiary", 'incendiary', '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', boolean_to_string("blank", 'blank', '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', boolean_to_string("corrosive", 'corrosive', '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("manufacturer", '')), 'D') ||
 | 
			
		||||
        setweight(to_tsvector('english', coalesce("upc", '')), 'D')
 | 
			
		||||
      ) STORED
 | 
			
		||||
    """
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -0,0 +1,34 @@
 | 
			
		||||
defmodule Cannery.Repo.Migrations.RenameAmmoGroupsToPacks do
 | 
			
		||||
  use Ecto.Migration
 | 
			
		||||
 | 
			
		||||
  def up do
 | 
			
		||||
    drop index(:ammo_groups, [:user_id], where: "count = 0", name: :empty_ammo_groups_index)
 | 
			
		||||
    drop index(:shot_groups, [:user_id, :ammo_group_id])
 | 
			
		||||
 | 
			
		||||
    flush()
 | 
			
		||||
 | 
			
		||||
    rename table(:ammo_groups), to: table(:packs)
 | 
			
		||||
 | 
			
		||||
    flush()
 | 
			
		||||
 | 
			
		||||
    create index(:packs, [:user_id], where: "count = 0", name: :empty_packs_index)
 | 
			
		||||
    rename table(:shot_groups), :ammo_group_id, to: :pack_id
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def down do
 | 
			
		||||
    drop index(:packs, [:user_id], where: "count = 0", name: :empty_packs_index)
 | 
			
		||||
 | 
			
		||||
    flush()
 | 
			
		||||
 | 
			
		||||
    rename table(:packs), to: table(:ammo_groups)
 | 
			
		||||
 | 
			
		||||
    flush()
 | 
			
		||||
 | 
			
		||||
    create index(:ammo_groups, [:user_id], where: "count = 0", name: :empty_ammo_groups_index)
 | 
			
		||||
    rename table(:shot_groups), :pack_id, to: :ammo_group_id
 | 
			
		||||
 | 
			
		||||
    flush()
 | 
			
		||||
 | 
			
		||||
    create index(:shot_groups, [:user_id, :ammo_group_id])
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -0,0 +1,7 @@
 | 
			
		||||
defmodule Cannery.Repo.Migrations.RenameShotGroupsToShotRecords do
 | 
			
		||||
  use Ecto.Migration
 | 
			
		||||
 | 
			
		||||
  def change do
 | 
			
		||||
    rename table(:shot_groups), to: table(:shot_records)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -0,0 +1,8 @@
 | 
			
		||||
defmodule Cannery.Repo.Migrations.RenameAmmoTypeToType do
 | 
			
		||||
  use Ecto.Migration
 | 
			
		||||
 | 
			
		||||
  def change do
 | 
			
		||||
    rename table(:ammo_types), to: table(:types)
 | 
			
		||||
    rename table(:packs), :ammo_type_id, to: :type_id
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
defmodule Cannery.Repo.Migrations.AddLotNumberToPacks do
 | 
			
		||||
  use Ecto.Migration
 | 
			
		||||
 | 
			
		||||
  def change do
 | 
			
		||||
    alter table(:packs) do
 | 
			
		||||
      add :lot_number, :string
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -11,11 +11,11 @@ defmodule Cannery.InvitesTest do
 | 
			
		||||
  @moduletag :invites_test
 | 
			
		||||
 | 
			
		||||
  @valid_attrs %{
 | 
			
		||||
    "name" => "some name"
 | 
			
		||||
    name: "some name"
 | 
			
		||||
  }
 | 
			
		||||
  @invalid_attrs %{
 | 
			
		||||
    "name" => nil,
 | 
			
		||||
    "token" => nil
 | 
			
		||||
    name: nil,
 | 
			
		||||
    token: nil
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  describe "invites" do
 | 
			
		||||
@@ -57,7 +57,7 @@ defmodule Cannery.InvitesTest do
 | 
			
		||||
 | 
			
		||||
      assert {:ok, _user} =
 | 
			
		||||
               Accounts.register_user(
 | 
			
		||||
                 %{"email" => unique_user_email(), "password" => valid_user_password()},
 | 
			
		||||
                 %{email: unique_user_email(), password: valid_user_password()},
 | 
			
		||||
                 token
 | 
			
		||||
               )
 | 
			
		||||
 | 
			
		||||
@@ -65,7 +65,7 @@ defmodule Cannery.InvitesTest do
 | 
			
		||||
 | 
			
		||||
      assert {:ok, _user} =
 | 
			
		||||
               Accounts.register_user(
 | 
			
		||||
                 %{"email" => unique_user_email(), "password" => valid_user_password()},
 | 
			
		||||
                 %{email: unique_user_email(), password: valid_user_password()},
 | 
			
		||||
                 token
 | 
			
		||||
               )
 | 
			
		||||
 | 
			
		||||
@@ -81,13 +81,13 @@ defmodule Cannery.InvitesTest do
 | 
			
		||||
 | 
			
		||||
      assert {:ok, _user} =
 | 
			
		||||
               Accounts.register_user(
 | 
			
		||||
                 %{"email" => unique_user_email(), "password" => valid_user_password()},
 | 
			
		||||
                 %{email: unique_user_email(), password: valid_user_password()},
 | 
			
		||||
                 token
 | 
			
		||||
               )
 | 
			
		||||
 | 
			
		||||
      assert {:ok, _user} =
 | 
			
		||||
               Accounts.register_user(
 | 
			
		||||
                 %{"email" => unique_user_email(), "password" => valid_user_password()},
 | 
			
		||||
                 %{email: unique_user_email(), password: valid_user_password()},
 | 
			
		||||
                 another_token
 | 
			
		||||
               )
 | 
			
		||||
 | 
			
		||||
@@ -97,7 +97,7 @@ defmodule Cannery.InvitesTest do
 | 
			
		||||
 | 
			
		||||
      assert {:ok, _user} =
 | 
			
		||||
               Accounts.register_user(
 | 
			
		||||
                 %{"email" => unique_user_email(), "password" => valid_user_password()},
 | 
			
		||||
                 %{email: unique_user_email(), password: valid_user_password()},
 | 
			
		||||
                 token
 | 
			
		||||
               )
 | 
			
		||||
 | 
			
		||||
@@ -138,21 +138,14 @@ defmodule Cannery.InvitesTest do
 | 
			
		||||
 | 
			
		||||
    test "create_invite/1 with valid data creates an unlimited invite",
 | 
			
		||||
         %{current_user: current_user} do
 | 
			
		||||
      assert {:ok, %Invite{} = invite} =
 | 
			
		||||
               Invites.create_invite(current_user, %{
 | 
			
		||||
                 "name" => "some name"
 | 
			
		||||
               })
 | 
			
		||||
 | 
			
		||||
      assert {:ok, %Invite{} = invite} = Invites.create_invite(current_user, %{name: "some name"})
 | 
			
		||||
      assert invite.name == "some name"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "create_invite/1 with valid data creates a limited invite",
 | 
			
		||||
         %{current_user: current_user} do
 | 
			
		||||
      assert {:ok, %Invite{} = invite} =
 | 
			
		||||
               Invites.create_invite(current_user, %{
 | 
			
		||||
                 "name" => "some name",
 | 
			
		||||
                 "uses_left" => 10
 | 
			
		||||
               })
 | 
			
		||||
               Invites.create_invite(current_user, %{name: "some name", uses_left: 10})
 | 
			
		||||
 | 
			
		||||
      assert invite.name == "some name"
 | 
			
		||||
      assert invite.uses_left == 10
 | 
			
		||||
@@ -168,7 +161,7 @@ defmodule Cannery.InvitesTest do
 | 
			
		||||
      assert {:ok, %Invite{} = new_invite} =
 | 
			
		||||
               Invites.update_invite(
 | 
			
		||||
                 invite,
 | 
			
		||||
                 %{"name" => "some updated name", "uses_left" => 5},
 | 
			
		||||
                 %{name: "some updated name", uses_left: 5},
 | 
			
		||||
                 current_user
 | 
			
		||||
               )
 | 
			
		||||
 | 
			
		||||
@@ -178,12 +171,12 @@ defmodule Cannery.InvitesTest do
 | 
			
		||||
 | 
			
		||||
    test "update_invite/2 can set an invite to be unlimited",
 | 
			
		||||
         %{invite: invite, current_user: current_user} do
 | 
			
		||||
      {:ok, invite} = Invites.update_invite(invite, %{"uses_left" => 5}, current_user)
 | 
			
		||||
      {:ok, invite} = Invites.update_invite(invite, %{uses_left: 5}, current_user)
 | 
			
		||||
 | 
			
		||||
      assert {:ok, %Invite{} = new_invite} =
 | 
			
		||||
               Invites.update_invite(
 | 
			
		||||
                 invite,
 | 
			
		||||
                 %{"name" => "some updated name", "uses_left" => nil},
 | 
			
		||||
                 %{name: "some updated name", uses_left: nil},
 | 
			
		||||
                 current_user
 | 
			
		||||
               )
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -63,8 +63,7 @@ defmodule Cannery.AccountsTest do
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "validates email and password when given" do
 | 
			
		||||
      {:error, changeset} =
 | 
			
		||||
        Accounts.register_user(%{"email" => "not valid", "password" => "not valid"})
 | 
			
		||||
      {:error, changeset} = Accounts.register_user(%{email: "not valid", password: "not valid"})
 | 
			
		||||
 | 
			
		||||
      assert %{
 | 
			
		||||
               email: ["must have the @ sign and no spaces"],
 | 
			
		||||
@@ -74,26 +73,25 @@ defmodule Cannery.AccountsTest do
 | 
			
		||||
 | 
			
		||||
    test "validates maximum values for email and password for security" do
 | 
			
		||||
      too_long = String.duplicate("db", 100)
 | 
			
		||||
      {:error, changeset} = Accounts.register_user(%{"email" => too_long, "password" => too_long})
 | 
			
		||||
      {:error, changeset} = Accounts.register_user(%{email: too_long, password: too_long})
 | 
			
		||||
      assert "should be at most 160 character(s)" in errors_on(changeset).email
 | 
			
		||||
      assert "should be at most 80 character(s)" in errors_on(changeset).password
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "validates email uniqueness" do
 | 
			
		||||
      %{email: email} = user_fixture()
 | 
			
		||||
      {:error, changeset} = Accounts.register_user(%{"email" => email})
 | 
			
		||||
      {:error, changeset} = Accounts.register_user(%{email: email})
 | 
			
		||||
      assert "has already been taken" in errors_on(changeset).email
 | 
			
		||||
 | 
			
		||||
      # Now try with the upper cased email too, to check that email case is ignored.
 | 
			
		||||
      {:error, changeset} = Accounts.register_user(%{"email" => String.upcase(email)})
 | 
			
		||||
      {:error, changeset} = Accounts.register_user(%{email: String.upcase(email)})
 | 
			
		||||
      assert "has already been taken" in errors_on(changeset).email
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "registers users with a hashed password" do
 | 
			
		||||
      email = unique_user_email()
 | 
			
		||||
 | 
			
		||||
      {:ok, user} =
 | 
			
		||||
        Accounts.register_user(%{"email" => email, "password" => valid_user_password()})
 | 
			
		||||
      {:ok, user} = Accounts.register_user(%{email: email, password: valid_user_password()})
 | 
			
		||||
 | 
			
		||||
      assert user.email == email
 | 
			
		||||
      assert is_binary(user.hashed_password)
 | 
			
		||||
@@ -103,11 +101,11 @@ defmodule Cannery.AccountsTest do
 | 
			
		||||
 | 
			
		||||
    test "records used invite during registration" do
 | 
			
		||||
      {:ok, %{id: invite_id, token: token}} =
 | 
			
		||||
        admin_fixture() |> Invites.create_invite(%{"name" => "my invite"})
 | 
			
		||||
        admin_fixture() |> Invites.create_invite(%{name: "my invite"})
 | 
			
		||||
 | 
			
		||||
      assert {:ok, %{invite_id: ^invite_id}} =
 | 
			
		||||
               Accounts.register_user(
 | 
			
		||||
                 %{"email" => unique_user_email(), "password" => valid_user_password()},
 | 
			
		||||
                 %{email: unique_user_email(), password: valid_user_password()},
 | 
			
		||||
                 token
 | 
			
		||||
               )
 | 
			
		||||
    end
 | 
			
		||||
@@ -123,7 +121,7 @@ defmodule Cannery.AccountsTest do
 | 
			
		||||
      email = unique_user_email()
 | 
			
		||||
      password = valid_user_password()
 | 
			
		||||
 | 
			
		||||
      changeset = Accounts.change_user_registration(%{"email" => email, "password" => password})
 | 
			
		||||
      changeset = Accounts.change_user_registration(%{email: email, password: password})
 | 
			
		||||
 | 
			
		||||
      assert changeset.valid?
 | 
			
		||||
      assert get_change(changeset, :email) == email
 | 
			
		||||
@@ -151,7 +149,7 @@ defmodule Cannery.AccountsTest do
 | 
			
		||||
 | 
			
		||||
    test "validates email", %{user: user} do
 | 
			
		||||
      {:error, changeset} =
 | 
			
		||||
        Accounts.apply_user_email(user, valid_user_password(), %{"email" => "not valid"})
 | 
			
		||||
        Accounts.apply_user_email(user, valid_user_password(), %{email: "not valid"})
 | 
			
		||||
 | 
			
		||||
      assert %{email: ["must have the @ sign and no spaces"]} = errors_on(changeset)
 | 
			
		||||
    end
 | 
			
		||||
@@ -160,7 +158,7 @@ defmodule Cannery.AccountsTest do
 | 
			
		||||
      too_long = String.duplicate("db", 100)
 | 
			
		||||
 | 
			
		||||
      {:error, changeset} =
 | 
			
		||||
        Accounts.apply_user_email(user, valid_user_password(), %{"email" => too_long})
 | 
			
		||||
        Accounts.apply_user_email(user, valid_user_password(), %{email: too_long})
 | 
			
		||||
 | 
			
		||||
      assert "should be at most 160 character(s)" in errors_on(changeset).email
 | 
			
		||||
    end
 | 
			
		||||
@@ -169,21 +167,21 @@ defmodule Cannery.AccountsTest do
 | 
			
		||||
      %{email: email} = user_fixture()
 | 
			
		||||
 | 
			
		||||
      {:error, changeset} =
 | 
			
		||||
        Accounts.apply_user_email(user, valid_user_password(), %{"email" => email})
 | 
			
		||||
        Accounts.apply_user_email(user, valid_user_password(), %{email: email})
 | 
			
		||||
 | 
			
		||||
      assert "has already been taken" in errors_on(changeset).email
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "validates current password", %{user: user} do
 | 
			
		||||
      {:error, changeset} =
 | 
			
		||||
        Accounts.apply_user_email(user, "invalid", %{"email" => unique_user_email()})
 | 
			
		||||
        Accounts.apply_user_email(user, "invalid", %{email: unique_user_email()})
 | 
			
		||||
 | 
			
		||||
      assert %{current_password: ["is not valid"]} = errors_on(changeset)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "applies the email without persisting it", %{user: user} do
 | 
			
		||||
      email = unique_user_email()
 | 
			
		||||
      {:ok, user} = Accounts.apply_user_email(user, valid_user_password(), %{"email" => email})
 | 
			
		||||
      {:ok, user} = Accounts.apply_user_email(user, valid_user_password(), %{email: email})
 | 
			
		||||
      assert user.email == email
 | 
			
		||||
      assert Accounts.get_user!(user.id).email != email
 | 
			
		||||
    end
 | 
			
		||||
@@ -258,11 +256,7 @@ defmodule Cannery.AccountsTest do
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "allows fields to be set" do
 | 
			
		||||
      changeset =
 | 
			
		||||
        Accounts.change_user_password(%User{}, %{
 | 
			
		||||
          "password" => "new valid password"
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
      changeset = Accounts.change_user_password(%User{}, %{password: "new valid password"})
 | 
			
		||||
      assert changeset.valid?
 | 
			
		||||
      assert get_change(changeset, :password) == "new valid password"
 | 
			
		||||
      assert is_nil(get_change(changeset, :hashed_password))
 | 
			
		||||
@@ -277,8 +271,8 @@ defmodule Cannery.AccountsTest do
 | 
			
		||||
    test "validates password", %{user: user} do
 | 
			
		||||
      {:error, changeset} =
 | 
			
		||||
        Accounts.update_user_password(user, valid_user_password(), %{
 | 
			
		||||
          "password" => "not valid",
 | 
			
		||||
          "password_confirmation" => "another"
 | 
			
		||||
          password: "not valid",
 | 
			
		||||
          password_confirmation: "another"
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
      assert %{
 | 
			
		||||
@@ -291,14 +285,14 @@ defmodule Cannery.AccountsTest do
 | 
			
		||||
      too_long = String.duplicate("db", 100)
 | 
			
		||||
 | 
			
		||||
      {:error, changeset} =
 | 
			
		||||
        Accounts.update_user_password(user, valid_user_password(), %{"password" => too_long})
 | 
			
		||||
        Accounts.update_user_password(user, valid_user_password(), %{password: too_long})
 | 
			
		||||
 | 
			
		||||
      assert "should be at most 80 character(s)" in errors_on(changeset).password
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "validates current password", %{user: user} do
 | 
			
		||||
      {:error, changeset} =
 | 
			
		||||
        Accounts.update_user_password(user, "invalid", %{"password" => valid_user_password()})
 | 
			
		||||
        Accounts.update_user_password(user, "invalid", %{password: valid_user_password()})
 | 
			
		||||
 | 
			
		||||
      assert %{current_password: ["is not valid"]} = errors_on(changeset)
 | 
			
		||||
    end
 | 
			
		||||
@@ -306,7 +300,7 @@ defmodule Cannery.AccountsTest do
 | 
			
		||||
    test "updates the password", %{user: user} do
 | 
			
		||||
      {:ok, user} =
 | 
			
		||||
        Accounts.update_user_password(user, valid_user_password(), %{
 | 
			
		||||
          "password" => "new valid password"
 | 
			
		||||
          password: "new valid password"
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
      assert is_nil(user.password)
 | 
			
		||||
@@ -318,7 +312,7 @@ defmodule Cannery.AccountsTest do
 | 
			
		||||
 | 
			
		||||
      {:ok, _} =
 | 
			
		||||
        Accounts.update_user_password(user, valid_user_password(), %{
 | 
			
		||||
          "password" => "new valid password"
 | 
			
		||||
          password: "new valid password"
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
      refute Repo.get_by(UserToken, user_id: user.id)
 | 
			
		||||
@@ -486,8 +480,8 @@ defmodule Cannery.AccountsTest do
 | 
			
		||||
    test "validates password", %{user: user} do
 | 
			
		||||
      {:error, changeset} =
 | 
			
		||||
        Accounts.reset_user_password(user, %{
 | 
			
		||||
          "password" => "not valid",
 | 
			
		||||
          "password_confirmation" => "another"
 | 
			
		||||
          password: "not valid",
 | 
			
		||||
          password_confirmation: "another"
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
      assert %{
 | 
			
		||||
@@ -498,13 +492,12 @@ defmodule Cannery.AccountsTest do
 | 
			
		||||
 | 
			
		||||
    test "validates maximum values for password for security", %{user: user} do
 | 
			
		||||
      too_long = String.duplicate("db", 100)
 | 
			
		||||
      {:error, changeset} = Accounts.reset_user_password(user, %{"password" => too_long})
 | 
			
		||||
      {:error, changeset} = Accounts.reset_user_password(user, %{password: too_long})
 | 
			
		||||
      assert "should be at most 80 character(s)" in errors_on(changeset).password
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "updates the password", %{user: user} do
 | 
			
		||||
      {:ok, updated_user} =
 | 
			
		||||
        Accounts.reset_user_password(user, %{"password" => "new valid password"})
 | 
			
		||||
      {:ok, updated_user} = Accounts.reset_user_password(user, %{password: "new valid password"})
 | 
			
		||||
 | 
			
		||||
      assert is_nil(updated_user.password)
 | 
			
		||||
      assert Accounts.get_user_by_email_and_password(user.email, "new valid password")
 | 
			
		||||
@@ -512,7 +505,7 @@ defmodule Cannery.AccountsTest do
 | 
			
		||||
 | 
			
		||||
    test "deletes all tokens for the given user", %{user: user} do
 | 
			
		||||
      _session_token = Accounts.generate_user_session_token(user)
 | 
			
		||||
      {:ok, _user} = Accounts.reset_user_password(user, %{"password" => "new valid password"})
 | 
			
		||||
      {:ok, _user} = Accounts.reset_user_password(user, %{password: "new valid password"})
 | 
			
		||||
      refute Repo.get_by(UserToken, user_id: user.id)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
@@ -5,421 +5,428 @@ defmodule Cannery.ActivityLogTest do
 | 
			
		||||
 | 
			
		||||
  use Cannery.DataCase
 | 
			
		||||
  import Cannery.Fixtures
 | 
			
		||||
 | 
			
		||||
  alias Cannery.{
 | 
			
		||||
    ActivityLog,
 | 
			
		||||
    ActivityLog.ShotGroup,
 | 
			
		||||
    Ammo
 | 
			
		||||
  }
 | 
			
		||||
  alias Cannery.{ActivityLog, ActivityLog.ShotRecord, Ammo}
 | 
			
		||||
 | 
			
		||||
  @moduletag :activity_log_test
 | 
			
		||||
 | 
			
		||||
  describe "shot_groups" do
 | 
			
		||||
  describe "shot_records" do
 | 
			
		||||
    setup do
 | 
			
		||||
      current_user = user_fixture()
 | 
			
		||||
      container = container_fixture(current_user)
 | 
			
		||||
      ammo_type = ammo_type_fixture(current_user)
 | 
			
		||||
      type = type_fixture(current_user)
 | 
			
		||||
 | 
			
		||||
      {1, [%{id: ammo_group_id} = ammo_group]} =
 | 
			
		||||
        ammo_group_fixture(%{"count" => 25}, ammo_type, container, current_user)
 | 
			
		||||
      {1, [%{id: pack_id} = pack]} = pack_fixture(%{count: 25}, type, container, current_user)
 | 
			
		||||
 | 
			
		||||
      shot_group =
 | 
			
		||||
        %{"count" => 5, "date" => ~N[2022-02-13 03:17:00], "notes" => "some notes"}
 | 
			
		||||
        |> shot_group_fixture(current_user, ammo_group)
 | 
			
		||||
      shot_record =
 | 
			
		||||
        %{count: 5, date: ~N[2022-02-13 03:17:00], notes: "some notes"}
 | 
			
		||||
        |> shot_record_fixture(current_user, pack)
 | 
			
		||||
 | 
			
		||||
      ammo_group = ammo_group_id |> Ammo.get_ammo_group!(current_user)
 | 
			
		||||
      pack = pack_id |> Ammo.get_pack!(current_user)
 | 
			
		||||
 | 
			
		||||
      [
 | 
			
		||||
        current_user: current_user,
 | 
			
		||||
        container: container,
 | 
			
		||||
        ammo_type: ammo_type,
 | 
			
		||||
        ammo_group: ammo_group,
 | 
			
		||||
        shot_group: shot_group
 | 
			
		||||
        type: type,
 | 
			
		||||
        pack: pack,
 | 
			
		||||
        shot_record: shot_record
 | 
			
		||||
      ]
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "get_shot_group!/2 returns the shot_group with given id",
 | 
			
		||||
         %{shot_group: shot_group, current_user: current_user} do
 | 
			
		||||
      assert ActivityLog.get_shot_group!(shot_group.id, current_user) == shot_group
 | 
			
		||||
    test "get_shot_record_count!/1 returns the correct amount of shot records",
 | 
			
		||||
         %{pack: pack, current_user: current_user} do
 | 
			
		||||
      assert ActivityLog.get_shot_record_count!(current_user) == 1
 | 
			
		||||
 | 
			
		||||
      shot_record_fixture(%{count: 1, date: ~N[2022-02-13 03:17:00]}, current_user, pack)
 | 
			
		||||
      assert ActivityLog.get_shot_record_count!(current_user) == 2
 | 
			
		||||
 | 
			
		||||
      shot_record_fixture(%{count: 1, date: ~N[2022-02-13 03:17:00]}, current_user, pack)
 | 
			
		||||
      assert ActivityLog.get_shot_record_count!(current_user) == 3
 | 
			
		||||
 | 
			
		||||
      other_user = user_fixture()
 | 
			
		||||
      assert ActivityLog.get_shot_record_count!(other_user) == 0
 | 
			
		||||
 | 
			
		||||
      container = container_fixture(other_user)
 | 
			
		||||
      type = type_fixture(other_user)
 | 
			
		||||
      {1, [pack]} = pack_fixture(%{count: 25}, type, container, other_user)
 | 
			
		||||
      shot_record_fixture(%{count: 1, date: ~N[2022-02-13 03:17:00]}, other_user, pack)
 | 
			
		||||
      assert ActivityLog.get_shot_record_count!(other_user) == 1
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "get_shot_group!/2 does not return a shot_group of another user",
 | 
			
		||||
         %{shot_group: shot_group} do
 | 
			
		||||
    test "get_shot_record!/2 returns the shot_record with given id",
 | 
			
		||||
         %{shot_record: shot_record, current_user: current_user} do
 | 
			
		||||
      assert ActivityLog.get_shot_record!(shot_record.id, current_user) == shot_record
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "get_shot_record!/2 does not return a shot_record of another user",
 | 
			
		||||
         %{shot_record: shot_record} do
 | 
			
		||||
      another_user = user_fixture()
 | 
			
		||||
 | 
			
		||||
      assert_raise Ecto.NoResultsError, fn ->
 | 
			
		||||
        ActivityLog.get_shot_group!(shot_group.id, another_user)
 | 
			
		||||
        ActivityLog.get_shot_record!(shot_record.id, another_user)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "create_shot_group/3 with valid data creates a shot_group",
 | 
			
		||||
         %{current_user: current_user, ammo_group: ammo_group} do
 | 
			
		||||
      valid_attrs = %{"count" => 10, "date" => ~D[2022-02-13], "notes" => "some notes"}
 | 
			
		||||
    test "create_shot_record/3 with valid data creates a shot_record",
 | 
			
		||||
         %{current_user: current_user, pack: pack} do
 | 
			
		||||
      valid_attrs = %{count: 10, date: ~D[2022-02-13], notes: "some notes"}
 | 
			
		||||
 | 
			
		||||
      assert {:ok, %ShotGroup{} = shot_group} =
 | 
			
		||||
               ActivityLog.create_shot_group(valid_attrs, current_user, ammo_group)
 | 
			
		||||
      assert {:ok, %ShotRecord{} = shot_record} =
 | 
			
		||||
               ActivityLog.create_shot_record(valid_attrs, current_user, pack)
 | 
			
		||||
 | 
			
		||||
      assert shot_group.count == 10
 | 
			
		||||
      assert shot_group.date == ~D[2022-02-13]
 | 
			
		||||
      assert shot_group.notes == "some notes"
 | 
			
		||||
      assert shot_record.count == 10
 | 
			
		||||
      assert shot_record.date == ~D[2022-02-13]
 | 
			
		||||
      assert shot_record.notes == "some notes"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "create_shot_group/3 removes corresponding count from ammo group",
 | 
			
		||||
    test "create_shot_record/3 removes corresponding count from pack",
 | 
			
		||||
         %{
 | 
			
		||||
           current_user: current_user,
 | 
			
		||||
           ammo_group: %{id: ammo_group_id, count: org_count} = ammo_group
 | 
			
		||||
           pack: %{id: pack_id, count: org_count} = pack
 | 
			
		||||
         } do
 | 
			
		||||
      valid_attrs = %{"count" => 10, "date" => ~D[2022-02-13], "notes" => "some notes"}
 | 
			
		||||
      valid_attrs = %{count: 10, date: ~D[2022-02-13], notes: "some notes"}
 | 
			
		||||
 | 
			
		||||
      assert {:ok, %ShotGroup{} = shot_group} =
 | 
			
		||||
               ActivityLog.create_shot_group(valid_attrs, current_user, ammo_group)
 | 
			
		||||
      assert {:ok, %ShotRecord{} = shot_record} =
 | 
			
		||||
               ActivityLog.create_shot_record(valid_attrs, current_user, pack)
 | 
			
		||||
 | 
			
		||||
      %{count: new_count} = ammo_group_id |> Ammo.get_ammo_group!(current_user)
 | 
			
		||||
      %{count: new_count} = pack_id |> Ammo.get_pack!(current_user)
 | 
			
		||||
 | 
			
		||||
      assert org_count - shot_group.count == new_count
 | 
			
		||||
      assert org_count - shot_record.count == new_count
 | 
			
		||||
      assert new_count == 10
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "create_shot_group/3 does not remove more than ammo group amount",
 | 
			
		||||
         %{current_user: current_user, ammo_group: %{id: ammo_group_id} = ammo_group} do
 | 
			
		||||
      valid_attrs = %{"count" => 20, "date" => ~D[2022-02-13], "notes" => "some notes"}
 | 
			
		||||
    test "create_shot_record/3 does not remove more tha pack amount",
 | 
			
		||||
         %{current_user: current_user, pack: %{id: pack_id} = pack} do
 | 
			
		||||
      valid_attrs = %{count: 20, date: ~D[2022-02-13], notes: "some notes"}
 | 
			
		||||
 | 
			
		||||
      assert {:ok, %ShotGroup{}} =
 | 
			
		||||
               ActivityLog.create_shot_group(valid_attrs, current_user, ammo_group)
 | 
			
		||||
      assert {:ok, %ShotRecord{}} =
 | 
			
		||||
               ActivityLog.create_shot_record(valid_attrs, current_user, pack)
 | 
			
		||||
 | 
			
		||||
      ammo_group = ammo_group_id |> Ammo.get_ammo_group!(current_user)
 | 
			
		||||
      pack = pack_id |> Ammo.get_pack!(current_user)
 | 
			
		||||
 | 
			
		||||
      assert ammo_group.count == 0
 | 
			
		||||
      assert pack.count == 0
 | 
			
		||||
 | 
			
		||||
      assert {:error, %Ecto.Changeset{}} =
 | 
			
		||||
               ActivityLog.create_shot_group(%{"count" => 1}, current_user, ammo_group)
 | 
			
		||||
               ActivityLog.create_shot_record(%{count: 1}, current_user, pack)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "create_shot_group/3 with invalid data returns error changeset",
 | 
			
		||||
         %{current_user: current_user, ammo_group: ammo_group} do
 | 
			
		||||
      invalid_params = %{"count" => nil, "date" => nil, "notes" => nil}
 | 
			
		||||
    test "create_shot_record/3 with invalid data returns error changeset",
 | 
			
		||||
         %{current_user: current_user, pack: pack} do
 | 
			
		||||
      invalid_params = %{count: nil, date: nil, notes: nil}
 | 
			
		||||
 | 
			
		||||
      assert {:error, %Ecto.Changeset{}} =
 | 
			
		||||
               ActivityLog.create_shot_group(invalid_params, current_user, ammo_group)
 | 
			
		||||
               ActivityLog.create_shot_record(invalid_params, current_user, pack)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "update_shot_group/3 with valid data updates the shot_group and ammo_group",
 | 
			
		||||
    test "update_shot_record/3 with valid data updates the shot_record and pack",
 | 
			
		||||
         %{
 | 
			
		||||
           shot_group: shot_group,
 | 
			
		||||
           ammo_group: %{id: ammo_group_id},
 | 
			
		||||
           shot_record: shot_record,
 | 
			
		||||
           pack: %{id: pack_id},
 | 
			
		||||
           current_user: current_user
 | 
			
		||||
         } do
 | 
			
		||||
      assert {:ok, %ShotGroup{} = shot_group} =
 | 
			
		||||
               ActivityLog.update_shot_group(
 | 
			
		||||
                 shot_group,
 | 
			
		||||
      assert {:ok, %ShotRecord{} = shot_record} =
 | 
			
		||||
               ActivityLog.update_shot_record(
 | 
			
		||||
                 shot_record,
 | 
			
		||||
                 %{
 | 
			
		||||
                   "count" => 10,
 | 
			
		||||
                   "date" => ~D[2022-02-13],
 | 
			
		||||
                   "notes" => "some updated notes"
 | 
			
		||||
                   count: 10,
 | 
			
		||||
                   date: ~D[2022-02-13],
 | 
			
		||||
                   notes: "some updated notes"
 | 
			
		||||
                 },
 | 
			
		||||
                 current_user
 | 
			
		||||
               )
 | 
			
		||||
 | 
			
		||||
      ammo_group = ammo_group_id |> Ammo.get_ammo_group!(current_user)
 | 
			
		||||
      pack = pack_id |> Ammo.get_pack!(current_user)
 | 
			
		||||
 | 
			
		||||
      assert shot_group.count == 10
 | 
			
		||||
      assert ammo_group.count == 15
 | 
			
		||||
      assert shot_group.date == ~D[2022-02-13]
 | 
			
		||||
      assert shot_group.notes == "some updated notes"
 | 
			
		||||
      assert shot_record.count == 10
 | 
			
		||||
      assert pack.count == 15
 | 
			
		||||
      assert shot_record.date == ~D[2022-02-13]
 | 
			
		||||
      assert shot_record.notes == "some updated notes"
 | 
			
		||||
 | 
			
		||||
      assert {:ok, %ShotGroup{} = shot_group} =
 | 
			
		||||
               ActivityLog.update_shot_group(
 | 
			
		||||
                 shot_group,
 | 
			
		||||
      assert {:ok, %ShotRecord{} = shot_record} =
 | 
			
		||||
               ActivityLog.update_shot_record(
 | 
			
		||||
                 shot_record,
 | 
			
		||||
                 %{
 | 
			
		||||
                   "count" => 25,
 | 
			
		||||
                   "date" => ~D[2022-02-13],
 | 
			
		||||
                   "notes" => "some updated notes"
 | 
			
		||||
                   count: 25,
 | 
			
		||||
                   date: ~D[2022-02-13],
 | 
			
		||||
                   notes: "some updated notes"
 | 
			
		||||
                 },
 | 
			
		||||
                 current_user
 | 
			
		||||
               )
 | 
			
		||||
 | 
			
		||||
      ammo_group = ammo_group_id |> Ammo.get_ammo_group!(current_user)
 | 
			
		||||
      pack = pack_id |> Ammo.get_pack!(current_user)
 | 
			
		||||
 | 
			
		||||
      assert shot_group.count == 25
 | 
			
		||||
      assert ammo_group.count == 0
 | 
			
		||||
      assert shot_record.count == 25
 | 
			
		||||
      assert pack.count == 0
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "update_shot_group/3 with invalid data returns error changeset",
 | 
			
		||||
         %{shot_group: shot_group, current_user: current_user} do
 | 
			
		||||
    test "update_shot_record/3 with invalid data returns error changeset",
 | 
			
		||||
         %{shot_record: shot_record, current_user: current_user} do
 | 
			
		||||
      assert {:error, %Ecto.Changeset{}} =
 | 
			
		||||
               ActivityLog.update_shot_group(
 | 
			
		||||
                 shot_group,
 | 
			
		||||
                 %{"count" => 26, "date" => nil, "notes" => nil},
 | 
			
		||||
               ActivityLog.update_shot_record(
 | 
			
		||||
                 shot_record,
 | 
			
		||||
                 %{count: 26, date: nil, notes: nil},
 | 
			
		||||
                 current_user
 | 
			
		||||
               )
 | 
			
		||||
 | 
			
		||||
      assert {:error, %Ecto.Changeset{}} =
 | 
			
		||||
               ActivityLog.update_shot_group(
 | 
			
		||||
                 shot_group,
 | 
			
		||||
                 %{"count" => -1, "date" => nil, "notes" => nil},
 | 
			
		||||
               ActivityLog.update_shot_record(
 | 
			
		||||
                 shot_record,
 | 
			
		||||
                 %{count: -1, date: nil, notes: nil},
 | 
			
		||||
                 current_user
 | 
			
		||||
               )
 | 
			
		||||
 | 
			
		||||
      assert shot_group == ActivityLog.get_shot_group!(shot_group.id, current_user)
 | 
			
		||||
      assert shot_record == ActivityLog.get_shot_record!(shot_record.id, current_user)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "delete_shot_group/2 deletes the shot_group and adds value back",
 | 
			
		||||
         %{shot_group: shot_group, current_user: current_user, ammo_group: %{id: ammo_group_id}} do
 | 
			
		||||
      assert {:ok, %ShotGroup{}} = ActivityLog.delete_shot_group(shot_group, current_user)
 | 
			
		||||
    test "delete_shot_record/2 deletes the shot_record and adds value back",
 | 
			
		||||
         %{shot_record: shot_record, current_user: current_user, pack: %{id: pack_id}} do
 | 
			
		||||
      assert {:ok, %ShotRecord{}} = ActivityLog.delete_shot_record(shot_record, current_user)
 | 
			
		||||
 | 
			
		||||
      assert %{count: 25} = ammo_group_id |> Ammo.get_ammo_group!(current_user)
 | 
			
		||||
      assert %{count: 25} = pack_id |> Ammo.get_pack!(current_user)
 | 
			
		||||
 | 
			
		||||
      assert_raise Ecto.NoResultsError, fn ->
 | 
			
		||||
        ActivityLog.get_shot_group!(shot_group.id, current_user)
 | 
			
		||||
        ActivityLog.get_shot_record!(shot_record.id, current_user)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "get_used_count/2 returns accurate used count", %{
 | 
			
		||||
      ammo_group: ammo_group,
 | 
			
		||||
      ammo_type: ammo_type,
 | 
			
		||||
      pack: pack,
 | 
			
		||||
      type: type,
 | 
			
		||||
      container: container,
 | 
			
		||||
      current_user: current_user
 | 
			
		||||
    } do
 | 
			
		||||
      {1, [another_ammo_group]} = ammo_group_fixture(ammo_type, container, current_user)
 | 
			
		||||
      assert 0 = another_ammo_group |> ActivityLog.get_used_count(current_user)
 | 
			
		||||
      assert 5 = ammo_group |> ActivityLog.get_used_count(current_user)
 | 
			
		||||
      {1, [another_pack]} = pack_fixture(type, container, current_user)
 | 
			
		||||
      assert 0 = another_pack |> ActivityLog.get_used_count(current_user)
 | 
			
		||||
      assert 5 = pack |> ActivityLog.get_used_count(current_user)
 | 
			
		||||
 | 
			
		||||
      shot_group_fixture(%{"count" => 15}, current_user, ammo_group)
 | 
			
		||||
      assert 20 = ammo_group |> ActivityLog.get_used_count(current_user)
 | 
			
		||||
      shot_record_fixture(%{count: 15}, current_user, pack)
 | 
			
		||||
      assert 20 = pack |> ActivityLog.get_used_count(current_user)
 | 
			
		||||
 | 
			
		||||
      shot_group_fixture(%{"count" => 10}, current_user, ammo_group)
 | 
			
		||||
      assert 30 = ammo_group |> ActivityLog.get_used_count(current_user)
 | 
			
		||||
      shot_record_fixture(%{count: 10}, current_user, pack)
 | 
			
		||||
      assert 30 = pack |> ActivityLog.get_used_count(current_user)
 | 
			
		||||
 | 
			
		||||
      {1, [another_ammo_group]} = ammo_group_fixture(ammo_type, container, current_user)
 | 
			
		||||
      assert 0 = another_ammo_group |> ActivityLog.get_used_count(current_user)
 | 
			
		||||
      {1, [another_pack]} = pack_fixture(type, container, current_user)
 | 
			
		||||
      assert 0 = another_pack |> ActivityLog.get_used_count(current_user)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "get_used_counts/2 returns accurate used counts", %{
 | 
			
		||||
      ammo_group: %{id: ammo_group_id} = ammo_group,
 | 
			
		||||
      ammo_type: ammo_type,
 | 
			
		||||
      pack: %{id: pack_id} = pack,
 | 
			
		||||
      type: type,
 | 
			
		||||
      container: container,
 | 
			
		||||
      current_user: current_user
 | 
			
		||||
    } do
 | 
			
		||||
      {1, [%{id: another_ammo_group_id} = another_ammo_group]} =
 | 
			
		||||
        ammo_group_fixture(ammo_type, container, current_user)
 | 
			
		||||
      {1, [%{id: another_pack_id} = another_pack]} = pack_fixture(type, container, current_user)
 | 
			
		||||
 | 
			
		||||
      assert %{ammo_group_id => 5} ==
 | 
			
		||||
               [ammo_group, another_ammo_group] |> ActivityLog.get_used_counts(current_user)
 | 
			
		||||
      assert %{pack_id => 5} ==
 | 
			
		||||
               [pack, another_pack] |> ActivityLog.get_used_counts(current_user)
 | 
			
		||||
 | 
			
		||||
      shot_group_fixture(%{"count" => 5}, current_user, another_ammo_group)
 | 
			
		||||
      used_counts = [ammo_group, another_ammo_group] |> ActivityLog.get_used_counts(current_user)
 | 
			
		||||
      assert %{^ammo_group_id => 5} = used_counts
 | 
			
		||||
      assert %{^another_ammo_group_id => 5} = used_counts
 | 
			
		||||
      shot_record_fixture(%{count: 5}, current_user, another_pack)
 | 
			
		||||
      used_counts = [pack, another_pack] |> ActivityLog.get_used_counts(current_user)
 | 
			
		||||
      assert %{^pack_id => 5} = used_counts
 | 
			
		||||
      assert %{^another_pack_id => 5} = used_counts
 | 
			
		||||
 | 
			
		||||
      shot_group_fixture(%{"count" => 15}, current_user, ammo_group)
 | 
			
		||||
      used_counts = [ammo_group, another_ammo_group] |> ActivityLog.get_used_counts(current_user)
 | 
			
		||||
      assert %{^ammo_group_id => 20} = used_counts
 | 
			
		||||
      assert %{^another_ammo_group_id => 5} = used_counts
 | 
			
		||||
      shot_record_fixture(%{count: 15}, current_user, pack)
 | 
			
		||||
      used_counts = [pack, another_pack] |> ActivityLog.get_used_counts(current_user)
 | 
			
		||||
      assert %{^pack_id => 20} = used_counts
 | 
			
		||||
      assert %{^another_pack_id => 5} = used_counts
 | 
			
		||||
 | 
			
		||||
      shot_group_fixture(%{"count" => 10}, current_user, ammo_group)
 | 
			
		||||
      used_counts = [ammo_group, another_ammo_group] |> ActivityLog.get_used_counts(current_user)
 | 
			
		||||
      assert %{^ammo_group_id => 30} = used_counts
 | 
			
		||||
      assert %{^another_ammo_group_id => 5} = used_counts
 | 
			
		||||
      shot_record_fixture(%{count: 10}, current_user, pack)
 | 
			
		||||
      used_counts = [pack, another_pack] |> ActivityLog.get_used_counts(current_user)
 | 
			
		||||
      assert %{^pack_id => 30} = used_counts
 | 
			
		||||
      assert %{^another_pack_id => 5} = used_counts
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "get_last_used_date/2 returns accurate used count", %{
 | 
			
		||||
      ammo_group: ammo_group,
 | 
			
		||||
      ammo_type: ammo_type,
 | 
			
		||||
      pack: pack,
 | 
			
		||||
      type: type,
 | 
			
		||||
      container: container,
 | 
			
		||||
      shot_group: %{date: date},
 | 
			
		||||
      shot_record: %{date: date},
 | 
			
		||||
      current_user: current_user
 | 
			
		||||
    } do
 | 
			
		||||
      {1, [another_ammo_group]} = ammo_group_fixture(ammo_type, container, current_user)
 | 
			
		||||
      assert another_ammo_group |> ActivityLog.get_last_used_date(current_user) |> is_nil()
 | 
			
		||||
      assert ^date = ammo_group |> ActivityLog.get_last_used_date(current_user)
 | 
			
		||||
      {1, [another_pack]} = pack_fixture(type, container, current_user)
 | 
			
		||||
      assert another_pack |> ActivityLog.get_last_used_date(current_user) |> is_nil()
 | 
			
		||||
      assert ^date = pack |> ActivityLog.get_last_used_date(current_user)
 | 
			
		||||
 | 
			
		||||
      %{date: date} = shot_group_fixture(%{"date" => ~D[2022-11-10]}, current_user, ammo_group)
 | 
			
		||||
      assert ^date = ammo_group |> ActivityLog.get_last_used_date(current_user)
 | 
			
		||||
      %{date: date} = shot_record_fixture(%{date: ~D[2022-11-10]}, current_user, pack)
 | 
			
		||||
      assert ^date = pack |> ActivityLog.get_last_used_date(current_user)
 | 
			
		||||
 | 
			
		||||
      %{date: date} = shot_group_fixture(%{"date" => ~D[2022-11-11]}, current_user, ammo_group)
 | 
			
		||||
      assert ^date = ammo_group |> ActivityLog.get_last_used_date(current_user)
 | 
			
		||||
      %{date: date} = shot_record_fixture(%{date: ~D[2022-11-11]}, current_user, pack)
 | 
			
		||||
      assert ^date = pack |> ActivityLog.get_last_used_date(current_user)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "get_last_used_dates/2 returns accurate used counts", %{
 | 
			
		||||
      ammo_group: %{id: ammo_group_id} = ammo_group,
 | 
			
		||||
      ammo_type: ammo_type,
 | 
			
		||||
      pack: %{id: pack_id} = pack,
 | 
			
		||||
      type: type,
 | 
			
		||||
      container: container,
 | 
			
		||||
      shot_group: %{date: date},
 | 
			
		||||
      shot_record: %{date: date},
 | 
			
		||||
      current_user: current_user
 | 
			
		||||
    } do
 | 
			
		||||
      {1, [%{id: another_ammo_group_id} = another_ammo_group]} =
 | 
			
		||||
        ammo_group_fixture(ammo_type, container, current_user)
 | 
			
		||||
      {1, [%{id: another_pack_id} = another_pack]} = pack_fixture(type, container, current_user)
 | 
			
		||||
 | 
			
		||||
      # unset date
 | 
			
		||||
      assert %{ammo_group_id => date} ==
 | 
			
		||||
               [ammo_group, another_ammo_group] |> ActivityLog.get_last_used_dates(current_user)
 | 
			
		||||
      assert %{pack_id => date} ==
 | 
			
		||||
               [pack, another_pack] |> ActivityLog.get_last_used_dates(current_user)
 | 
			
		||||
 | 
			
		||||
      shot_group_fixture(%{"date" => ~D[2022-11-09]}, current_user, another_ammo_group)
 | 
			
		||||
      shot_record_fixture(%{date: ~D[2022-11-09]}, current_user, another_pack)
 | 
			
		||||
 | 
			
		||||
      # setting initial date
 | 
			
		||||
      last_used_shot_groups =
 | 
			
		||||
        [ammo_group, another_ammo_group] |> ActivityLog.get_last_used_dates(current_user)
 | 
			
		||||
      last_used_shot_records =
 | 
			
		||||
        [pack, another_pack] |> ActivityLog.get_last_used_dates(current_user)
 | 
			
		||||
 | 
			
		||||
      assert %{^ammo_group_id => ^date} = last_used_shot_groups
 | 
			
		||||
      assert %{^another_ammo_group_id => ~D[2022-11-09]} = last_used_shot_groups
 | 
			
		||||
      assert %{^pack_id => ^date} = last_used_shot_records
 | 
			
		||||
      assert %{^another_pack_id => ~D[2022-11-09]} = last_used_shot_records
 | 
			
		||||
 | 
			
		||||
      # setting another date
 | 
			
		||||
      shot_group_fixture(%{"date" => ~D[2022-11-10]}, current_user, ammo_group)
 | 
			
		||||
      shot_record_fixture(%{date: ~D[2022-11-10]}, current_user, pack)
 | 
			
		||||
 | 
			
		||||
      last_used_shot_groups =
 | 
			
		||||
        [ammo_group, another_ammo_group] |> ActivityLog.get_last_used_dates(current_user)
 | 
			
		||||
      last_used_shot_records =
 | 
			
		||||
        [pack, another_pack] |> ActivityLog.get_last_used_dates(current_user)
 | 
			
		||||
 | 
			
		||||
      assert %{^ammo_group_id => ~D[2022-11-10]} = last_used_shot_groups
 | 
			
		||||
      assert %{^another_ammo_group_id => ~D[2022-11-09]} = last_used_shot_groups
 | 
			
		||||
      assert %{^pack_id => ~D[2022-11-10]} = last_used_shot_records
 | 
			
		||||
      assert %{^another_pack_id => ~D[2022-11-09]} = last_used_shot_records
 | 
			
		||||
 | 
			
		||||
      # setting yet another date
 | 
			
		||||
      shot_group_fixture(%{"date" => ~D[2022-11-11]}, current_user, ammo_group)
 | 
			
		||||
      shot_record_fixture(%{date: ~D[2022-11-11]}, current_user, pack)
 | 
			
		||||
 | 
			
		||||
      last_used_shot_groups =
 | 
			
		||||
        [ammo_group, another_ammo_group] |> ActivityLog.get_last_used_dates(current_user)
 | 
			
		||||
      last_used_shot_records =
 | 
			
		||||
        [pack, another_pack] |> ActivityLog.get_last_used_dates(current_user)
 | 
			
		||||
 | 
			
		||||
      assert %{^ammo_group_id => ~D[2022-11-11]} = last_used_shot_groups
 | 
			
		||||
      assert %{^another_ammo_group_id => ~D[2022-11-09]} = last_used_shot_groups
 | 
			
		||||
      assert %{^pack_id => ~D[2022-11-11]} = last_used_shot_records
 | 
			
		||||
      assert %{^another_pack_id => ~D[2022-11-09]} = last_used_shot_records
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "get_used_count_for_ammo_type/2 gets accurate used round count for ammo type",
 | 
			
		||||
         %{ammo_type: ammo_type, ammo_group: ammo_group, current_user: current_user} do
 | 
			
		||||
      another_ammo_type = ammo_type_fixture(current_user)
 | 
			
		||||
      assert 0 = another_ammo_type |> ActivityLog.get_used_count_for_ammo_type(current_user)
 | 
			
		||||
      assert 5 = ammo_type |> ActivityLog.get_used_count_for_ammo_type(current_user)
 | 
			
		||||
    test "get_used_count_for_type/2 gets accurate used round count for type",
 | 
			
		||||
         %{type: type, pack: pack, current_user: current_user} do
 | 
			
		||||
      another_type = type_fixture(current_user)
 | 
			
		||||
      assert 0 = another_type |> ActivityLog.get_used_count_for_type(current_user)
 | 
			
		||||
      assert 5 = type |> ActivityLog.get_used_count_for_type(current_user)
 | 
			
		||||
 | 
			
		||||
      shot_group_fixture(%{"count" => 5}, current_user, ammo_group)
 | 
			
		||||
      assert 10 = ammo_type |> ActivityLog.get_used_count_for_ammo_type(current_user)
 | 
			
		||||
      shot_record_fixture(%{count: 5}, current_user, pack)
 | 
			
		||||
      assert 10 = type |> ActivityLog.get_used_count_for_type(current_user)
 | 
			
		||||
 | 
			
		||||
      shot_group_fixture(%{"count" => 1}, current_user, ammo_group)
 | 
			
		||||
      assert 11 = ammo_type |> ActivityLog.get_used_count_for_ammo_type(current_user)
 | 
			
		||||
      shot_record_fixture(%{count: 1}, current_user, pack)
 | 
			
		||||
      assert 11 = type |> ActivityLog.get_used_count_for_type(current_user)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "get_used_count_for_ammo_types/2 gets accurate used round count for ammo types", %{
 | 
			
		||||
      ammo_type: %{id: ammo_type_id} = ammo_type,
 | 
			
		||||
    test "get_used_count_for_types/2 gets accurate used round count for types", %{
 | 
			
		||||
      type: %{id: type_id} = type,
 | 
			
		||||
      container: container,
 | 
			
		||||
      current_user: current_user
 | 
			
		||||
    } do
 | 
			
		||||
      # testing unused ammo type
 | 
			
		||||
      %{id: another_ammo_type_id} = another_ammo_type = ammo_type_fixture(current_user)
 | 
			
		||||
      {1, [ammo_group]} = ammo_group_fixture(another_ammo_type, container, current_user)
 | 
			
		||||
      # testing unused type
 | 
			
		||||
      %{id: another_type_id} = another_type = type_fixture(current_user)
 | 
			
		||||
      {1, [pack]} = pack_fixture(another_type, container, current_user)
 | 
			
		||||
 | 
			
		||||
      assert %{ammo_type_id => 5} ==
 | 
			
		||||
               [ammo_type, another_ammo_type]
 | 
			
		||||
               |> ActivityLog.get_used_count_for_ammo_types(current_user)
 | 
			
		||||
      assert %{type_id => 5} ==
 | 
			
		||||
               [type, another_type]
 | 
			
		||||
               |> ActivityLog.get_used_count_for_types(current_user)
 | 
			
		||||
 | 
			
		||||
      # use generated ammo group
 | 
			
		||||
      shot_group_fixture(%{"count" => 5}, current_user, ammo_group)
 | 
			
		||||
      # use generated pack
 | 
			
		||||
      shot_record_fixture(%{count: 5}, current_user, pack)
 | 
			
		||||
 | 
			
		||||
      used_counts =
 | 
			
		||||
        [ammo_type, another_ammo_type] |> ActivityLog.get_used_count_for_ammo_types(current_user)
 | 
			
		||||
      used_counts = [type, another_type] |> ActivityLog.get_used_count_for_types(current_user)
 | 
			
		||||
 | 
			
		||||
      assert %{^ammo_type_id => 5} = used_counts
 | 
			
		||||
      assert %{^another_ammo_type_id => 5} = used_counts
 | 
			
		||||
      assert %{^type_id => 5} = used_counts
 | 
			
		||||
      assert %{^another_type_id => 5} = used_counts
 | 
			
		||||
 | 
			
		||||
      # use generated ammo group again
 | 
			
		||||
      shot_group_fixture(%{"count" => 1}, current_user, ammo_group)
 | 
			
		||||
      # use generated pack again
 | 
			
		||||
      shot_record_fixture(%{count: 1}, current_user, pack)
 | 
			
		||||
 | 
			
		||||
      used_counts =
 | 
			
		||||
        [ammo_type, another_ammo_type] |> ActivityLog.get_used_count_for_ammo_types(current_user)
 | 
			
		||||
      used_counts = [type, another_type] |> ActivityLog.get_used_count_for_types(current_user)
 | 
			
		||||
 | 
			
		||||
      assert %{^ammo_type_id => 5} = used_counts
 | 
			
		||||
      assert %{^another_ammo_type_id => 6} = used_counts
 | 
			
		||||
      assert %{^type_id => 5} = used_counts
 | 
			
		||||
      assert %{^another_type_id => 6} = used_counts
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "list_shot_groups/3" do
 | 
			
		||||
  describe "list_shot_records/3" do
 | 
			
		||||
    setup do
 | 
			
		||||
      current_user = user_fixture()
 | 
			
		||||
      container = container_fixture(current_user)
 | 
			
		||||
      ammo_type = ammo_type_fixture(current_user)
 | 
			
		||||
      {1, [ammo_group]} = ammo_group_fixture(ammo_type, container, current_user)
 | 
			
		||||
      type = type_fixture(current_user)
 | 
			
		||||
      {1, [pack]} = pack_fixture(type, container, current_user)
 | 
			
		||||
 | 
			
		||||
      [
 | 
			
		||||
        current_user: current_user,
 | 
			
		||||
        container: container,
 | 
			
		||||
        ammo_type: ammo_type,
 | 
			
		||||
        ammo_group: ammo_group
 | 
			
		||||
        type: type,
 | 
			
		||||
        pack: pack
 | 
			
		||||
      ]
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "list_shot_groups/3 returns relevant shot_groups for a type",
 | 
			
		||||
    test "list_shot_records/3 returns relevant shot_records for a type",
 | 
			
		||||
         %{current_user: current_user, container: container} do
 | 
			
		||||
      other_user = user_fixture()
 | 
			
		||||
      other_container = container_fixture(other_user)
 | 
			
		||||
 | 
			
		||||
      for type <- ["rifle", "shotgun", "pistol"] do
 | 
			
		||||
        other_ammo_type = ammo_type_fixture(%{"type" => type}, other_user)
 | 
			
		||||
        {1, [other_ammo_group]} = ammo_group_fixture(other_ammo_type, other_container, other_user)
 | 
			
		||||
        shot_group_fixture(other_user, other_ammo_group)
 | 
			
		||||
      for class <- ["rifle", "shotgun", "pistol"] do
 | 
			
		||||
        other_type = type_fixture(%{class: class}, other_user)
 | 
			
		||||
        {1, [other_pack]} = pack_fixture(other_type, other_container, other_user)
 | 
			
		||||
        shot_record_fixture(other_user, other_pack)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      rifle_ammo_type = ammo_type_fixture(%{"type" => "rifle"}, current_user)
 | 
			
		||||
      {1, [rifle_ammo_group]} = ammo_group_fixture(rifle_ammo_type, container, current_user)
 | 
			
		||||
      rifle_shot_group = shot_group_fixture(current_user, rifle_ammo_group)
 | 
			
		||||
      rifle_type = type_fixture(%{class: :rifle}, current_user)
 | 
			
		||||
      {1, [rifle_pack]} = pack_fixture(rifle_type, container, current_user)
 | 
			
		||||
      rifle_shot_record = shot_record_fixture(current_user, rifle_pack)
 | 
			
		||||
 | 
			
		||||
      shotgun_ammo_type = ammo_type_fixture(%{"type" => "shotgun"}, current_user)
 | 
			
		||||
      {1, [shotgun_ammo_group]} = ammo_group_fixture(shotgun_ammo_type, container, current_user)
 | 
			
		||||
      shotgun_shot_group = shot_group_fixture(current_user, shotgun_ammo_group)
 | 
			
		||||
      shotgun_type = type_fixture(%{class: :shotgun}, current_user)
 | 
			
		||||
      {1, [shotgun_pack]} = pack_fixture(shotgun_type, container, current_user)
 | 
			
		||||
      shotgun_shot_record = shot_record_fixture(current_user, shotgun_pack)
 | 
			
		||||
 | 
			
		||||
      pistol_ammo_type = ammo_type_fixture(%{"type" => "pistol"}, current_user)
 | 
			
		||||
      {1, [pistol_ammo_group]} = ammo_group_fixture(pistol_ammo_type, container, current_user)
 | 
			
		||||
      pistol_shot_group = shot_group_fixture(current_user, pistol_ammo_group)
 | 
			
		||||
      pistol_type = type_fixture(%{class: :pistol}, current_user)
 | 
			
		||||
      {1, [pistol_pack]} = pack_fixture(pistol_type, container, current_user)
 | 
			
		||||
      pistol_shot_record = shot_record_fixture(current_user, pistol_pack)
 | 
			
		||||
 | 
			
		||||
      assert [^rifle_shot_group] = ActivityLog.list_shot_groups(:rifle, current_user)
 | 
			
		||||
      assert [^shotgun_shot_group] = ActivityLog.list_shot_groups(:shotgun, current_user)
 | 
			
		||||
      assert [^pistol_shot_group] = ActivityLog.list_shot_groups(:pistol, current_user)
 | 
			
		||||
      assert [^rifle_shot_record] = ActivityLog.list_shot_records(:rifle, current_user)
 | 
			
		||||
      assert [^shotgun_shot_record] = ActivityLog.list_shot_records(:shotgun, current_user)
 | 
			
		||||
      assert [^pistol_shot_record] = ActivityLog.list_shot_records(:pistol, current_user)
 | 
			
		||||
 | 
			
		||||
      shot_groups = ActivityLog.list_shot_groups(:all, current_user)
 | 
			
		||||
      assert Enum.count(shot_groups) == 3
 | 
			
		||||
      assert rifle_shot_group in shot_groups
 | 
			
		||||
      assert shotgun_shot_group in shot_groups
 | 
			
		||||
      assert pistol_shot_group in shot_groups
 | 
			
		||||
      shot_records = ActivityLog.list_shot_records(:all, current_user)
 | 
			
		||||
      assert Enum.count(shot_records) == 3
 | 
			
		||||
      assert rifle_shot_record in shot_records
 | 
			
		||||
      assert shotgun_shot_record in shot_records
 | 
			
		||||
      assert pistol_shot_record in shot_records
 | 
			
		||||
 | 
			
		||||
      shot_groups = ActivityLog.list_shot_groups(nil, current_user)
 | 
			
		||||
      assert Enum.count(shot_groups) == 3
 | 
			
		||||
      assert rifle_shot_group in shot_groups
 | 
			
		||||
      assert shotgun_shot_group in shot_groups
 | 
			
		||||
      assert pistol_shot_group in shot_groups
 | 
			
		||||
      shot_records = ActivityLog.list_shot_records(nil, current_user)
 | 
			
		||||
      assert Enum.count(shot_records) == 3
 | 
			
		||||
      assert rifle_shot_record in shot_records
 | 
			
		||||
      assert shotgun_shot_record in shot_records
 | 
			
		||||
      assert pistol_shot_record in shot_records
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "list_shot_groups/3 returns relevant shot_groups for a search", %{
 | 
			
		||||
      ammo_type: ammo_type,
 | 
			
		||||
      ammo_group: ammo_group,
 | 
			
		||||
    test "list_shot_records/3 returns relevant shot_records for a search", %{
 | 
			
		||||
      type: type,
 | 
			
		||||
      pack: pack,
 | 
			
		||||
      container: container,
 | 
			
		||||
      current_user: current_user
 | 
			
		||||
    } do
 | 
			
		||||
      shot_group_a = shot_group_fixture(%{"notes" => "amazing"}, current_user, ammo_group)
 | 
			
		||||
      shot_record_a = shot_record_fixture(%{notes: "amazing"}, current_user, pack)
 | 
			
		||||
 | 
			
		||||
      {1, [another_ammo_group]} =
 | 
			
		||||
        ammo_group_fixture(%{"notes" => "stupendous"}, ammo_type, container, current_user)
 | 
			
		||||
      {1, [another_pack]} = pack_fixture(%{notes: "stupendous"}, type, container, current_user)
 | 
			
		||||
 | 
			
		||||
      shot_group_b = shot_group_fixture(current_user, another_ammo_group)
 | 
			
		||||
      shot_record_b = shot_record_fixture(current_user, another_pack)
 | 
			
		||||
 | 
			
		||||
      another_ammo_type = ammo_type_fixture(%{"name" => "fabulous ammo"}, current_user)
 | 
			
		||||
      another_type = type_fixture(%{name: "fabulous ammo"}, current_user)
 | 
			
		||||
 | 
			
		||||
      {1, [yet_another_ammo_group]} =
 | 
			
		||||
        ammo_group_fixture(another_ammo_type, container, current_user)
 | 
			
		||||
      {1, [yet_another_pack]} = pack_fixture(another_type, container, current_user)
 | 
			
		||||
 | 
			
		||||
      shot_group_c = shot_group_fixture(current_user, yet_another_ammo_group)
 | 
			
		||||
      shot_record_c = shot_record_fixture(current_user, yet_another_pack)
 | 
			
		||||
 | 
			
		||||
      another_user = user_fixture()
 | 
			
		||||
      another_container = container_fixture(another_user)
 | 
			
		||||
      another_ammo_type = ammo_type_fixture(another_user)
 | 
			
		||||
      another_type = type_fixture(another_user)
 | 
			
		||||
 | 
			
		||||
      {1, [another_ammo_group]} =
 | 
			
		||||
        ammo_group_fixture(another_ammo_type, another_container, another_user)
 | 
			
		||||
      {1, [another_pack]} = pack_fixture(another_type, another_container, another_user)
 | 
			
		||||
 | 
			
		||||
      _shouldnt_return = shot_group_fixture(another_user, another_ammo_group)
 | 
			
		||||
      _shouldnt_return = shot_record_fixture(another_user, another_pack)
 | 
			
		||||
 | 
			
		||||
      # notes
 | 
			
		||||
      assert ActivityLog.list_shot_groups("amazing", :all, current_user) == [shot_group_a]
 | 
			
		||||
      assert ActivityLog.list_shot_records("amazing", :all, current_user) == [shot_record_a]
 | 
			
		||||
 | 
			
		||||
      # ammo group attributes
 | 
			
		||||
      assert ActivityLog.list_shot_groups("stupendous", :all, current_user) == [shot_group_b]
 | 
			
		||||
      # pack attributes
 | 
			
		||||
      assert ActivityLog.list_shot_records("stupendous", :all, current_user) == [shot_record_b]
 | 
			
		||||
 | 
			
		||||
      # ammo type attributes
 | 
			
		||||
      assert ActivityLog.list_shot_groups("fabulous", :all, current_user) == [shot_group_c]
 | 
			
		||||
      # type attributes
 | 
			
		||||
      assert ActivityLog.list_shot_records("fabulous", :all, current_user) == [shot_record_c]
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -10,37 +10,37 @@ defmodule Cannery.ContainersTest do
 | 
			
		||||
  @moduletag :containers_test
 | 
			
		||||
 | 
			
		||||
  @valid_attrs %{
 | 
			
		||||
    "desc" => "some desc",
 | 
			
		||||
    "location" => "some location",
 | 
			
		||||
    "name" => "some name",
 | 
			
		||||
    "type" => "some type"
 | 
			
		||||
    desc: "some desc",
 | 
			
		||||
    location: "some location",
 | 
			
		||||
    name: "some name",
 | 
			
		||||
    type: "some type"
 | 
			
		||||
  }
 | 
			
		||||
  @update_attrs %{
 | 
			
		||||
    "desc" => "some updated desc",
 | 
			
		||||
    "location" => "some updated location",
 | 
			
		||||
    "name" => "some updated name",
 | 
			
		||||
    "type" => "some updated type"
 | 
			
		||||
    desc: "some updated desc",
 | 
			
		||||
    location: "some updated location",
 | 
			
		||||
    name: "some updated name",
 | 
			
		||||
    type: "some updated type"
 | 
			
		||||
  }
 | 
			
		||||
  @invalid_attrs %{
 | 
			
		||||
    "desc" => nil,
 | 
			
		||||
    "location" => nil,
 | 
			
		||||
    "name" => nil,
 | 
			
		||||
    "type" => nil
 | 
			
		||||
    desc: nil,
 | 
			
		||||
    location: nil,
 | 
			
		||||
    name: nil,
 | 
			
		||||
    type: nil
 | 
			
		||||
  }
 | 
			
		||||
  @valid_tag_attrs %{
 | 
			
		||||
    "bg_color" => "#100000",
 | 
			
		||||
    "name" => "some name",
 | 
			
		||||
    "text_color" => "#000000"
 | 
			
		||||
    bg_color: "#100000",
 | 
			
		||||
    name: "some name",
 | 
			
		||||
    text_color: "#000000"
 | 
			
		||||
  }
 | 
			
		||||
  @update_tag_attrs %{
 | 
			
		||||
    "bg_color" => "#100001",
 | 
			
		||||
    "name" => "some updated name",
 | 
			
		||||
    "text_color" => "#000001"
 | 
			
		||||
    bg_color: "#100001",
 | 
			
		||||
    name: "some updated name",
 | 
			
		||||
    text_color: "#000001"
 | 
			
		||||
  }
 | 
			
		||||
  @invalid_tag_attrs %{
 | 
			
		||||
    "bg_color" => nil,
 | 
			
		||||
    "name" => nil,
 | 
			
		||||
    "text_color" => nil
 | 
			
		||||
    bg_color: nil,
 | 
			
		||||
    name: nil,
 | 
			
		||||
    text_color: nil
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  describe "containers" do
 | 
			
		||||
@@ -57,25 +57,24 @@ defmodule Cannery.ContainersTest do
 | 
			
		||||
 | 
			
		||||
    test "list_containers/2 returns relevant containers for a user",
 | 
			
		||||
         %{current_user: current_user} do
 | 
			
		||||
      container_a = container_fixture(%{"name" => "my cool container"}, current_user)
 | 
			
		||||
      container_b = container_fixture(%{"desc" => "a fascinating description"}, current_user)
 | 
			
		||||
      container_a = container_fixture(%{name: "my cool container"}, current_user)
 | 
			
		||||
      container_b = container_fixture(%{desc: "a fascinating description"}, current_user)
 | 
			
		||||
 | 
			
		||||
      %{id: container_c_id} =
 | 
			
		||||
        container_c = container_fixture(%{"location" => "a secret place"}, current_user)
 | 
			
		||||
        container_c = container_fixture(%{location: "a secret place"}, current_user)
 | 
			
		||||
 | 
			
		||||
      tag = tag_fixture(%{"name" => "stupendous tag"}, current_user)
 | 
			
		||||
      tag = tag_fixture(%{name: "stupendous tag"}, current_user)
 | 
			
		||||
      Containers.add_tag!(container_c, tag, current_user)
 | 
			
		||||
      container_c = container_c_id |> Containers.get_container!(current_user)
 | 
			
		||||
 | 
			
		||||
      %{id: container_d_id} =
 | 
			
		||||
        container_d = container_fixture(%{"type" => "musty old box"}, current_user)
 | 
			
		||||
        container_d = container_fixture(%{type: "musty old box"}, current_user)
 | 
			
		||||
 | 
			
		||||
      tag = tag_fixture(%{"name" => "amazing tag"}, current_user)
 | 
			
		||||
      tag = tag_fixture(%{name: "amazing tag"}, current_user)
 | 
			
		||||
      Containers.add_tag!(container_d, tag, current_user)
 | 
			
		||||
      container_d = container_d_id |> Containers.get_container!(current_user)
 | 
			
		||||
 | 
			
		||||
      _shouldnt_return =
 | 
			
		||||
        container_fixture(%{"name" => "another person's container"}, user_fixture())
 | 
			
		||||
      _shouldnt_return = container_fixture(%{name: "another person's container"}, user_fixture())
 | 
			
		||||
 | 
			
		||||
      # attributes
 | 
			
		||||
      assert Containers.list_containers("cool", current_user) == [container_a]
 | 
			
		||||
@@ -109,7 +108,7 @@ defmodule Cannery.ContainersTest do
 | 
			
		||||
 | 
			
		||||
    test "create_container/2 with valid data creates a container", %{current_user: current_user} do
 | 
			
		||||
      assert {:ok, %Container{} = container} =
 | 
			
		||||
               @valid_attrs |> Containers.create_container(current_user)
 | 
			
		||||
               Containers.create_container(@valid_attrs, current_user)
 | 
			
		||||
 | 
			
		||||
      assert container.desc == "some desc"
 | 
			
		||||
      assert container.location == "some location"
 | 
			
		||||
@@ -120,7 +119,7 @@ defmodule Cannery.ContainersTest do
 | 
			
		||||
 | 
			
		||||
    test "create_container/2 with invalid data returns error changeset",
 | 
			
		||||
         %{current_user: current_user} do
 | 
			
		||||
      assert {:error, %Changeset{}} = @invalid_attrs |> Containers.create_container(current_user)
 | 
			
		||||
      assert {:error, %Changeset{}} = Containers.create_container(@invalid_attrs, current_user)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "update_container/3 with valid data updates the container",
 | 
			
		||||
@@ -163,15 +162,10 @@ defmodule Cannery.ContainersTest do
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "list_tags/2 returns relevant tags for a user", %{current_user: current_user} do
 | 
			
		||||
      tag_a = tag_fixture(%{"name" => "bullets"}, current_user)
 | 
			
		||||
      tag_b = tag_fixture(%{"name" => "hollows"}, current_user)
 | 
			
		||||
      tag_a = tag_fixture(%{name: "bullets"}, current_user)
 | 
			
		||||
      tag_b = tag_fixture(%{name: "hollows"}, current_user)
 | 
			
		||||
 | 
			
		||||
      _shouldnt_return =
 | 
			
		||||
        %{
 | 
			
		||||
          "name" => "bullet",
 | 
			
		||||
          "desc" => "pews brass shell"
 | 
			
		||||
        }
 | 
			
		||||
        |> tag_fixture(user_fixture())
 | 
			
		||||
      tag_fixture(%{name: "bullet", desc: "pews brass shell"}, user_fixture())
 | 
			
		||||
 | 
			
		||||
      # name
 | 
			
		||||
      assert Containers.list_tags("bullet", current_user) == [tag_a]
 | 
			
		||||
 
 | 
			
		||||
@@ -11,19 +11,19 @@ defmodule CanneryWeb.ExportControllerTest do
 | 
			
		||||
  setup [:register_and_log_in_user]
 | 
			
		||||
 | 
			
		||||
  defp add_data(%{current_user: current_user}) do
 | 
			
		||||
    ammo_type = ammo_type_fixture(current_user)
 | 
			
		||||
    type = type_fixture(current_user)
 | 
			
		||||
    container = container_fixture(current_user)
 | 
			
		||||
    tag = tag_fixture(current_user)
 | 
			
		||||
    Containers.add_tag!(container, tag, current_user)
 | 
			
		||||
    {1, [ammo_group]} = ammo_group_fixture(ammo_type, container, current_user)
 | 
			
		||||
    shot_group = shot_group_fixture(current_user, ammo_group)
 | 
			
		||||
    ammo_group = ammo_group |> Repo.reload!()
 | 
			
		||||
    {1, [pack]} = pack_fixture(type, container, current_user)
 | 
			
		||||
    shot_record = shot_record_fixture(current_user, pack)
 | 
			
		||||
    pack = pack |> Repo.reload!()
 | 
			
		||||
 | 
			
		||||
    %{
 | 
			
		||||
      ammo_type: ammo_type,
 | 
			
		||||
      ammo_group: ammo_group,
 | 
			
		||||
      type: type,
 | 
			
		||||
      pack: pack,
 | 
			
		||||
      container: container,
 | 
			
		||||
      shot_group: shot_group,
 | 
			
		||||
      shot_record: shot_record,
 | 
			
		||||
      tag: tag
 | 
			
		||||
    }
 | 
			
		||||
  end
 | 
			
		||||
@@ -35,56 +35,66 @@ defmodule CanneryWeb.ExportControllerTest do
 | 
			
		||||
      conn: conn,
 | 
			
		||||
      current_user: current_user,
 | 
			
		||||
      container: container,
 | 
			
		||||
      ammo_type: ammo_type,
 | 
			
		||||
      ammo_group: ammo_group,
 | 
			
		||||
      shot_group: shot_group,
 | 
			
		||||
      type: type,
 | 
			
		||||
      pack: pack,
 | 
			
		||||
      shot_record: shot_record,
 | 
			
		||||
      tag: tag
 | 
			
		||||
    } do
 | 
			
		||||
      conn = get(conn, Routes.export_path(conn, :export, :json))
 | 
			
		||||
 | 
			
		||||
      ideal_ammo_group = %{
 | 
			
		||||
        "ammo_type_id" => ammo_group.ammo_type_id,
 | 
			
		||||
        "container_id" => ammo_group.container_id,
 | 
			
		||||
        "count" => ammo_group.count,
 | 
			
		||||
        "id" => ammo_group.id,
 | 
			
		||||
        "notes" => ammo_group.notes,
 | 
			
		||||
        "price_paid" => ammo_group.price_paid,
 | 
			
		||||
        "staged" => ammo_group.staged,
 | 
			
		||||
        "used_count" => ammo_group |> ActivityLog.get_used_count(current_user),
 | 
			
		||||
        "original_count" => ammo_group |> Ammo.get_original_count(current_user),
 | 
			
		||||
        "cpr" => ammo_group |> Ammo.get_cpr(current_user),
 | 
			
		||||
        "percentage_remaining" => ammo_group |> Ammo.get_percentage_remaining(current_user)
 | 
			
		||||
      ideal_pack = %{
 | 
			
		||||
        "type_id" => pack.type_id,
 | 
			
		||||
        "container_id" => pack.container_id,
 | 
			
		||||
        "count" => pack.count,
 | 
			
		||||
        "id" => pack.id,
 | 
			
		||||
        "notes" => pack.notes,
 | 
			
		||||
        "price_paid" => pack.price_paid,
 | 
			
		||||
        "lot_number" => pack.lot_number,
 | 
			
		||||
        "staged" => pack.staged,
 | 
			
		||||
        "used_count" => pack |> ActivityLog.get_used_count(current_user),
 | 
			
		||||
        "original_count" => pack |> Ammo.get_original_count(current_user),
 | 
			
		||||
        "cpr" => pack |> Ammo.get_cpr(current_user),
 | 
			
		||||
        "percentage_remaining" => pack |> Ammo.get_percentage_remaining(current_user)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      ideal_ammo_type = %{
 | 
			
		||||
        "blank" => ammo_type.blank,
 | 
			
		||||
        "bullet_core" => ammo_type.bullet_core,
 | 
			
		||||
        "bullet_type" => ammo_type.bullet_type,
 | 
			
		||||
        "caliber" => ammo_type.caliber,
 | 
			
		||||
        "cartridge" => ammo_type.cartridge,
 | 
			
		||||
        "case_material" => ammo_type.case_material,
 | 
			
		||||
        "corrosive" => ammo_type.corrosive,
 | 
			
		||||
        "desc" => ammo_type.desc,
 | 
			
		||||
        "firing_type" => ammo_type.firing_type,
 | 
			
		||||
        "grains" => ammo_type.grains,
 | 
			
		||||
        "id" => ammo_type.id,
 | 
			
		||||
        "incendiary" => ammo_type.incendiary,
 | 
			
		||||
        "jacket_type" => ammo_type.jacket_type,
 | 
			
		||||
        "manufacturer" => ammo_type.manufacturer,
 | 
			
		||||
        "muzzle_velocity" => ammo_type.muzzle_velocity,
 | 
			
		||||
        "name" => ammo_type.name,
 | 
			
		||||
        "powder_grains_per_charge" => ammo_type.powder_grains_per_charge,
 | 
			
		||||
        "powder_type" => ammo_type.powder_type,
 | 
			
		||||
        "pressure" => ammo_type.pressure,
 | 
			
		||||
        "primer_type" => ammo_type.primer_type,
 | 
			
		||||
        "tracer" => ammo_type.tracer,
 | 
			
		||||
        "upc" => ammo_type.upc,
 | 
			
		||||
        "average_cost" => ammo_type |> Ammo.get_average_cost_for_ammo_type(current_user),
 | 
			
		||||
        "round_count" => ammo_type |> Ammo.get_round_count_for_ammo_type(current_user),
 | 
			
		||||
        "used_count" => ammo_type |> ActivityLog.get_used_count_for_ammo_type(current_user),
 | 
			
		||||
        "ammo_group_count" => ammo_type |> Ammo.get_ammo_groups_count_for_type(current_user),
 | 
			
		||||
        "total_ammo_group_count" =>
 | 
			
		||||
          ammo_type |> Ammo.get_ammo_groups_count_for_type(current_user, true)
 | 
			
		||||
      ideal_type = %{
 | 
			
		||||
        "name" => type.name,
 | 
			
		||||
        "desc" => type.desc,
 | 
			
		||||
        "class" => to_string(type.class),
 | 
			
		||||
        "bullet_type" => type.bullet_type,
 | 
			
		||||
        "bullet_core" => type.bullet_core,
 | 
			
		||||
        "caliber" => type.caliber,
 | 
			
		||||
        "case_material" => type.case_material,
 | 
			
		||||
        "powder_type" => type.powder_type,
 | 
			
		||||
        "grains" => type.grains,
 | 
			
		||||
        "pressure" => type.pressure,
 | 
			
		||||
        "primer_type" => type.primer_type,
 | 
			
		||||
        "firing_type" => type.firing_type,
 | 
			
		||||
        "manufacturer" => type.manufacturer,
 | 
			
		||||
        "upc" => type.upc,
 | 
			
		||||
        "tracer" => type.tracer,
 | 
			
		||||
        "incendiary" => type.incendiary,
 | 
			
		||||
        "blank" => type.blank,
 | 
			
		||||
        "corrosive" => type.corrosive,
 | 
			
		||||
        "cartridge" => type.cartridge,
 | 
			
		||||
        "jacket_type" => type.jacket_type,
 | 
			
		||||
        "powder_grains_per_charge" => type.powder_grains_per_charge,
 | 
			
		||||
        "muzzle_velocity" => type.muzzle_velocity,
 | 
			
		||||
        "wadding" => type.wadding,
 | 
			
		||||
        "shot_type" => type.shot_type,
 | 
			
		||||
        "shot_material" => type.shot_material,
 | 
			
		||||
        "shot_size" => type.shot_size,
 | 
			
		||||
        "unfired_length" => type.unfired_length,
 | 
			
		||||
        "brass_height" => type.brass_height,
 | 
			
		||||
        "chamber_size" => type.chamber_size,
 | 
			
		||||
        "load_grains" => type.load_grains,
 | 
			
		||||
        "shot_charge_weight" => type.shot_charge_weight,
 | 
			
		||||
        "dram_equivalent" => type.dram_equivalent,
 | 
			
		||||
        "average_cost" => type |> Ammo.get_average_cost_for_type(current_user),
 | 
			
		||||
        "round_count" => type |> Ammo.get_round_count_for_type(current_user),
 | 
			
		||||
        "used_count" => type |> ActivityLog.get_used_count_for_type(current_user),
 | 
			
		||||
        "pack_count" => type |> Ammo.get_packs_count_for_type(current_user),
 | 
			
		||||
        "total_pack_count" => type |> Ammo.get_packs_count_for_type(current_user, true)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      ideal_container = %{
 | 
			
		||||
@@ -101,17 +111,16 @@ defmodule CanneryWeb.ExportControllerTest do
 | 
			
		||||
          }
 | 
			
		||||
        ],
 | 
			
		||||
        "type" => container.type,
 | 
			
		||||
        "ammo_group_count" =>
 | 
			
		||||
          container |> Ammo.get_ammo_groups_count_for_container!(current_user),
 | 
			
		||||
        "pack_count" => container |> Ammo.get_packs_count_for_container!(current_user),
 | 
			
		||||
        "round_count" => container |> Ammo.get_round_count_for_container!(current_user)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      ideal_shot_group = %{
 | 
			
		||||
        "ammo_group_id" => shot_group.ammo_group_id,
 | 
			
		||||
        "count" => shot_group.count,
 | 
			
		||||
        "date" => to_string(shot_group.date),
 | 
			
		||||
        "id" => shot_group.id,
 | 
			
		||||
        "notes" => shot_group.notes
 | 
			
		||||
      ideal_shot_record = %{
 | 
			
		||||
        "pack_id" => shot_record.pack_id,
 | 
			
		||||
        "count" => shot_record.count,
 | 
			
		||||
        "date" => to_string(shot_record.date),
 | 
			
		||||
        "id" => shot_record.id,
 | 
			
		||||
        "notes" => shot_record.notes
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      ideal_user = %{
 | 
			
		||||
@@ -126,10 +135,10 @@ defmodule CanneryWeb.ExportControllerTest do
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      json_resp = conn |> json_response(200)
 | 
			
		||||
      assert %{"ammo_groups" => [^ideal_ammo_group]} = json_resp
 | 
			
		||||
      assert %{"ammo_types" => [^ideal_ammo_type]} = json_resp
 | 
			
		||||
      assert %{"packs" => [^ideal_pack]} = json_resp
 | 
			
		||||
      assert %{"types" => [^ideal_type]} = json_resp
 | 
			
		||||
      assert %{"containers" => [^ideal_container]} = json_resp
 | 
			
		||||
      assert %{"shot_groups" => [^ideal_shot_group]} = json_resp
 | 
			
		||||
      assert %{"shot_records" => [^ideal_shot_record]} = json_resp
 | 
			
		||||
      assert %{"user" => ^ideal_user} = json_resp
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,6 @@ defmodule CanneryWeb.UserAuthTest do
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb.ConnCase, async: true
 | 
			
		||||
  import CanneryWeb.Gettext
 | 
			
		||||
  alias Cannery.Accounts
 | 
			
		||||
  alias CanneryWeb.UserAuth
 | 
			
		||||
 | 
			
		||||
@@ -148,7 +147,7 @@ defmodule CanneryWeb.UserAuthTest do
 | 
			
		||||
      assert redirected_to(conn) == Routes.user_session_path(conn, :new)
 | 
			
		||||
 | 
			
		||||
      assert get_flash(conn, :error) ==
 | 
			
		||||
               dgettext("errors", "You must confirm your account and log in to access this page.")
 | 
			
		||||
               "You must confirm your account and log in to access this page."
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "stores the path to redirect to on GET", %{conn: conn} do
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,6 @@ defmodule CanneryWeb.UserConfirmationControllerTest do
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb.ConnCase, async: true
 | 
			
		||||
  import CanneryWeb.Gettext
 | 
			
		||||
  alias Cannery.{Accounts, Repo}
 | 
			
		||||
 | 
			
		||||
  @moduletag :user_confirmation_controller_test
 | 
			
		||||
@@ -17,7 +16,7 @@ defmodule CanneryWeb.UserConfirmationControllerTest do
 | 
			
		||||
    test "renders the confirmation page", %{conn: conn} do
 | 
			
		||||
      conn = get(conn, Routes.user_confirmation_path(conn, :new))
 | 
			
		||||
      response = html_response(conn, 200)
 | 
			
		||||
      assert response =~ dgettext("actions", "Resend confirmation instructions")
 | 
			
		||||
      assert response =~ "Resend confirmation instructions"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -25,18 +24,12 @@ defmodule CanneryWeb.UserConfirmationControllerTest do
 | 
			
		||||
    @tag :capture_log
 | 
			
		||||
    test "sends a new confirmation token", %{conn: conn, user: user} do
 | 
			
		||||
      conn =
 | 
			
		||||
        post(conn, Routes.user_confirmation_path(conn, :create), %{
 | 
			
		||||
          "user" => %{"email" => user.email}
 | 
			
		||||
        })
 | 
			
		||||
        post(conn, Routes.user_confirmation_path(conn, :create), %{user: %{email: user.email}})
 | 
			
		||||
 | 
			
		||||
      assert redirected_to(conn) == "/"
 | 
			
		||||
 | 
			
		||||
      assert get_flash(conn, :info) =~
 | 
			
		||||
               dgettext(
 | 
			
		||||
                 "prompts",
 | 
			
		||||
                 "If your email is in our system and it has not been confirmed yet, " <>
 | 
			
		||||
                   "you will receive an email with instructions shortly."
 | 
			
		||||
               )
 | 
			
		||||
               "If your email is in our system and it has not been confirmed yet, you will receive an email with instructions shortly."
 | 
			
		||||
 | 
			
		||||
      assert Repo.get_by!(Accounts.UserToken, user_id: user.id).context == "confirm"
 | 
			
		||||
    end
 | 
			
		||||
@@ -45,34 +38,24 @@ defmodule CanneryWeb.UserConfirmationControllerTest do
 | 
			
		||||
      Repo.update!(Accounts.User.confirm_changeset(user))
 | 
			
		||||
 | 
			
		||||
      conn =
 | 
			
		||||
        post(conn, Routes.user_confirmation_path(conn, :create), %{
 | 
			
		||||
          "user" => %{"email" => user.email}
 | 
			
		||||
        })
 | 
			
		||||
        post(conn, Routes.user_confirmation_path(conn, :create), %{user: %{email: user.email}})
 | 
			
		||||
 | 
			
		||||
      assert redirected_to(conn) == "/"
 | 
			
		||||
 | 
			
		||||
      assert get_flash(conn, :info) =~
 | 
			
		||||
               dgettext(
 | 
			
		||||
                 "prompts",
 | 
			
		||||
                 "If your email is in our system and it has not been confirmed yet, " <>
 | 
			
		||||
                   "you will receive an email with instructions shortly."
 | 
			
		||||
               )
 | 
			
		||||
               "If your email is in our system and it has not been confirmed yet, you will receive an email with instructions shortly."
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "does not send confirmation token if email is invalid", %{conn: conn} do
 | 
			
		||||
      conn =
 | 
			
		||||
        post(conn, Routes.user_confirmation_path(conn, :create), %{
 | 
			
		||||
          "user" => %{"email" => "unknown@example.com"}
 | 
			
		||||
          user: %{email: "unknown@example.com"}
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
      assert redirected_to(conn) == "/"
 | 
			
		||||
 | 
			
		||||
      assert get_flash(conn, :info) =~
 | 
			
		||||
               dgettext(
 | 
			
		||||
                 "prompts",
 | 
			
		||||
                 "If your email is in our system and it has not been confirmed yet, " <>
 | 
			
		||||
                   "you will receive an email with instructions shortly."
 | 
			
		||||
               )
 | 
			
		||||
               "If your email is in our system and it has not been confirmed yet, you will receive an email with instructions shortly."
 | 
			
		||||
 | 
			
		||||
      assert Repo.all(Accounts.UserToken) == []
 | 
			
		||||
    end
 | 
			
		||||
@@ -87,10 +70,7 @@ defmodule CanneryWeb.UserConfirmationControllerTest do
 | 
			
		||||
 | 
			
		||||
      conn = get(conn, Routes.user_confirmation_path(conn, :confirm, token))
 | 
			
		||||
      assert redirected_to(conn) == "/"
 | 
			
		||||
 | 
			
		||||
      assert get_flash(conn, :info) =~
 | 
			
		||||
               dgettext("prompts", "%{email} confirmed successfully", email: user.email)
 | 
			
		||||
 | 
			
		||||
      assert get_flash(conn, :info) =~ "#{user.email} confirmed successfully"
 | 
			
		||||
      assert Accounts.get_user!(user.id).confirmed_at
 | 
			
		||||
      refute get_session(conn, :user_token)
 | 
			
		||||
      assert Repo.all(Accounts.UserToken) == []
 | 
			
		||||
@@ -99,8 +79,7 @@ defmodule CanneryWeb.UserConfirmationControllerTest do
 | 
			
		||||
      conn = get(conn, Routes.user_confirmation_path(conn, :confirm, token))
 | 
			
		||||
      assert redirected_to(conn) == "/"
 | 
			
		||||
 | 
			
		||||
      assert get_flash(conn, :error) =~
 | 
			
		||||
               dgettext("errors", "User confirmation link is invalid or it has expired")
 | 
			
		||||
      assert get_flash(conn, :error) =~ "User confirmation link is invalid or it has expired"
 | 
			
		||||
 | 
			
		||||
      # When logged in
 | 
			
		||||
      conn =
 | 
			
		||||
@@ -115,10 +94,7 @@ defmodule CanneryWeb.UserConfirmationControllerTest do
 | 
			
		||||
    test "does not confirm email with invalid token", %{conn: conn, user: user} do
 | 
			
		||||
      conn = get(conn, Routes.user_confirmation_path(conn, :confirm, "oops"))
 | 
			
		||||
      assert redirected_to(conn) == "/"
 | 
			
		||||
 | 
			
		||||
      assert get_flash(conn, :error) =~
 | 
			
		||||
               dgettext("errors", "User confirmation link is invalid or it has expired")
 | 
			
		||||
 | 
			
		||||
      assert get_flash(conn, :error) =~ "User confirmation link is invalid or it has expired"
 | 
			
		||||
      refute Accounts.get_user!(user.id).confirmed_at
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,6 @@ defmodule CanneryWeb.UserRegistrationControllerTest do
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb.ConnCase, async: true
 | 
			
		||||
  import CanneryWeb.Gettext
 | 
			
		||||
 | 
			
		||||
  @moduletag :user_registration_controller_test
 | 
			
		||||
 | 
			
		||||
@@ -12,8 +11,8 @@ defmodule CanneryWeb.UserRegistrationControllerTest do
 | 
			
		||||
    test "renders registration page", %{conn: conn} do
 | 
			
		||||
      conn = get(conn, Routes.user_registration_path(conn, :new))
 | 
			
		||||
      response = html_response(conn, 200)
 | 
			
		||||
      assert response =~ dgettext("actions", "Register")
 | 
			
		||||
      assert response =~ dgettext("actions", "Log in")
 | 
			
		||||
      assert response =~ "Register"
 | 
			
		||||
      assert response =~ "Log in"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "redirects if already logged in", %{conn: conn} do
 | 
			
		||||
@@ -29,11 +28,11 @@ defmodule CanneryWeb.UserRegistrationControllerTest do
 | 
			
		||||
 | 
			
		||||
      conn =
 | 
			
		||||
        post(conn, Routes.user_registration_path(conn, :create), %{
 | 
			
		||||
          "user" => valid_user_attributes(email: email)
 | 
			
		||||
          user: valid_user_attributes(email: email)
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
      assert get_session(conn, :phoenix_flash) == %{
 | 
			
		||||
               "info" => dgettext("prompts", "Please check your email to verify your account")
 | 
			
		||||
               "info" => "Please check your email to verify your account"
 | 
			
		||||
             }
 | 
			
		||||
 | 
			
		||||
      assert redirected_to(conn) =~ "/"
 | 
			
		||||
@@ -48,11 +47,11 @@ defmodule CanneryWeb.UserRegistrationControllerTest do
 | 
			
		||||
    test "render errors for invalid data", %{conn: conn} do
 | 
			
		||||
      conn =
 | 
			
		||||
        post(conn, Routes.user_registration_path(conn, :create), %{
 | 
			
		||||
          "user" => %{"email" => "with spaces", "password" => "too short"}
 | 
			
		||||
          user: %{email: "with spaces", password: "too short"}
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
      response = html_response(conn, 200)
 | 
			
		||||
      assert response =~ gettext("Register")
 | 
			
		||||
      assert response =~ "Register"
 | 
			
		||||
      assert response =~ "must have the @ sign and no spaces"
 | 
			
		||||
      assert response =~ "should be at least 12 character"
 | 
			
		||||
    end
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,6 @@ defmodule CanneryWeb.UserResetPasswordControllerTest do
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb.ConnCase, async: true
 | 
			
		||||
  import CanneryWeb.Gettext
 | 
			
		||||
  alias Cannery.{Accounts, Repo}
 | 
			
		||||
 | 
			
		||||
  @moduletag :user_reset_password_controller_test
 | 
			
		||||
@@ -17,7 +16,7 @@ defmodule CanneryWeb.UserResetPasswordControllerTest do
 | 
			
		||||
    test "renders the reset password page", %{conn: conn} do
 | 
			
		||||
      conn = get(conn, Routes.user_reset_password_path(conn, :new))
 | 
			
		||||
      response = html_response(conn, 200)
 | 
			
		||||
      assert response =~ dgettext("actions", "Forgot your password?")
 | 
			
		||||
      assert response =~ "Forgot your password?"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -25,17 +24,12 @@ defmodule CanneryWeb.UserResetPasswordControllerTest do
 | 
			
		||||
    @tag :capture_log
 | 
			
		||||
    test "sends a new reset password token", %{conn: conn, user: user} do
 | 
			
		||||
      conn =
 | 
			
		||||
        post(conn, Routes.user_reset_password_path(conn, :create), %{
 | 
			
		||||
          "user" => %{"email" => user.email}
 | 
			
		||||
        })
 | 
			
		||||
        post(conn, Routes.user_reset_password_path(conn, :create), %{user: %{email: user.email}})
 | 
			
		||||
 | 
			
		||||
      assert redirected_to(conn) == "/"
 | 
			
		||||
 | 
			
		||||
      assert get_flash(conn, :info) =~
 | 
			
		||||
               dgettext(
 | 
			
		||||
                 "prompts",
 | 
			
		||||
                 "If your email is in our system, you will receive instructions to reset your password shortly."
 | 
			
		||||
               )
 | 
			
		||||
               "If your email is in our system, you will receive instructions to reset your password shortly."
 | 
			
		||||
 | 
			
		||||
      assert Repo.get_by!(Accounts.UserToken, user_id: user.id).context == "reset_password"
 | 
			
		||||
    end
 | 
			
		||||
@@ -43,16 +37,13 @@ defmodule CanneryWeb.UserResetPasswordControllerTest do
 | 
			
		||||
    test "does not send reset password token if email is invalid", %{conn: conn} do
 | 
			
		||||
      conn =
 | 
			
		||||
        post(conn, Routes.user_reset_password_path(conn, :create), %{
 | 
			
		||||
          "user" => %{"email" => "unknown@example.com"}
 | 
			
		||||
          user: %{email: "unknown@example.com"}
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
      assert redirected_to(conn) == "/"
 | 
			
		||||
 | 
			
		||||
      assert get_flash(conn, :info) =~
 | 
			
		||||
               dgettext(
 | 
			
		||||
                 "prompts",
 | 
			
		||||
                 "If your email is in our system, you will receive instructions to reset your password shortly."
 | 
			
		||||
               )
 | 
			
		||||
               "If your email is in our system, you will receive instructions to reset your password shortly."
 | 
			
		||||
 | 
			
		||||
      assert Repo.all(Accounts.UserToken) == []
 | 
			
		||||
    end
 | 
			
		||||
@@ -70,15 +61,13 @@ defmodule CanneryWeb.UserResetPasswordControllerTest do
 | 
			
		||||
 | 
			
		||||
    test "renders reset password", %{conn: conn, token: token} do
 | 
			
		||||
      conn = get(conn, Routes.user_reset_password_path(conn, :edit, token))
 | 
			
		||||
      assert html_response(conn, 200) =~ dgettext("actions", "Reset password")
 | 
			
		||||
      assert html_response(conn, 200) =~ "Reset password"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "does not render reset password with invalid token", %{conn: conn} do
 | 
			
		||||
      conn = get(conn, Routes.user_reset_password_path(conn, :edit, "oops"))
 | 
			
		||||
      assert redirected_to(conn) == "/"
 | 
			
		||||
 | 
			
		||||
      assert get_flash(conn, :error) =~
 | 
			
		||||
               dgettext("errors", "Reset password link is invalid or it has expired")
 | 
			
		||||
      assert get_flash(conn, :error) =~ "Reset password link is invalid or it has expired"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -95,39 +84,37 @@ defmodule CanneryWeb.UserResetPasswordControllerTest do
 | 
			
		||||
    test "resets password once", %{conn: conn, user: user, token: token} do
 | 
			
		||||
      conn =
 | 
			
		||||
        put(conn, Routes.user_reset_password_path(conn, :update, token), %{
 | 
			
		||||
          "user" => %{
 | 
			
		||||
            "password" => "new valid password",
 | 
			
		||||
            "password_confirmation" => "new valid password"
 | 
			
		||||
          user: %{
 | 
			
		||||
            password: "new valid password",
 | 
			
		||||
            password_confirmation: "new valid password"
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
      assert redirected_to(conn) == Routes.user_session_path(conn, :new)
 | 
			
		||||
      refute get_session(conn, :user_token)
 | 
			
		||||
      assert get_flash(conn, :info) =~ dgettext("prompts", "Password reset successfully")
 | 
			
		||||
      assert get_flash(conn, :info) =~ "Password reset successfully"
 | 
			
		||||
      assert Accounts.get_user_by_email_and_password(user.email, "new valid password")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "does not reset password on invalid data", %{conn: conn, token: token} do
 | 
			
		||||
      conn =
 | 
			
		||||
        put(conn, Routes.user_reset_password_path(conn, :update, token), %{
 | 
			
		||||
          "user" => %{
 | 
			
		||||
            "password" => "too short",
 | 
			
		||||
            "password_confirmation" => "does not match"
 | 
			
		||||
          user: %{
 | 
			
		||||
            password: "too short",
 | 
			
		||||
            password_confirmation: "does not match"
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
      response = html_response(conn, 200)
 | 
			
		||||
      assert response =~ gettext("Reset password")
 | 
			
		||||
      assert response =~ dgettext("errors", "should be at least 12 character(s)")
 | 
			
		||||
      assert response =~ dgettext("errors", "does not match password")
 | 
			
		||||
      assert response =~ "Reset password"
 | 
			
		||||
      assert response =~ "should be at least 12 character(s)"
 | 
			
		||||
      assert response =~ "does not match password"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "does not reset password with invalid token", %{conn: conn} do
 | 
			
		||||
      conn = put(conn, Routes.user_reset_password_path(conn, :update, "oops"))
 | 
			
		||||
      assert redirected_to(conn) == "/"
 | 
			
		||||
 | 
			
		||||
      assert get_flash(conn, :error) =~
 | 
			
		||||
               dgettext("errors", "Reset password link is invalid or it has expired")
 | 
			
		||||
      assert get_flash(conn, :error) =~ "Reset password link is invalid or it has expired"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,6 @@ defmodule CanneryWeb.UserSessionControllerTest do
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb.ConnCase, async: true
 | 
			
		||||
  import CanneryWeb.Gettext
 | 
			
		||||
 | 
			
		||||
  @moduletag :user_session_controller_test
 | 
			
		||||
 | 
			
		||||
@@ -16,7 +15,7 @@ defmodule CanneryWeb.UserSessionControllerTest do
 | 
			
		||||
    test "renders log in page", %{conn: conn} do
 | 
			
		||||
      conn = get(conn, Routes.user_session_path(conn, :new))
 | 
			
		||||
      response = html_response(conn, 200)
 | 
			
		||||
      assert response =~ dgettext("actions", "Log in")
 | 
			
		||||
      assert response =~ "Log in"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "redirects if already logged in", %{conn: conn, current_user: current_user} do
 | 
			
		||||
@@ -29,7 +28,7 @@ defmodule CanneryWeb.UserSessionControllerTest do
 | 
			
		||||
    test "logs the user in", %{conn: conn, current_user: current_user} do
 | 
			
		||||
      conn =
 | 
			
		||||
        post(conn, Routes.user_session_path(conn, :create), %{
 | 
			
		||||
          "user" => %{"email" => current_user.email, "password" => valid_user_password()}
 | 
			
		||||
          user: %{email: current_user.email, password: valid_user_password()}
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
      assert get_session(conn, :user_token)
 | 
			
		||||
@@ -39,16 +38,16 @@ defmodule CanneryWeb.UserSessionControllerTest do
 | 
			
		||||
      conn = get(conn, "/")
 | 
			
		||||
      response = html_response(conn, 200)
 | 
			
		||||
      assert response =~ current_user.email
 | 
			
		||||
      assert response =~ dgettext("prompts", "Are you sure you want to log out?")
 | 
			
		||||
      assert response =~ "Are you sure you want to log out?"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "logs the user in with remember me", %{conn: conn, current_user: current_user} do
 | 
			
		||||
      conn =
 | 
			
		||||
        post(conn, Routes.user_session_path(conn, :create), %{
 | 
			
		||||
          "user" => %{
 | 
			
		||||
            "email" => current_user.email,
 | 
			
		||||
            "password" => valid_user_password(),
 | 
			
		||||
            "remember_me" => "true"
 | 
			
		||||
          user: %{
 | 
			
		||||
            email: current_user.email,
 | 
			
		||||
            password: valid_user_password(),
 | 
			
		||||
            remember_me: "true"
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
@@ -61,9 +60,9 @@ defmodule CanneryWeb.UserSessionControllerTest do
 | 
			
		||||
        conn
 | 
			
		||||
        |> init_test_session(user_return_to: "/foo/bar")
 | 
			
		||||
        |> post(Routes.user_session_path(conn, :create), %{
 | 
			
		||||
          "user" => %{
 | 
			
		||||
            "email" => current_user.email,
 | 
			
		||||
            "password" => valid_user_password()
 | 
			
		||||
          user: %{
 | 
			
		||||
            email: current_user.email,
 | 
			
		||||
            password: valid_user_password()
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
@@ -74,12 +73,12 @@ defmodule CanneryWeb.UserSessionControllerTest do
 | 
			
		||||
         %{conn: conn, current_user: current_user} do
 | 
			
		||||
      conn =
 | 
			
		||||
        post(conn, Routes.user_session_path(conn, :create), %{
 | 
			
		||||
          "user" => %{"email" => current_user.email, "password" => "bad"}
 | 
			
		||||
          user: %{email: current_user.email, password: "bad"}
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
      response = html_response(conn, 200)
 | 
			
		||||
      assert response =~ dgettext("actions", "Log in")
 | 
			
		||||
      assert response =~ dgettext("errors", "Invalid email or password")
 | 
			
		||||
      assert response =~ "Log in"
 | 
			
		||||
      assert response =~ "Invalid email or password"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -88,14 +87,14 @@ defmodule CanneryWeb.UserSessionControllerTest do
 | 
			
		||||
      conn = conn |> log_in_user(current_user) |> delete(Routes.user_session_path(conn, :delete))
 | 
			
		||||
      assert redirected_to(conn) == "/"
 | 
			
		||||
      refute get_session(conn, :user_token)
 | 
			
		||||
      assert get_flash(conn, :info) =~ gettext("Logged out successfully")
 | 
			
		||||
      assert get_flash(conn, :info) =~ "Logged out successfully"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "succeeds even if the user is not logged in", %{conn: conn} do
 | 
			
		||||
      conn = delete(conn, Routes.user_session_path(conn, :delete))
 | 
			
		||||
      assert redirected_to(conn) == "/"
 | 
			
		||||
      refute get_session(conn, :user_token)
 | 
			
		||||
      assert get_flash(conn, :info) =~ gettext("Logged out successfully")
 | 
			
		||||
      assert get_flash(conn, :info) =~ "Logged out successfully"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,6 @@ defmodule CanneryWeb.UserSettingsControllerTest do
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb.ConnCase, async: true
 | 
			
		||||
  import CanneryWeb.Gettext
 | 
			
		||||
  alias Cannery.Accounts
 | 
			
		||||
 | 
			
		||||
  @moduletag :user_settings_controller_test
 | 
			
		||||
@@ -15,7 +14,7 @@ defmodule CanneryWeb.UserSettingsControllerTest do
 | 
			
		||||
    test "renders settings page", %{conn: conn} do
 | 
			
		||||
      conn = get(conn, Routes.user_settings_path(conn, :edit))
 | 
			
		||||
      response = html_response(conn, 200)
 | 
			
		||||
      assert response =~ gettext("Settings")
 | 
			
		||||
      assert response =~ "Settings"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "redirects if user is not logged in" do
 | 
			
		||||
@@ -30,40 +29,36 @@ defmodule CanneryWeb.UserSettingsControllerTest do
 | 
			
		||||
         %{conn: conn, current_user: current_user} do
 | 
			
		||||
      new_password_conn =
 | 
			
		||||
        put(conn, Routes.user_settings_path(conn, :update), %{
 | 
			
		||||
          "action" => "update_password",
 | 
			
		||||
          "current_password" => valid_user_password(),
 | 
			
		||||
          "user" => %{
 | 
			
		||||
            "password" => "new valid password",
 | 
			
		||||
            "password_confirmation" => "new valid password"
 | 
			
		||||
          action: "update_password",
 | 
			
		||||
          current_password: valid_user_password(),
 | 
			
		||||
          user: %{
 | 
			
		||||
            password: "new valid password",
 | 
			
		||||
            password_confirmation: "new valid password"
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
      assert redirected_to(new_password_conn) == Routes.user_settings_path(conn, :edit)
 | 
			
		||||
      assert get_session(new_password_conn, :user_token) != get_session(conn, :user_token)
 | 
			
		||||
 | 
			
		||||
      assert get_flash(new_password_conn, :info) =~
 | 
			
		||||
               dgettext("actions", "Password updated successfully")
 | 
			
		||||
 | 
			
		||||
      assert get_flash(new_password_conn, :info) =~ "Password updated successfully"
 | 
			
		||||
      assert Accounts.get_user_by_email_and_password(current_user.email, "new valid password")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "does not update password on invalid data", %{conn: conn} do
 | 
			
		||||
      old_password_conn =
 | 
			
		||||
        put(conn, Routes.user_settings_path(conn, :update), %{
 | 
			
		||||
          "action" => "update_password",
 | 
			
		||||
          "current_password" => "invalid",
 | 
			
		||||
          "user" => %{
 | 
			
		||||
            "password" => "too short",
 | 
			
		||||
            "password_confirmation" => "does not match"
 | 
			
		||||
          action: "update_password",
 | 
			
		||||
          current_password: "invalid",
 | 
			
		||||
          user: %{
 | 
			
		||||
            password: "too short",
 | 
			
		||||
            password_confirmation: "does not match"
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
      response = html_response(old_password_conn, 200)
 | 
			
		||||
      assert response =~ gettext("Settings")
 | 
			
		||||
      assert response =~ dgettext("errors", "should be at least 12 character(s)")
 | 
			
		||||
      assert response =~ dgettext("errors", "does not match password")
 | 
			
		||||
      assert response =~ dgettext("errors", "is not valid")
 | 
			
		||||
 | 
			
		||||
      assert response =~ "Settings"
 | 
			
		||||
      assert response =~ "should be at least 12 character(s)"
 | 
			
		||||
      assert response =~ "does not match password"
 | 
			
		||||
      assert response =~ "is not valid"
 | 
			
		||||
      assert get_session(old_password_conn, :user_token) == get_session(conn, :user_token)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
@@ -73,18 +68,15 @@ defmodule CanneryWeb.UserSettingsControllerTest do
 | 
			
		||||
    test "updates the user email", %{conn: conn, current_user: current_user} do
 | 
			
		||||
      conn =
 | 
			
		||||
        put(conn, Routes.user_settings_path(conn, :update), %{
 | 
			
		||||
          "action" => "update_email",
 | 
			
		||||
          "current_password" => valid_user_password(),
 | 
			
		||||
          "user" => %{"email" => unique_user_email()}
 | 
			
		||||
          action: "update_email",
 | 
			
		||||
          current_password: valid_user_password(),
 | 
			
		||||
          user: %{email: unique_user_email()}
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
      assert redirected_to(conn) == Routes.user_settings_path(conn, :edit)
 | 
			
		||||
 | 
			
		||||
      assert get_flash(conn, :info) =~
 | 
			
		||||
               dgettext(
 | 
			
		||||
                 "prompts",
 | 
			
		||||
                 "A link to confirm your email change has been sent to the new address."
 | 
			
		||||
               )
 | 
			
		||||
               "A link to confirm your email change has been sent to the new address."
 | 
			
		||||
 | 
			
		||||
      assert Accounts.get_user_by_email(current_user.email)
 | 
			
		||||
    end
 | 
			
		||||
@@ -92,15 +84,15 @@ defmodule CanneryWeb.UserSettingsControllerTest do
 | 
			
		||||
    test "does not update email on invalid data", %{conn: conn} do
 | 
			
		||||
      conn =
 | 
			
		||||
        put(conn, Routes.user_settings_path(conn, :update), %{
 | 
			
		||||
          "action" => "update_email",
 | 
			
		||||
          "current_password" => "invalid",
 | 
			
		||||
          "user" => %{"email" => "with spaces"}
 | 
			
		||||
          action: "update_email",
 | 
			
		||||
          current_password: "invalid",
 | 
			
		||||
          user: %{email: "with spaces"}
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
      response = html_response(conn, 200)
 | 
			
		||||
      assert response =~ gettext("Settings")
 | 
			
		||||
      assert response =~ dgettext("errors", "must have the @ sign and no spaces")
 | 
			
		||||
      assert response =~ dgettext("errors", "is not valid")
 | 
			
		||||
      assert response =~ "Settings"
 | 
			
		||||
      assert response =~ "must have the @ sign and no spaces"
 | 
			
		||||
      assert response =~ "is not valid"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -124,24 +116,19 @@ defmodule CanneryWeb.UserSettingsControllerTest do
 | 
			
		||||
         %{conn: conn, current_user: current_user, token: token, email: email} do
 | 
			
		||||
      conn = get(conn, Routes.user_settings_path(conn, :confirm_email, token))
 | 
			
		||||
      assert redirected_to(conn) == Routes.user_settings_path(conn, :edit)
 | 
			
		||||
      assert get_flash(conn, :info) =~ dgettext("prompts", "Email changed successfully")
 | 
			
		||||
      assert get_flash(conn, :info) =~ "Email changed successfully"
 | 
			
		||||
      refute Accounts.get_user_by_email(current_user.email)
 | 
			
		||||
      assert Accounts.get_user_by_email(email)
 | 
			
		||||
 | 
			
		||||
      conn = get(conn, Routes.user_settings_path(conn, :confirm_email, token))
 | 
			
		||||
      assert redirected_to(conn) == Routes.user_settings_path(conn, :edit)
 | 
			
		||||
 | 
			
		||||
      assert get_flash(conn, :error) =~
 | 
			
		||||
               dgettext("errors", "Email change link is invalid or it has expired")
 | 
			
		||||
      assert get_flash(conn, :error) =~ "Email change link is invalid or it has expired"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "does not update email with invalid token", %{conn: conn, current_user: current_user} do
 | 
			
		||||
      conn = get(conn, Routes.user_settings_path(conn, :confirm_email, "oops"))
 | 
			
		||||
      assert redirected_to(conn) == Routes.user_settings_path(conn, :edit)
 | 
			
		||||
 | 
			
		||||
      assert get_flash(conn, :error) =~
 | 
			
		||||
               dgettext("errors", "Email change link is invalid or it has expired")
 | 
			
		||||
 | 
			
		||||
      assert get_flash(conn, :error) =~ "Email change link is invalid or it has expired"
 | 
			
		||||
      assert Accounts.get_user_by_email(current_user.email)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,481 +0,0 @@
 | 
			
		||||
defmodule CanneryWeb.AmmoGroupLiveTest do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  Tests ammo group live pages
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb.ConnCase
 | 
			
		||||
  import Phoenix.LiveViewTest
 | 
			
		||||
  import CanneryWeb.Gettext
 | 
			
		||||
  alias Cannery.{Ammo, Repo}
 | 
			
		||||
 | 
			
		||||
  @moduletag :ammo_group_live_test
 | 
			
		||||
  @shot_group_create_attrs %{"ammo_left" => 5, "notes" => "some notes"}
 | 
			
		||||
  @shot_group_update_attrs %{
 | 
			
		||||
    "count" => 5,
 | 
			
		||||
    "date" => ~N[2022-02-13 03:17:00],
 | 
			
		||||
    "notes" => "some updated notes"
 | 
			
		||||
  }
 | 
			
		||||
  @create_attrs %{"count" => 42, "notes" => "some notes", "price_paid" => 120.5}
 | 
			
		||||
  @update_attrs %{"count" => 43, "notes" => "some updated notes", "price_paid" => 456.7}
 | 
			
		||||
  @ammo_group_create_limit 10_000
 | 
			
		||||
  @empty_attrs %{
 | 
			
		||||
    "price_paid" => 50,
 | 
			
		||||
    "count" => 20
 | 
			
		||||
  }
 | 
			
		||||
  @shot_group_attrs %{
 | 
			
		||||
    "price_paid" => 50,
 | 
			
		||||
    "count" => 20
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  # @invalid_attrs %{count: -1, notes: nil, price_paid: nil}
 | 
			
		||||
 | 
			
		||||
  defp create_ammo_group(%{current_user: current_user}) do
 | 
			
		||||
    ammo_type = ammo_type_fixture(current_user)
 | 
			
		||||
    container = container_fixture(current_user)
 | 
			
		||||
    {1, [ammo_group]} = ammo_group_fixture(@create_attrs, ammo_type, container, current_user)
 | 
			
		||||
 | 
			
		||||
    %{ammo_type: ammo_type, ammo_group: ammo_group, container: container}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp create_shot_group(%{current_user: current_user, ammo_group: ammo_group}) do
 | 
			
		||||
    shot_group = shot_group_fixture(@shot_group_update_attrs, current_user, ammo_group)
 | 
			
		||||
    ammo_group = ammo_group |> Repo.reload!()
 | 
			
		||||
 | 
			
		||||
    %{ammo_group: ammo_group, shot_group: shot_group}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp create_empty_ammo_group(%{
 | 
			
		||||
         current_user: current_user,
 | 
			
		||||
         ammo_type: ammo_type,
 | 
			
		||||
         container: container
 | 
			
		||||
       }) do
 | 
			
		||||
    {1, [ammo_group]} = ammo_group_fixture(@empty_attrs, ammo_type, container, current_user)
 | 
			
		||||
    shot_group = shot_group_fixture(@shot_group_attrs, current_user, ammo_group)
 | 
			
		||||
    ammo_group = ammo_group |> Repo.reload!()
 | 
			
		||||
    %{empty_ammo_group: ammo_group, shot_group: shot_group}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "Index of ammo group" do
 | 
			
		||||
    setup [:register_and_log_in_user, :create_ammo_group]
 | 
			
		||||
 | 
			
		||||
    test "lists all ammo_groups", %{conn: conn, ammo_group: ammo_group} do
 | 
			
		||||
      {:ok, _index_live, html} = live(conn, Routes.ammo_group_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      ammo_group = ammo_group |> Repo.preload(:ammo_type)
 | 
			
		||||
      assert html =~ gettext("Ammo")
 | 
			
		||||
      assert html =~ ammo_group.ammo_type.name
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "can sort by type",
 | 
			
		||||
         %{conn: conn, container: container, current_user: current_user} do
 | 
			
		||||
      rifle_type = ammo_type_fixture(%{"type" => "rifle"}, current_user)
 | 
			
		||||
      {1, [rifle_ammo_group]} = ammo_group_fixture(rifle_type, container, current_user)
 | 
			
		||||
      shotgun_type = ammo_type_fixture(%{"type" => "shotgun"}, current_user)
 | 
			
		||||
      {1, [shotgun_ammo_group]} = ammo_group_fixture(shotgun_type, container, current_user)
 | 
			
		||||
      pistol_type = ammo_type_fixture(%{"type" => "pistol"}, current_user)
 | 
			
		||||
      {1, [pistol_ammo_group]} = ammo_group_fixture(pistol_type, container, current_user)
 | 
			
		||||
 | 
			
		||||
      {:ok, index_live, html} = live(conn, Routes.ammo_group_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ "All"
 | 
			
		||||
 | 
			
		||||
      assert html =~ rifle_ammo_group.ammo_type.name
 | 
			
		||||
      assert html =~ shotgun_ammo_group.ammo_type.name
 | 
			
		||||
      assert html =~ pistol_ammo_group.ammo_type.name
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_type"]/)
 | 
			
		||||
        |> render_change(ammo_type: %{type: :rifle})
 | 
			
		||||
 | 
			
		||||
      assert html =~ rifle_ammo_group.ammo_type.name
 | 
			
		||||
      refute html =~ shotgun_ammo_group.ammo_type.name
 | 
			
		||||
      refute html =~ pistol_ammo_group.ammo_type.name
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_type"]/)
 | 
			
		||||
        |> render_change(ammo_type: %{type: :shotgun})
 | 
			
		||||
 | 
			
		||||
      refute html =~ rifle_ammo_group.ammo_type.name
 | 
			
		||||
      assert html =~ shotgun_ammo_group.ammo_type.name
 | 
			
		||||
      refute html =~ pistol_ammo_group.ammo_type.name
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_type"]/)
 | 
			
		||||
        |> render_change(ammo_type: %{type: :pistol})
 | 
			
		||||
 | 
			
		||||
      refute html =~ rifle_ammo_group.ammo_type.name
 | 
			
		||||
      refute html =~ shotgun_ammo_group.ammo_type.name
 | 
			
		||||
      assert html =~ pistol_ammo_group.ammo_type.name
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_type"]/)
 | 
			
		||||
        |> render_change(ammo_type: %{type: :all})
 | 
			
		||||
 | 
			
		||||
      assert html =~ rifle_ammo_group.ammo_type.name
 | 
			
		||||
      assert html =~ shotgun_ammo_group.ammo_type.name
 | 
			
		||||
      assert html =~ pistol_ammo_group.ammo_type.name
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "can search for ammo_groups", %{conn: conn, ammo_group: ammo_group} do
 | 
			
		||||
      {:ok, index_live, html} = live(conn, Routes.ammo_group_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      ammo_group = ammo_group |> Repo.preload(:ammo_type)
 | 
			
		||||
 | 
			
		||||
      assert html =~ ammo_group.ammo_type.name
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/,
 | 
			
		||||
               search: %{search_term: ammo_group.ammo_type.name}
 | 
			
		||||
             )
 | 
			
		||||
             |> render_change() =~ ammo_group.ammo_type.name
 | 
			
		||||
 | 
			
		||||
      assert_patch(
 | 
			
		||||
        index_live,
 | 
			
		||||
        Routes.ammo_group_index_path(conn, :search, ammo_group.ammo_type.name)
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      refute index_live
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/, search: %{search_term: "something_else"})
 | 
			
		||||
             |> render_change() =~ ammo_group.ammo_type.name
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.ammo_group_index_path(conn, :search, "something_else"))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/, search: %{search_term: ""})
 | 
			
		||||
             |> render_change() =~ ammo_group.ammo_type.name
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.ammo_group_index_path(conn, :index))
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "saves a single new ammo_group", %{conn: conn} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.ammo_group_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element("a", dgettext("actions", "Add Ammo")) |> render_click() =~
 | 
			
		||||
               dgettext("actions", "Add Ammo")
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.ammo_group_index_path(conn, :new))
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#ammo_group-form", ammo_group: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "can't be blank")
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#ammo_group-form", ammo_group: @create_attrs)
 | 
			
		||||
        |> render_submit()
 | 
			
		||||
        |> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ dgettext("prompts", "Ammo added successfully")
 | 
			
		||||
      assert html =~ "\n42\n"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "saves multiple new ammo_groups", %{conn: conn, current_user: current_user} do
 | 
			
		||||
      multiplier = 25
 | 
			
		||||
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.ammo_group_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element("a", dgettext("actions", "Add Ammo")) |> render_click() =~
 | 
			
		||||
               dgettext("actions", "Add Ammo")
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.ammo_group_index_path(conn, :new))
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#ammo_group-form", ammo_group: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "can't be blank")
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#ammo_group-form",
 | 
			
		||||
          ammo_group: @create_attrs |> Map.put("multiplier", to_string(multiplier))
 | 
			
		||||
        )
 | 
			
		||||
        |> render_submit()
 | 
			
		||||
        |> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ dgettext("prompts", "Ammo added successfully")
 | 
			
		||||
      assert Ammo.list_ammo_groups(nil, :all, current_user) |> Enum.count() == multiplier + 1
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "does not save invalid number of new ammo_groups", %{conn: conn} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.ammo_group_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element("a", dgettext("actions", "Add Ammo")) |> render_click() =~
 | 
			
		||||
               dgettext("actions", "Add Ammo")
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.ammo_group_index_path(conn, :new))
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#ammo_group-form", ammo_group: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "can't be blank")
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#ammo_group-form", ammo_group: @create_attrs |> Map.put("multiplier", "0"))
 | 
			
		||||
             |> render_submit() =~
 | 
			
		||||
               dgettext(
 | 
			
		||||
                 "errors",
 | 
			
		||||
                 "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}",
 | 
			
		||||
                 multiplier: 0,
 | 
			
		||||
                 max: @ammo_group_create_limit
 | 
			
		||||
               )
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#ammo_group-form",
 | 
			
		||||
               ammo_group:
 | 
			
		||||
                 @create_attrs |> Map.put("multiplier", to_string(@ammo_group_create_limit + 1))
 | 
			
		||||
             )
 | 
			
		||||
             |> render_submit() =~
 | 
			
		||||
               dgettext(
 | 
			
		||||
                 "errors",
 | 
			
		||||
                 "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}",
 | 
			
		||||
                 multiplier: @ammo_group_create_limit + 1,
 | 
			
		||||
                 max: @ammo_group_create_limit
 | 
			
		||||
               )
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "updates ammo_group in listing", %{conn: conn, ammo_group: ammo_group} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.ammo_group_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> element(~s/a[aria-label="Edit ammo group of #{ammo_group.count} bullets"]/)
 | 
			
		||||
             |> render_click() =~
 | 
			
		||||
               gettext("Edit ammo")
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.ammo_group_index_path(conn, :edit, ammo_group))
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#ammo_group-form", ammo_group: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "can't be blank")
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#ammo_group-form", ammo_group: @update_attrs)
 | 
			
		||||
        |> render_submit()
 | 
			
		||||
        |> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ dgettext("prompts", "Ammo updated successfully")
 | 
			
		||||
      assert html =~ "\n43\n"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "clones ammo_group in listing", %{conn: conn, ammo_group: ammo_group} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.ammo_group_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> element(~s/a[aria-label="Clone ammo group of #{ammo_group.count} bullets"]/)
 | 
			
		||||
        |> render_click()
 | 
			
		||||
 | 
			
		||||
      assert html =~ dgettext("actions", "Add Ammo")
 | 
			
		||||
      assert html =~ gettext("$%{amount}", amount: display_currency(120.5))
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.ammo_group_index_path(conn, :clone, ammo_group))
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#ammo_group-form", ammo_group: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "can't be blank")
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#ammo_group-form")
 | 
			
		||||
        |> render_submit()
 | 
			
		||||
        |> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ dgettext("prompts", "Ammo added successfully")
 | 
			
		||||
      assert html =~ "\n42\n"
 | 
			
		||||
      assert html =~ gettext("$%{amount}", amount: display_currency(120.5))
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "clones ammo_group in listing with updates", %{conn: conn, ammo_group: ammo_group} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.ammo_group_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> element(~s/a[aria-label="Clone ammo group of #{ammo_group.count} bullets"]/)
 | 
			
		||||
        |> render_click()
 | 
			
		||||
 | 
			
		||||
      assert html =~ dgettext("actions", "Add Ammo")
 | 
			
		||||
      assert html =~ gettext("$%{amount}", amount: display_currency(120.5))
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.ammo_group_index_path(conn, :clone, ammo_group))
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#ammo_group-form", ammo_group: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "can't be blank")
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#ammo_group-form", ammo_group: Map.merge(@create_attrs, %{"count" => 43}))
 | 
			
		||||
        |> render_submit()
 | 
			
		||||
        |> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ dgettext("prompts", "Ammo added successfully")
 | 
			
		||||
      assert html =~ "\n43\n"
 | 
			
		||||
      assert html =~ gettext("$%{amount}", amount: display_currency(120.5))
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "deletes ammo_group in listing", %{conn: conn, ammo_group: ammo_group} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.ammo_group_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> element(~s/a[aria-label="Delete ammo group of #{ammo_group.count} bullets"]/)
 | 
			
		||||
             |> render_click()
 | 
			
		||||
 | 
			
		||||
      refute has_element?(index_live, "#ammo_group-#{ammo_group.id}")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "saves new shot_group", %{conn: conn, ammo_group: ammo_group} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.ammo_group_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element("a", dgettext("actions", "Record shots")) |> render_click() =~
 | 
			
		||||
               gettext("Record shots")
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.ammo_group_index_path(conn, :add_shot_group, ammo_group))
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#shot_group-form", shot_group: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "is invalid")
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#shot-group-form", shot_group: @shot_group_create_attrs)
 | 
			
		||||
        |> render_submit()
 | 
			
		||||
        |> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ dgettext("prompts", "Shots recorded successfully")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    @spec display_currency(float()) :: String.t()
 | 
			
		||||
    defp display_currency(float), do: :erlang.float_to_binary(float, decimals: 2)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "Index of empty ammo group" do
 | 
			
		||||
    setup [:register_and_log_in_user, :create_ammo_group, :create_empty_ammo_group]
 | 
			
		||||
 | 
			
		||||
    test "hides empty ammo groups by default", %{
 | 
			
		||||
      conn: conn,
 | 
			
		||||
      empty_ammo_group: ammo_group,
 | 
			
		||||
      current_user: current_user
 | 
			
		||||
    } do
 | 
			
		||||
      {:ok, show_live, html} = live(conn, Routes.ammo_group_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ dgettext("actions", "Show used")
 | 
			
		||||
      refute html =~ gettext("$%{amount}", amount: display_currency(50.00))
 | 
			
		||||
 | 
			
		||||
      percentage = ammo_group |> Ammo.get_percentage_remaining(current_user)
 | 
			
		||||
      refute html =~ "\n#{gettext("%{percentage}%", percentage: percentage)}\n"
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        show_live
 | 
			
		||||
        |> element(~s/input[type="checkbox"][aria-labelledby="toggle_show_used-label"}]/)
 | 
			
		||||
        |> render_click()
 | 
			
		||||
 | 
			
		||||
      assert html =~ gettext("$%{amount}", amount: display_currency(50.00))
 | 
			
		||||
 | 
			
		||||
      percentage = ammo_group |> Ammo.get_percentage_remaining(current_user)
 | 
			
		||||
      assert html =~ "\n#{gettext("%{percentage}%", percentage: percentage)}\n"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "Show ammo group" do
 | 
			
		||||
    setup [:register_and_log_in_user, :create_ammo_group]
 | 
			
		||||
 | 
			
		||||
    test "displays ammo_group", %{conn: conn, ammo_group: ammo_group} do
 | 
			
		||||
      {:ok, _show_live, html} = live(conn, Routes.ammo_group_show_path(conn, :show, ammo_group))
 | 
			
		||||
 | 
			
		||||
      ammo_group = ammo_group |> Repo.preload(:ammo_type)
 | 
			
		||||
      assert html =~ gettext("Show Ammo")
 | 
			
		||||
      assert html =~ ammo_group.ammo_type.name
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "updates ammo_group within modal", %{conn: conn, ammo_group: ammo_group} do
 | 
			
		||||
      {:ok, show_live, _html} = live(conn, Routes.ammo_group_show_path(conn, :show, ammo_group))
 | 
			
		||||
 | 
			
		||||
      assert show_live
 | 
			
		||||
             |> element(~s/a[aria-label="Edit ammo group of #{ammo_group.count} bullets"]/)
 | 
			
		||||
             |> render_click() =~
 | 
			
		||||
               gettext("Edit Ammo")
 | 
			
		||||
 | 
			
		||||
      assert_patch(show_live, Routes.ammo_group_show_path(conn, :edit, ammo_group))
 | 
			
		||||
 | 
			
		||||
      # assert show_live
 | 
			
		||||
      #        |> form("#ammo_group-form", ammo_group: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "can't be blank")
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        show_live
 | 
			
		||||
        |> form("#ammo_group-form", ammo_group: @update_attrs)
 | 
			
		||||
        |> render_submit()
 | 
			
		||||
        |> follow_redirect(conn, Routes.ammo_group_show_path(conn, :show, ammo_group))
 | 
			
		||||
 | 
			
		||||
      assert html =~ dgettext("prompts", "Ammo updated successfully")
 | 
			
		||||
      assert html =~ "some updated notes"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "saves new shot_group", %{conn: conn, ammo_group: ammo_group} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.ammo_group_show_path(conn, :show, ammo_group))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element("a", dgettext("actions", "Record shots")) |> render_click() =~
 | 
			
		||||
               gettext("Record shots")
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.ammo_group_show_path(conn, :add_shot_group, ammo_group))
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#shot_group-form", shot_group: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "is invalid")
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#shot-group-form", shot_group: @shot_group_create_attrs)
 | 
			
		||||
        |> render_submit()
 | 
			
		||||
        |> follow_redirect(conn, Routes.ammo_group_show_path(conn, :show, ammo_group))
 | 
			
		||||
 | 
			
		||||
      assert html =~ dgettext("prompts", "Shots recorded successfully")
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "Show ammo group with shot group" do
 | 
			
		||||
    setup [:register_and_log_in_user, :create_ammo_group, :create_shot_group]
 | 
			
		||||
 | 
			
		||||
    test "updates shot_group in listing",
 | 
			
		||||
         %{conn: conn, ammo_group: ammo_group, shot_group: shot_group} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.ammo_group_show_path(conn, :edit, ammo_group))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> element(~s/a[aria-label="Edit shot group of #{shot_group.count} shots"]/)
 | 
			
		||||
             |> render_click() =~
 | 
			
		||||
               gettext("Edit Shot Records")
 | 
			
		||||
 | 
			
		||||
      assert_patch(
 | 
			
		||||
        index_live,
 | 
			
		||||
        Routes.ammo_group_show_path(conn, :edit_shot_group, ammo_group, shot_group)
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#shot_group-form", shot_group: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "is invalid")
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#shot-group-form", shot_group: @shot_group_update_attrs)
 | 
			
		||||
        |> render_submit()
 | 
			
		||||
        |> follow_redirect(conn, Routes.ammo_group_show_path(conn, :show, ammo_group))
 | 
			
		||||
 | 
			
		||||
      assert html =~ dgettext("actions", "Shot records updated successfully")
 | 
			
		||||
      assert html =~ "some updated notes"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "deletes shot_group in listing",
 | 
			
		||||
         %{conn: conn, ammo_group: ammo_group, shot_group: shot_group} do
 | 
			
		||||
      {:ok, index_live, _html} =
 | 
			
		||||
        live(conn, Routes.ammo_group_show_path(conn, :edit_shot_group, ammo_group, shot_group))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> element(~s/a[aria-label="Delete shot record of #{shot_group.count} shots"]/)
 | 
			
		||||
             |> render_click()
 | 
			
		||||
 | 
			
		||||
      refute has_element?(index_live, "#shot_group-#{shot_group.id}")
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -1,409 +0,0 @@
 | 
			
		||||
defmodule CanneryWeb.AmmoTypeLiveTest do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  Tests the ammo type liveview
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb.ConnCase
 | 
			
		||||
  import Phoenix.LiveViewTest
 | 
			
		||||
  import CanneryWeb.Gettext
 | 
			
		||||
  alias Cannery.{Ammo, Repo}
 | 
			
		||||
 | 
			
		||||
  @moduletag :ammo_type_live_test
 | 
			
		||||
 | 
			
		||||
  @create_attrs %{
 | 
			
		||||
    "bullet_type" => "some bullet_type",
 | 
			
		||||
    "case_material" => "some case_material",
 | 
			
		||||
    "desc" => "some desc",
 | 
			
		||||
    "manufacturer" => "some manufacturer",
 | 
			
		||||
    "name" => "some name",
 | 
			
		||||
    "grains" => 120
 | 
			
		||||
  }
 | 
			
		||||
  @update_attrs %{
 | 
			
		||||
    "bullet_type" => "some updated bullet_type",
 | 
			
		||||
    "case_material" => "some updated case_material",
 | 
			
		||||
    "desc" => "some updated desc",
 | 
			
		||||
    "manufacturer" => "some updated manufacturer",
 | 
			
		||||
    "name" => "some updated name",
 | 
			
		||||
    "grains" => 456
 | 
			
		||||
  }
 | 
			
		||||
  @ammo_group_attrs %{
 | 
			
		||||
    "notes" => "some ammo group",
 | 
			
		||||
    "count" => 20
 | 
			
		||||
  }
 | 
			
		||||
  @shot_group_attrs %{
 | 
			
		||||
    "notes" => "some shot group",
 | 
			
		||||
    "count" => 20
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  # @invalid_attrs %{
 | 
			
		||||
  #   "bullet_type" => nil,
 | 
			
		||||
  #   "case_material" => nil,
 | 
			
		||||
  #   "desc" => nil,
 | 
			
		||||
  #   "manufacturer" => nil,
 | 
			
		||||
  #   "name" => nil,
 | 
			
		||||
  #   "grains" => nil
 | 
			
		||||
  # }
 | 
			
		||||
 | 
			
		||||
  defp create_ammo_type(%{current_user: current_user}) do
 | 
			
		||||
    %{ammo_type: ammo_type_fixture(@create_attrs, current_user)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp create_ammo_group(%{ammo_type: ammo_type, current_user: current_user}) do
 | 
			
		||||
    container = container_fixture(current_user)
 | 
			
		||||
    {1, [ammo_group]} = ammo_group_fixture(@ammo_group_attrs, ammo_type, container, current_user)
 | 
			
		||||
 | 
			
		||||
    %{ammo_group: ammo_group, container: container}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp create_empty_ammo_group(%{ammo_type: ammo_type, current_user: current_user}) do
 | 
			
		||||
    container = container_fixture(current_user)
 | 
			
		||||
    {1, [ammo_group]} = ammo_group_fixture(@ammo_group_attrs, ammo_type, container, current_user)
 | 
			
		||||
    shot_group = shot_group_fixture(@shot_group_attrs, current_user, ammo_group)
 | 
			
		||||
    ammo_group = ammo_group |> Repo.reload!()
 | 
			
		||||
 | 
			
		||||
    %{ammo_group: ammo_group, container: container, shot_group: shot_group}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "Index" do
 | 
			
		||||
    setup [:register_and_log_in_user, :create_ammo_type]
 | 
			
		||||
 | 
			
		||||
    test "lists all ammo_types", %{conn: conn, ammo_type: ammo_type} do
 | 
			
		||||
      {:ok, _index_live, html} = live(conn, Routes.ammo_type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ gettext("Catalog")
 | 
			
		||||
      assert html =~ ammo_type.bullet_type
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "can sort by type", %{conn: conn, current_user: current_user} do
 | 
			
		||||
      rifle_type = ammo_type_fixture(%{"type" => "rifle"}, current_user)
 | 
			
		||||
      shotgun_type = ammo_type_fixture(%{"type" => "shotgun"}, current_user)
 | 
			
		||||
      pistol_type = ammo_type_fixture(%{"type" => "pistol"}, current_user)
 | 
			
		||||
 | 
			
		||||
      {:ok, index_live, html} = live(conn, Routes.ammo_type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ "All"
 | 
			
		||||
 | 
			
		||||
      assert html =~ rifle_type.name
 | 
			
		||||
      assert html =~ shotgun_type.name
 | 
			
		||||
      assert html =~ pistol_type.name
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_type"]/)
 | 
			
		||||
        |> render_change(ammo_type: %{type: :rifle})
 | 
			
		||||
 | 
			
		||||
      assert html =~ rifle_type.name
 | 
			
		||||
      refute html =~ shotgun_type.name
 | 
			
		||||
      refute html =~ pistol_type.name
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_type"]/)
 | 
			
		||||
        |> render_change(ammo_type: %{type: :shotgun})
 | 
			
		||||
 | 
			
		||||
      refute html =~ rifle_type.name
 | 
			
		||||
      assert html =~ shotgun_type.name
 | 
			
		||||
      refute html =~ pistol_type.name
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_type"]/)
 | 
			
		||||
        |> render_change(ammo_type: %{type: :pistol})
 | 
			
		||||
 | 
			
		||||
      refute html =~ rifle_type.name
 | 
			
		||||
      refute html =~ shotgun_type.name
 | 
			
		||||
      assert html =~ pistol_type.name
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_type"]/)
 | 
			
		||||
        |> render_change(ammo_type: %{type: :all})
 | 
			
		||||
 | 
			
		||||
      assert html =~ rifle_type.name
 | 
			
		||||
      assert html =~ shotgun_type.name
 | 
			
		||||
      assert html =~ pistol_type.name
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "can search for ammo_type", %{conn: conn, ammo_type: ammo_type} do
 | 
			
		||||
      {:ok, index_live, html} = live(conn, Routes.ammo_type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ ammo_type.bullet_type
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/)
 | 
			
		||||
             |> render_change(search: %{search_term: ammo_type.bullet_type}) =~
 | 
			
		||||
               ammo_type.bullet_type
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.ammo_type_index_path(conn, :search, ammo_type.bullet_type))
 | 
			
		||||
 | 
			
		||||
      refute index_live
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/)
 | 
			
		||||
             |> render_change(search: %{search_term: "something_else"}) =~ ammo_type.bullet_type
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.ammo_type_index_path(conn, :search, "something_else"))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/)
 | 
			
		||||
             |> render_change(search: %{search_term: ""}) =~ ammo_type.bullet_type
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.ammo_type_index_path(conn, :index))
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "saves new ammo_type", %{conn: conn, current_user: current_user, ammo_type: ammo_type} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.ammo_type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element("a", dgettext("actions", "New Ammo type")) |> render_click() =~
 | 
			
		||||
               gettext("New Ammo type")
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.ammo_type_index_path(conn, :new))
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#ammo_type-form", ammo_type: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "can't be blank")
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#ammo_type-form")
 | 
			
		||||
        |> render_submit(ammo_type: @create_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.ammo_type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      ammo_type = ammo_type.id |> Ammo.get_ammo_type!(current_user)
 | 
			
		||||
      assert html =~ dgettext("prompts", "%{name} created successfully", name: ammo_type.name)
 | 
			
		||||
      assert html =~ "some bullet_type"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "updates ammo_type in listing",
 | 
			
		||||
         %{conn: conn, current_user: current_user, ammo_type: ammo_type} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.ammo_type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element(~s/a[aria-label="Edit #{ammo_type.name}"]/) |> render_click() =~
 | 
			
		||||
               gettext("Edit %{ammo_type_name}", ammo_type_name: ammo_type.name)
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.ammo_type_index_path(conn, :edit, ammo_type))
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#ammo_type-form", ammo_type: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "can't be blank")
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#ammo_type-form")
 | 
			
		||||
        |> render_submit(ammo_type: @update_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.ammo_type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      ammo_type = ammo_type.id |> Ammo.get_ammo_type!(current_user)
 | 
			
		||||
      assert html =~ dgettext("prompts", "%{name} updated successfully", name: ammo_type.name)
 | 
			
		||||
      assert html =~ "some updated bullet_type"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "clones ammo_type in listing",
 | 
			
		||||
         %{conn: conn, current_user: current_user, ammo_type: ammo_type} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.ammo_type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      html = index_live |> element(~s/a[aria-label="Clone #{ammo_type.name}"]/) |> render_click()
 | 
			
		||||
      assert html =~ gettext("New Ammo type")
 | 
			
		||||
      assert html =~ "some bullet_type"
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.ammo_type_index_path(conn, :clone, ammo_type))
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#ammo_type-form", ammo_type: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "can't be blank")
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#ammo_type-form")
 | 
			
		||||
        |> render_submit(ammo_type: @create_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.ammo_type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      ammo_type = ammo_type.id |> Ammo.get_ammo_type!(current_user)
 | 
			
		||||
      assert html =~ dgettext("prompts", "%{name} created successfully", name: ammo_type.name)
 | 
			
		||||
      assert html =~ "some bullet_type"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "clones ammo_type in listing with updates",
 | 
			
		||||
         %{conn: conn, current_user: current_user, ammo_type: ammo_type} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.ammo_type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      html = index_live |> element(~s/a[aria-label="Clone #{ammo_type.name}"]/) |> render_click()
 | 
			
		||||
      assert html =~ gettext("New Ammo type")
 | 
			
		||||
      assert html =~ "some bullet_type"
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.ammo_type_index_path(conn, :clone, ammo_type))
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#ammo_type-form", ammo_type: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "can't be blank")
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#ammo_type-form")
 | 
			
		||||
        |> render_submit(
 | 
			
		||||
          ammo_type: Map.merge(@create_attrs, %{"bullet_type" => "some updated bullet_type"})
 | 
			
		||||
        )
 | 
			
		||||
        |> follow_redirect(conn, Routes.ammo_type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      ammo_type = ammo_type.id |> Ammo.get_ammo_type!(current_user)
 | 
			
		||||
      assert html =~ dgettext("prompts", "%{name} created successfully", name: ammo_type.name)
 | 
			
		||||
      assert html =~ "some updated bullet_type"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "deletes ammo_type in listing", %{conn: conn, ammo_type: ammo_type} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.ammo_type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element(~s/a[aria-label="Delete #{ammo_type.name}"]/) |> render_click()
 | 
			
		||||
      refute has_element?(index_live, "#ammo_type-#{ammo_type.id}")
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "Index with ammo group" do
 | 
			
		||||
    setup [:register_and_log_in_user, :create_ammo_type, :create_ammo_group]
 | 
			
		||||
 | 
			
		||||
    test "shows used ammo groups on toggle",
 | 
			
		||||
         %{conn: conn, ammo_group: ammo_group, current_user: current_user} do
 | 
			
		||||
      {:ok, index_live, html} = live(conn, Routes.ammo_type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ dgettext("actions", "Show used")
 | 
			
		||||
      refute html =~ gettext("Used rounds")
 | 
			
		||||
      refute html =~ gettext("Total ever rounds")
 | 
			
		||||
      refute html =~ gettext("Used packs")
 | 
			
		||||
      refute html =~ gettext("Total ever packs")
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> element(~s/input[type="checkbox"][aria-labelledby="toggle_show_used-label"}]/)
 | 
			
		||||
        |> render_click()
 | 
			
		||||
 | 
			
		||||
      assert html =~ gettext("Used rounds")
 | 
			
		||||
      assert html =~ gettext("Total ever rounds")
 | 
			
		||||
      assert html =~ gettext("Used packs")
 | 
			
		||||
      assert html =~ gettext("Total ever packs")
 | 
			
		||||
 | 
			
		||||
      assert html =~ "\n20\n"
 | 
			
		||||
      assert html =~ "\n0\n"
 | 
			
		||||
      assert html =~ "\n1\n"
 | 
			
		||||
 | 
			
		||||
      shot_group_fixture(%{"count" => 5}, current_user, ammo_group)
 | 
			
		||||
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.ammo_type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> element(~s/input[type="checkbox"][aria-labelledby="toggle_show_used-label"}]/)
 | 
			
		||||
        |> render_click()
 | 
			
		||||
 | 
			
		||||
      assert html =~ "\n15\n"
 | 
			
		||||
      assert html =~ "\n5\n"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "Show ammo type" do
 | 
			
		||||
    setup [:register_and_log_in_user, :create_ammo_type]
 | 
			
		||||
 | 
			
		||||
    test "displays ammo_type", %{
 | 
			
		||||
      conn: conn,
 | 
			
		||||
      ammo_type: %{name: name, bullet_type: bullet_type} = ammo_type
 | 
			
		||||
    } do
 | 
			
		||||
      {:ok, _show_live, html} = live(conn, Routes.ammo_type_show_path(conn, :show, ammo_type))
 | 
			
		||||
 | 
			
		||||
      assert html =~ name
 | 
			
		||||
      assert html =~ bullet_type
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "updates ammo_type within modal",
 | 
			
		||||
         %{conn: conn, current_user: current_user, ammo_type: %{name: name} = ammo_type} do
 | 
			
		||||
      {:ok, show_live, _html} = live(conn, Routes.ammo_type_show_path(conn, :show, ammo_type))
 | 
			
		||||
 | 
			
		||||
      assert show_live |> element(~s/a[aria-label="Edit #{ammo_type.name}"]/) |> render_click() =~
 | 
			
		||||
               gettext("Edit %{ammo_type_name}", ammo_type_name: name)
 | 
			
		||||
 | 
			
		||||
      assert_patch(show_live, Routes.ammo_type_show_path(conn, :edit, ammo_type))
 | 
			
		||||
 | 
			
		||||
      # assert show_live
 | 
			
		||||
      #        |> form("#ammo_type-form", ammo_type: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "can't be blank")
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        show_live
 | 
			
		||||
        |> form("#ammo_type-form")
 | 
			
		||||
        |> render_submit(ammo_type: @update_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.ammo_type_show_path(conn, :show, ammo_type))
 | 
			
		||||
 | 
			
		||||
      ammo_type = ammo_type.id |> Ammo.get_ammo_type!(current_user)
 | 
			
		||||
      assert html =~ dgettext("prompts", "%{name} updated successfully", name: ammo_type.name)
 | 
			
		||||
      assert html =~ "some updated bullet_type"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "Show ammo type with ammo group" do
 | 
			
		||||
    setup [:register_and_log_in_user, :create_ammo_type, :create_ammo_group]
 | 
			
		||||
 | 
			
		||||
    test "displays ammo group", %{
 | 
			
		||||
      conn: conn,
 | 
			
		||||
      ammo_type: %{name: ammo_type_name} = ammo_type,
 | 
			
		||||
      container: %{name: container_name}
 | 
			
		||||
    } do
 | 
			
		||||
      {:ok, _show_live, html} = live(conn, Routes.ammo_type_show_path(conn, :show, ammo_type))
 | 
			
		||||
 | 
			
		||||
      assert html =~ ammo_type_name
 | 
			
		||||
      assert html =~ "\n20\n"
 | 
			
		||||
      assert html =~ container_name
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "displays ammo group in table",
 | 
			
		||||
         %{conn: conn, ammo_type: ammo_type, container: %{name: container_name}} do
 | 
			
		||||
      {:ok, show_live, _html} = live(conn, Routes.ammo_type_show_path(conn, :show, ammo_type))
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        show_live
 | 
			
		||||
        |> element(~s/input[type="checkbox"][aria-labelledby="toggle_table-label"}]/)
 | 
			
		||||
        |> render_click()
 | 
			
		||||
 | 
			
		||||
      assert html =~ "\n20\n"
 | 
			
		||||
      assert html =~ container_name
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "Show ammo type with empty ammo group" do
 | 
			
		||||
    setup [:register_and_log_in_user, :create_ammo_type, :create_empty_ammo_group]
 | 
			
		||||
 | 
			
		||||
    test "displays empty ammo groups on toggle",
 | 
			
		||||
         %{conn: conn, ammo_type: ammo_type, container: %{name: container_name}} do
 | 
			
		||||
      {:ok, show_live, html} = live(conn, Routes.ammo_type_show_path(conn, :show, ammo_type))
 | 
			
		||||
 | 
			
		||||
      assert html =~ dgettext("actions", "Show used")
 | 
			
		||||
      refute html =~ "\n20\n"
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        show_live
 | 
			
		||||
        |> element(~s/input[type="checkbox"][aria-labelledby="toggle_show_used-label"}]/)
 | 
			
		||||
        |> render_click()
 | 
			
		||||
 | 
			
		||||
      assert html =~ "\n20\n"
 | 
			
		||||
      assert html =~ "Empty"
 | 
			
		||||
      assert html =~ container_name
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "displays empty ammo groups in table on toggle",
 | 
			
		||||
         %{conn: conn, ammo_type: ammo_type, container: %{name: container_name}} do
 | 
			
		||||
      {:ok, show_live, _html} = live(conn, Routes.ammo_type_show_path(conn, :show, ammo_type))
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        show_live
 | 
			
		||||
        |> element(~s/input[type="checkbox"][aria-labelledby="toggle_table-label"}]/)
 | 
			
		||||
        |> render_click()
 | 
			
		||||
 | 
			
		||||
      assert html =~ dgettext("actions", "Show used")
 | 
			
		||||
      refute html =~ "\n20\n"
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        show_live
 | 
			
		||||
        |> element(~s/input[type="checkbox"][aria-labelledby="toggle_show_used-label"}]/)
 | 
			
		||||
        |> render_click()
 | 
			
		||||
 | 
			
		||||
      assert html =~ "\n20\n"
 | 
			
		||||
      assert html =~ "Empty"
 | 
			
		||||
      assert html =~ container_name
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -5,48 +5,46 @@ defmodule CanneryWeb.ContainerLiveTest do
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb.ConnCase
 | 
			
		||||
  import Phoenix.LiveViewTest
 | 
			
		||||
  import CanneryWeb.Gettext
 | 
			
		||||
  alias Cannery.Containers
 | 
			
		||||
 | 
			
		||||
  @moduletag :container_live_test
 | 
			
		||||
 | 
			
		||||
  @create_attrs %{
 | 
			
		||||
    "desc" => "some desc",
 | 
			
		||||
    "location" => "some location",
 | 
			
		||||
    "name" => "some name",
 | 
			
		||||
    "type" => "some type"
 | 
			
		||||
    desc: "some desc",
 | 
			
		||||
    location: "some location",
 | 
			
		||||
    name: "some name",
 | 
			
		||||
    type: "some type"
 | 
			
		||||
  }
 | 
			
		||||
  @update_attrs %{
 | 
			
		||||
    "desc" => "some updated desc",
 | 
			
		||||
    "location" => "some updated location",
 | 
			
		||||
    "name" => "some updated name",
 | 
			
		||||
    "type" => "some updated type"
 | 
			
		||||
    desc: "some updated desc",
 | 
			
		||||
    location: "some updated location",
 | 
			
		||||
    name: "some updated name",
 | 
			
		||||
    type: "some updated type"
 | 
			
		||||
  }
 | 
			
		||||
  @ammo_type_attrs %{
 | 
			
		||||
    "bullet_type" => "some bullet_type",
 | 
			
		||||
    "case_material" => "some case_material",
 | 
			
		||||
    "desc" => "some desc",
 | 
			
		||||
    "manufacturer" => "some manufacturer",
 | 
			
		||||
    "name" => "some name",
 | 
			
		||||
    "grains" => 120
 | 
			
		||||
  @invalid_attrs %{desc: nil, location: nil, name: nil, type: nil}
 | 
			
		||||
  @type_attrs %{
 | 
			
		||||
    bullet_type: "some bullet_type",
 | 
			
		||||
    case_material: "some case_material",
 | 
			
		||||
    desc: "some desc",
 | 
			
		||||
    manufacturer: "some manufacturer",
 | 
			
		||||
    name: "some name",
 | 
			
		||||
    grains: 120
 | 
			
		||||
  }
 | 
			
		||||
  @ammo_group_attrs %{
 | 
			
		||||
    "notes" => "some ammo group",
 | 
			
		||||
    "count" => 20
 | 
			
		||||
  @pack_attrs %{
 | 
			
		||||
    notes: "some pack",
 | 
			
		||||
    count: 20
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  # @invalid_attrs %{desc: nil, location: nil, name: nil, type: nil}
 | 
			
		||||
 | 
			
		||||
  defp create_container(%{current_user: current_user}) do
 | 
			
		||||
    container = container_fixture(@create_attrs, current_user)
 | 
			
		||||
    %{container: container}
 | 
			
		||||
    [container: container]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp create_ammo_group(%{container: container, current_user: current_user}) do
 | 
			
		||||
    ammo_type = ammo_type_fixture(@ammo_type_attrs, current_user)
 | 
			
		||||
    {1, [ammo_group]} = ammo_group_fixture(@ammo_group_attrs, ammo_type, container, current_user)
 | 
			
		||||
  defp create_pack(%{container: container, current_user: current_user}) do
 | 
			
		||||
    type = type_fixture(@type_attrs, current_user)
 | 
			
		||||
    {1, [pack]} = pack_fixture(@pack_attrs, type, container, current_user)
 | 
			
		||||
 | 
			
		||||
    %{ammo_type: ammo_type, ammo_group: ammo_group}
 | 
			
		||||
    [type: type, pack: pack]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "Index" do
 | 
			
		||||
@@ -55,7 +53,7 @@ defmodule CanneryWeb.ContainerLiveTest do
 | 
			
		||||
    test "lists all containers", %{conn: conn, container: container} do
 | 
			
		||||
      {:ok, _index_live, html} = live(conn, Routes.container_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ gettext("Containers")
 | 
			
		||||
      assert html =~ "Containers"
 | 
			
		||||
      assert html =~ container.location
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
@@ -67,7 +65,7 @@ defmodule CanneryWeb.ContainerLiveTest do
 | 
			
		||||
        |> element(~s/input[type="checkbox"][aria-labelledby="toggle_table-label"}]/)
 | 
			
		||||
        |> render_click()
 | 
			
		||||
 | 
			
		||||
      assert html =~ gettext("Containers")
 | 
			
		||||
      assert html =~ "Containers"
 | 
			
		||||
      assert html =~ container.location
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
@@ -77,22 +75,20 @@ defmodule CanneryWeb.ContainerLiveTest do
 | 
			
		||||
      assert html =~ container.location
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/,
 | 
			
		||||
               search: %{search_term: container.location}
 | 
			
		||||
             )
 | 
			
		||||
             |> render_change() =~ container.location
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/)
 | 
			
		||||
             |> render_change(search: %{search_term: container.location}) =~ container.location
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.container_index_path(conn, :search, container.location))
 | 
			
		||||
 | 
			
		||||
      refute index_live
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/, search: %{search_term: "something_else"})
 | 
			
		||||
             |> render_change() =~ container.location
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/)
 | 
			
		||||
             |> render_change(search: %{search_term: "something_else"}) =~ container.location
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.container_index_path(conn, :search, "something_else"))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/, search: %{search_term: ""})
 | 
			
		||||
             |> render_change() =~ container.location
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/)
 | 
			
		||||
             |> render_change(search: %{search_term: ""}) =~ container.location
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.container_index_path(conn, :index))
 | 
			
		||||
    end
 | 
			
		||||
@@ -100,22 +96,20 @@ defmodule CanneryWeb.ContainerLiveTest do
 | 
			
		||||
    test "saves new container", %{conn: conn, container: container} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.container_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element("a", dgettext("actions", "New Container")) |> render_click() =~
 | 
			
		||||
               gettext("New Container")
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element("a", "New Container") |> render_click() =~ "New Container"
 | 
			
		||||
      assert_patch(index_live, Routes.container_index_path(conn, :new))
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#container-form", container: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "can't be blank")
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#container-form")
 | 
			
		||||
             |> render_change(container: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#container-form", container: @create_attrs)
 | 
			
		||||
        |> render_submit()
 | 
			
		||||
        |> form("#container-form")
 | 
			
		||||
        |> render_submit(container: @create_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.container_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ dgettext("prompts", "%{name} created successfully", name: container.name)
 | 
			
		||||
      assert html =~ "#{container.name} created successfully"
 | 
			
		||||
      assert html =~ "some location"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
@@ -127,22 +121,22 @@ defmodule CanneryWeb.ContainerLiveTest do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.container_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element(~s/a[aria-label="Edit #{container.name}"]/) |> render_click() =~
 | 
			
		||||
               gettext("Edit %{name}", name: container.name)
 | 
			
		||||
               "Edit #{container.name}"
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.container_index_path(conn, :edit, container))
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#container-form", container: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "can't be blank")
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#container-form")
 | 
			
		||||
             |> render_change(container: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#container-form", container: @update_attrs)
 | 
			
		||||
        |> render_submit()
 | 
			
		||||
        |> form("#container-form")
 | 
			
		||||
        |> render_submit(container: @update_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.container_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      container = container.id |> Containers.get_container!(current_user)
 | 
			
		||||
      assert html =~ dgettext("prompts", "%{name} updated successfully", name: container.name)
 | 
			
		||||
      assert html =~ "#{container.name} updated successfully"
 | 
			
		||||
      assert html =~ "some updated location"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
@@ -154,23 +148,23 @@ defmodule CanneryWeb.ContainerLiveTest do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.container_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      html = index_live |> element(~s/a[aria-label="Clone #{container.name}"]/) |> render_click()
 | 
			
		||||
      assert html =~ gettext("New Container")
 | 
			
		||||
      assert html =~ "New Container"
 | 
			
		||||
      assert html =~ "some location"
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.container_index_path(conn, :clone, container))
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#container-form", container: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "can't be blank")
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#container-form")
 | 
			
		||||
             |> render_change(container: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#container-form", container: @create_attrs)
 | 
			
		||||
        |> render_submit()
 | 
			
		||||
        |> form("#container-form")
 | 
			
		||||
        |> render_submit(container: @create_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.container_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      container = container.id |> Containers.get_container!(current_user)
 | 
			
		||||
      assert html =~ dgettext("prompts", "%{name} created successfully", name: container.name)
 | 
			
		||||
      assert html =~ "#{container.name} created successfully"
 | 
			
		||||
      assert html =~ "some location"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
@@ -182,30 +176,29 @@ defmodule CanneryWeb.ContainerLiveTest do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.container_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element(~s/a[aria-label="Clone #{container.name}"]/) |> render_click() =~
 | 
			
		||||
               gettext("New Container")
 | 
			
		||||
               "New Container"
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.container_index_path(conn, :clone, container))
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#container-form", container: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "can't be blank")
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#container-form")
 | 
			
		||||
             |> render_change(container: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#container-form",
 | 
			
		||||
        |> form("#container-form")
 | 
			
		||||
        |> render_submit(
 | 
			
		||||
          container: Map.merge(@create_attrs, %{location: "some updated location"})
 | 
			
		||||
        )
 | 
			
		||||
        |> render_submit()
 | 
			
		||||
        |> follow_redirect(conn, Routes.container_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      container = container.id |> Containers.get_container!(current_user)
 | 
			
		||||
      assert html =~ dgettext("prompts", "%{name} created successfully", name: container.name)
 | 
			
		||||
      assert html =~ "#{container.name} created successfully"
 | 
			
		||||
      assert html =~ "some updated location"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "deletes container in listing", %{conn: conn, container: container} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.container_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element(~s/a[aria-label="Delete #{container.name}"]/) |> render_click()
 | 
			
		||||
      refute has_element?(index_live, "#container-#{container.id}")
 | 
			
		||||
    end
 | 
			
		||||
@@ -219,7 +212,6 @@ defmodule CanneryWeb.ContainerLiveTest do
 | 
			
		||||
      container: %{name: name, location: location} = container
 | 
			
		||||
    } do
 | 
			
		||||
      {:ok, _show_live, html} = live(conn, Routes.container_show_path(conn, :show, container))
 | 
			
		||||
 | 
			
		||||
      assert html =~ name
 | 
			
		||||
      assert html =~ location
 | 
			
		||||
    end
 | 
			
		||||
@@ -232,93 +224,93 @@ defmodule CanneryWeb.ContainerLiveTest do
 | 
			
		||||
      {:ok, show_live, _html} = live(conn, Routes.container_show_path(conn, :show, container))
 | 
			
		||||
 | 
			
		||||
      assert show_live |> element(~s/a[aria-label="Edit #{container.name}"]/) |> render_click() =~
 | 
			
		||||
               gettext("Edit %{name}", name: container.name)
 | 
			
		||||
               "Edit #{container.name}"
 | 
			
		||||
 | 
			
		||||
      assert_patch(show_live, Routes.container_show_path(conn, :edit, container))
 | 
			
		||||
 | 
			
		||||
      # assert show_live
 | 
			
		||||
      #        |> form("#container-form", container: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "can't be blank")
 | 
			
		||||
      assert show_live
 | 
			
		||||
             |> form("#container-form")
 | 
			
		||||
             |> render_change(container: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        show_live
 | 
			
		||||
        |> form("#container-form", container: @update_attrs)
 | 
			
		||||
        |> render_submit()
 | 
			
		||||
        |> form("#container-form")
 | 
			
		||||
        |> render_submit(container: @update_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.container_show_path(conn, :show, container))
 | 
			
		||||
 | 
			
		||||
      container = container.id |> Containers.get_container!(current_user)
 | 
			
		||||
      assert html =~ dgettext("prompts", "%{name} updated successfully", name: container.name)
 | 
			
		||||
      assert html =~ "#{container.name} updated successfully"
 | 
			
		||||
      assert html =~ "some updated location"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "can sort by type",
 | 
			
		||||
         %{conn: conn, container: container, current_user: current_user} do
 | 
			
		||||
      rifle_type = ammo_type_fixture(%{"type" => "rifle"}, current_user)
 | 
			
		||||
      {1, [rifle_ammo_group]} = ammo_group_fixture(rifle_type, container, current_user)
 | 
			
		||||
      shotgun_type = ammo_type_fixture(%{"type" => "shotgun"}, current_user)
 | 
			
		||||
      {1, [shotgun_ammo_group]} = ammo_group_fixture(shotgun_type, container, current_user)
 | 
			
		||||
      pistol_type = ammo_type_fixture(%{"type" => "pistol"}, current_user)
 | 
			
		||||
      {1, [pistol_ammo_group]} = ammo_group_fixture(pistol_type, container, current_user)
 | 
			
		||||
      rifle_type = type_fixture(%{class: :rifle}, current_user)
 | 
			
		||||
      {1, [rifle_pack]} = pack_fixture(rifle_type, container, current_user)
 | 
			
		||||
      shotgun_type = type_fixture(%{class: :shotgun}, current_user)
 | 
			
		||||
      {1, [shotgun_pack]} = pack_fixture(shotgun_type, container, current_user)
 | 
			
		||||
      pistol_type = type_fixture(%{class: :pistol}, current_user)
 | 
			
		||||
      {1, [pistol_pack]} = pack_fixture(pistol_type, container, current_user)
 | 
			
		||||
 | 
			
		||||
      {:ok, index_live, html} = live(conn, Routes.container_show_path(conn, :show, container))
 | 
			
		||||
 | 
			
		||||
      assert html =~ "All"
 | 
			
		||||
 | 
			
		||||
      assert html =~ rifle_ammo_group.ammo_type.name
 | 
			
		||||
      assert html =~ shotgun_ammo_group.ammo_type.name
 | 
			
		||||
      assert html =~ pistol_ammo_group.ammo_type.name
 | 
			
		||||
      assert html =~ rifle_pack.type.name
 | 
			
		||||
      assert html =~ shotgun_pack.type.name
 | 
			
		||||
      assert html =~ pistol_pack.type.name
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_type"]/)
 | 
			
		||||
        |> render_change(ammo_type: %{type: :rifle})
 | 
			
		||||
        |> form(~s/form[phx-change="change_class"]/)
 | 
			
		||||
        |> render_change(type: %{class: :rifle})
 | 
			
		||||
 | 
			
		||||
      assert html =~ rifle_ammo_group.ammo_type.name
 | 
			
		||||
      refute html =~ shotgun_ammo_group.ammo_type.name
 | 
			
		||||
      refute html =~ pistol_ammo_group.ammo_type.name
 | 
			
		||||
      assert html =~ rifle_pack.type.name
 | 
			
		||||
      refute html =~ shotgun_pack.type.name
 | 
			
		||||
      refute html =~ pistol_pack.type.name
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_type"]/)
 | 
			
		||||
        |> render_change(ammo_type: %{type: :shotgun})
 | 
			
		||||
        |> form(~s/form[phx-change="change_class"]/)
 | 
			
		||||
        |> render_change(type: %{class: :shotgun})
 | 
			
		||||
 | 
			
		||||
      refute html =~ rifle_ammo_group.ammo_type.name
 | 
			
		||||
      assert html =~ shotgun_ammo_group.ammo_type.name
 | 
			
		||||
      refute html =~ pistol_ammo_group.ammo_type.name
 | 
			
		||||
      refute html =~ rifle_pack.type.name
 | 
			
		||||
      assert html =~ shotgun_pack.type.name
 | 
			
		||||
      refute html =~ pistol_pack.type.name
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_type"]/)
 | 
			
		||||
        |> render_change(ammo_type: %{type: :pistol})
 | 
			
		||||
        |> form(~s/form[phx-change="change_class"]/)
 | 
			
		||||
        |> render_change(type: %{class: :pistol})
 | 
			
		||||
 | 
			
		||||
      refute html =~ rifle_ammo_group.ammo_type.name
 | 
			
		||||
      refute html =~ shotgun_ammo_group.ammo_type.name
 | 
			
		||||
      assert html =~ pistol_ammo_group.ammo_type.name
 | 
			
		||||
      refute html =~ rifle_pack.type.name
 | 
			
		||||
      refute html =~ shotgun_pack.type.name
 | 
			
		||||
      assert html =~ pistol_pack.type.name
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_type"]/)
 | 
			
		||||
        |> render_change(ammo_type: %{type: :all})
 | 
			
		||||
        |> form(~s/form[phx-change="change_class"]/)
 | 
			
		||||
        |> render_change(type: %{class: :all})
 | 
			
		||||
 | 
			
		||||
      assert html =~ rifle_ammo_group.ammo_type.name
 | 
			
		||||
      assert html =~ shotgun_ammo_group.ammo_type.name
 | 
			
		||||
      assert html =~ pistol_ammo_group.ammo_type.name
 | 
			
		||||
      assert html =~ rifle_pack.type.name
 | 
			
		||||
      assert html =~ shotgun_pack.type.name
 | 
			
		||||
      assert html =~ pistol_pack.type.name
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "Show with ammo group" do
 | 
			
		||||
    setup [:register_and_log_in_user, :create_container, :create_ammo_group]
 | 
			
		||||
  describe "Show with pack" do
 | 
			
		||||
    setup [:register_and_log_in_user, :create_container, :create_pack]
 | 
			
		||||
 | 
			
		||||
    test "displays ammo group",
 | 
			
		||||
         %{conn: conn, ammo_type: %{name: ammo_type_name}, container: container} do
 | 
			
		||||
    test "displays pack",
 | 
			
		||||
         %{conn: conn, type: %{name: type_name}, container: container} do
 | 
			
		||||
      {:ok, _show_live, html} = live(conn, Routes.container_show_path(conn, :show, container))
 | 
			
		||||
 | 
			
		||||
      assert html =~ ammo_type_name
 | 
			
		||||
      assert html =~ type_name
 | 
			
		||||
      assert html =~ "\n20\n"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "displays ammo group in table",
 | 
			
		||||
         %{conn: conn, ammo_type: %{name: ammo_type_name}, container: container} do
 | 
			
		||||
    test "displays pack in table",
 | 
			
		||||
         %{conn: conn, type: %{name: type_name}, container: container} do
 | 
			
		||||
      {:ok, show_live, _html} = live(conn, Routes.container_show_path(conn, :show, container))
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
@@ -326,7 +318,7 @@ defmodule CanneryWeb.ContainerLiveTest do
 | 
			
		||||
        |> element(~s/input[type="checkbox"][aria-labelledby="toggle_table-label"}]/)
 | 
			
		||||
        |> render_click()
 | 
			
		||||
 | 
			
		||||
      assert html =~ ammo_type_name
 | 
			
		||||
      assert html =~ type_name
 | 
			
		||||
      assert html =~ "\n20\n"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
@@ -5,14 +5,13 @@ defmodule CanneryWeb.HomeLiveTest do
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb.ConnCase
 | 
			
		||||
  import Phoenix.LiveViewTest
 | 
			
		||||
  import CanneryWeb.Gettext
 | 
			
		||||
 | 
			
		||||
  @moduletag :home_live_test
 | 
			
		||||
 | 
			
		||||
  test "disconnected and connected render", %{conn: conn} do
 | 
			
		||||
    {:ok, home_live, disconnected_html} = live(conn, "/")
 | 
			
		||||
    assert disconnected_html =~ gettext("Welcome to %{name}", name: "Cannery")
 | 
			
		||||
    assert render(home_live) =~ gettext("Welcome to %{name}", name: "Cannery")
 | 
			
		||||
    assert disconnected_html =~ "Welcome to Cannery"
 | 
			
		||||
    assert render(home_live) =~ "Welcome to Cannery"
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  test "displays version number", %{conn: conn} do
 | 
			
		||||
 
 | 
			
		||||
@@ -5,13 +5,12 @@ defmodule CanneryWeb.InviteLiveTest do
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb.ConnCase
 | 
			
		||||
  import Phoenix.LiveViewTest
 | 
			
		||||
  import CanneryWeb.Gettext
 | 
			
		||||
  alias Cannery.Accounts.Invites
 | 
			
		||||
 | 
			
		||||
  @moduletag :invite_live_test
 | 
			
		||||
  @create_attrs %{"name" => "some name"}
 | 
			
		||||
  @update_attrs %{"name" => "some updated name"}
 | 
			
		||||
  # @invalid_attrs %{"name" => nil}
 | 
			
		||||
  @create_attrs %{name: "some name"}
 | 
			
		||||
  @update_attrs %{name: "some updated name"}
 | 
			
		||||
  @invalid_attrs %{name: nil}
 | 
			
		||||
 | 
			
		||||
  describe "Index" do
 | 
			
		||||
    setup [:register_and_log_in_user]
 | 
			
		||||
@@ -24,32 +23,27 @@ defmodule CanneryWeb.InviteLiveTest do
 | 
			
		||||
    test "lists all invites", %{conn: conn, invite: invite} do
 | 
			
		||||
      {:ok, _index_live, html} = live(conn, Routes.invite_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ gettext("Invites")
 | 
			
		||||
      assert html =~ "Invites"
 | 
			
		||||
      assert html =~ invite.name
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "saves new invite", %{conn: conn} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.invite_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element("a", dgettext("actions", "Create Invite")) |> render_click() =~
 | 
			
		||||
               gettext("New Invite")
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element("a", "Create Invite") |> render_click() =~ "New Invite"
 | 
			
		||||
      assert_patch(index_live, Routes.invite_index_path(conn, :new))
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#invite-form", invite: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "can't be blank")
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#invite-form")
 | 
			
		||||
             |> render_change(invite: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _live, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#invite-form", invite: @create_attrs)
 | 
			
		||||
        |> render_submit()
 | 
			
		||||
        |> form("#invite-form")
 | 
			
		||||
        |> render_submit(invite: @create_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.invite_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~
 | 
			
		||||
               dgettext("prompts", "%{invite_name} created successfully", invite_name: "some name")
 | 
			
		||||
 | 
			
		||||
      assert html =~ "some name"
 | 
			
		||||
      assert html =~ "some name created successfully"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "updates invite in listing", %{conn: conn, invite: invite} do
 | 
			
		||||
@@ -57,27 +51,21 @@ defmodule CanneryWeb.InviteLiveTest do
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> element(~s/a[aria-label="Edit invite for #{invite.name}"]/)
 | 
			
		||||
             |> render_click() =~
 | 
			
		||||
               gettext("Edit Invite")
 | 
			
		||||
             |> render_click() =~ "Edit Invite"
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.invite_index_path(conn, :edit, invite))
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#invite-form", invite: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "can't be blank")
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#invite-form")
 | 
			
		||||
             |> render_change(invite: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _live, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#invite-form", invite: @update_attrs)
 | 
			
		||||
        |> render_submit()
 | 
			
		||||
        |> form("#invite-form")
 | 
			
		||||
        |> render_submit(invite: @update_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.invite_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~
 | 
			
		||||
               dgettext("prompts", "%{invite_name} updated successfully",
 | 
			
		||||
                 invite_name: "some updated name"
 | 
			
		||||
               )
 | 
			
		||||
 | 
			
		||||
      assert html =~ "some updated name"
 | 
			
		||||
      assert html =~ "some updated name updated successfully"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "deletes invite in listing", %{conn: conn, invite: invite} do
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										459
									
								
								test/cannery_web/live/pack_live_test.exs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										459
									
								
								test/cannery_web/live/pack_live_test.exs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,459 @@
 | 
			
		||||
defmodule CanneryWeb.PackLiveTest do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  Tests pack live pages
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb.ConnCase
 | 
			
		||||
  import Phoenix.LiveViewTest
 | 
			
		||||
  alias Cannery.{Ammo, Repo}
 | 
			
		||||
 | 
			
		||||
  @moduletag :pack_live_test
 | 
			
		||||
  @create_attrs %{count: 42, notes: "some notes", price_paid: 120.5}
 | 
			
		||||
  @update_attrs %{count: 43, notes: "some updated notes", price_paid: 456.7}
 | 
			
		||||
  @invalid_attrs %{count: nil, notes: nil, price_paid: nil}
 | 
			
		||||
  @pack_create_limit 10_000
 | 
			
		||||
  @shot_record_create_attrs %{ammo_left: 5, notes: "some notes"}
 | 
			
		||||
  @shot_record_update_attrs %{
 | 
			
		||||
    count: 5,
 | 
			
		||||
    date: ~N[2022-02-13 03:17:00],
 | 
			
		||||
    notes: "some updated notes"
 | 
			
		||||
  }
 | 
			
		||||
  @shot_record_invalid_attrs %{ammo_left: nil, count: nil, notes: nil}
 | 
			
		||||
  @empty_attrs %{
 | 
			
		||||
    price_paid: 50,
 | 
			
		||||
    count: 20
 | 
			
		||||
  }
 | 
			
		||||
  @shot_record_attrs %{
 | 
			
		||||
    price_paid: 50,
 | 
			
		||||
    count: 20
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  defp create_pack(%{current_user: current_user}) do
 | 
			
		||||
    type = type_fixture(current_user)
 | 
			
		||||
    container = container_fixture(current_user)
 | 
			
		||||
    {1, [pack]} = pack_fixture(@create_attrs, type, container, current_user)
 | 
			
		||||
    [type: type, pack: pack, container: container]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp create_shot_record(%{current_user: current_user, pack: pack}) do
 | 
			
		||||
    shot_record = shot_record_fixture(@shot_record_update_attrs, current_user, pack)
 | 
			
		||||
    pack = pack |> Repo.reload!()
 | 
			
		||||
    [pack: pack, shot_record: shot_record]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp create_empty_pack(%{
 | 
			
		||||
         current_user: current_user,
 | 
			
		||||
         type: type,
 | 
			
		||||
         container: container
 | 
			
		||||
       }) do
 | 
			
		||||
    {1, [pack]} = pack_fixture(@empty_attrs, type, container, current_user)
 | 
			
		||||
    shot_record = shot_record_fixture(@shot_record_attrs, current_user, pack)
 | 
			
		||||
    pack = pack |> Repo.reload!()
 | 
			
		||||
    [empty_pack: pack, shot_record: shot_record]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "Index of pack" do
 | 
			
		||||
    setup [:register_and_log_in_user, :create_pack]
 | 
			
		||||
 | 
			
		||||
    test "lists all packs", %{conn: conn, pack: pack} do
 | 
			
		||||
      {:ok, _index_live, html} = live(conn, Routes.pack_index_path(conn, :index))
 | 
			
		||||
      pack = pack |> Repo.preload(:type)
 | 
			
		||||
      assert html =~ "Ammo"
 | 
			
		||||
      assert html =~ pack.type.name
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "can sort by type",
 | 
			
		||||
         %{conn: conn, container: container, current_user: current_user} do
 | 
			
		||||
      rifle_type = type_fixture(%{class: :rifle}, current_user)
 | 
			
		||||
      {1, [rifle_pack]} = pack_fixture(rifle_type, container, current_user)
 | 
			
		||||
      shotgun_type = type_fixture(%{class: :shotgun}, current_user)
 | 
			
		||||
      {1, [shotgun_pack]} = pack_fixture(shotgun_type, container, current_user)
 | 
			
		||||
      pistol_type = type_fixture(%{class: :pistol}, current_user)
 | 
			
		||||
      {1, [pistol_pack]} = pack_fixture(pistol_type, container, current_user)
 | 
			
		||||
 | 
			
		||||
      {:ok, index_live, html} = live(conn, Routes.pack_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ "All"
 | 
			
		||||
 | 
			
		||||
      assert html =~ rifle_pack.type.name
 | 
			
		||||
      assert html =~ shotgun_pack.type.name
 | 
			
		||||
      assert html =~ pistol_pack.type.name
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_class"]/)
 | 
			
		||||
        |> render_change(type: %{class: :rifle})
 | 
			
		||||
 | 
			
		||||
      assert html =~ rifle_pack.type.name
 | 
			
		||||
      refute html =~ shotgun_pack.type.name
 | 
			
		||||
      refute html =~ pistol_pack.type.name
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_class"]/)
 | 
			
		||||
        |> render_change(type: %{class: :shotgun})
 | 
			
		||||
 | 
			
		||||
      refute html =~ rifle_pack.type.name
 | 
			
		||||
      assert html =~ shotgun_pack.type.name
 | 
			
		||||
      refute html =~ pistol_pack.type.name
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_class"]/)
 | 
			
		||||
        |> render_change(type: %{class: :pistol})
 | 
			
		||||
 | 
			
		||||
      refute html =~ rifle_pack.type.name
 | 
			
		||||
      refute html =~ shotgun_pack.type.name
 | 
			
		||||
      assert html =~ pistol_pack.type.name
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_class"]/)
 | 
			
		||||
        |> render_change(type: %{class: :all})
 | 
			
		||||
 | 
			
		||||
      assert html =~ rifle_pack.type.name
 | 
			
		||||
      assert html =~ shotgun_pack.type.name
 | 
			
		||||
      assert html =~ pistol_pack.type.name
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "can search for packs", %{conn: conn, pack: pack} do
 | 
			
		||||
      {:ok, index_live, html} = live(conn, Routes.pack_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      pack = pack |> Repo.preload(:type)
 | 
			
		||||
 | 
			
		||||
      assert html =~ pack.type.name
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/)
 | 
			
		||||
             |> render_change(search: %{search_term: pack.type.name}) =~
 | 
			
		||||
               pack.type.name
 | 
			
		||||
 | 
			
		||||
      assert_patch(
 | 
			
		||||
        index_live,
 | 
			
		||||
        Routes.pack_index_path(conn, :search, pack.type.name)
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      refute index_live
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/)
 | 
			
		||||
             |> render_change(search: %{search_term: "something_else"}) =~
 | 
			
		||||
               pack.type.name
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.pack_index_path(conn, :search, "something_else"))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/)
 | 
			
		||||
             |> render_change(search: %{search_term: ""}) =~ pack.type.name
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.pack_index_path(conn, :index))
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "saves a single new pack", %{conn: conn} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.pack_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element("a", "Add Ammo") |> render_click() =~ "Add Ammo"
 | 
			
		||||
      assert_patch(index_live, Routes.pack_index_path(conn, :new))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#pack-form")
 | 
			
		||||
             |> render_change(pack: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#pack-form")
 | 
			
		||||
        |> render_submit(pack: @create_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.pack_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ "Ammo added successfully"
 | 
			
		||||
      assert html =~ "\n42\n"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "saves multiple new packs", %{conn: conn, current_user: current_user} do
 | 
			
		||||
      multiplier = 25
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.pack_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element("a", "Add Ammo") |> render_click() =~ "Add Ammo"
 | 
			
		||||
      assert_patch(index_live, Routes.pack_index_path(conn, :new))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#pack-form")
 | 
			
		||||
             |> render_change(pack: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#pack-form")
 | 
			
		||||
        |> render_submit(pack: @create_attrs |> Map.put(:multiplier, multiplier))
 | 
			
		||||
        |> follow_redirect(conn, Routes.pack_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ "Ammo added successfully"
 | 
			
		||||
      assert Ammo.list_packs(nil, :all, current_user) |> Enum.count() == multiplier + 1
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "does not save invalid number of new packs", %{conn: conn} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.pack_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element("a", "Add Ammo") |> render_click() =~ "Add Ammo"
 | 
			
		||||
      assert_patch(index_live, Routes.pack_index_path(conn, :new))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#pack-form")
 | 
			
		||||
             |> render_change(pack: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#pack-form")
 | 
			
		||||
             |> render_submit(pack: @create_attrs |> Map.put(:multiplier, "0")) =~
 | 
			
		||||
               "Invalid number of copies, must be between 1 and #{@pack_create_limit}. Was 0"
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#pack-form")
 | 
			
		||||
             |> render_submit(pack: @create_attrs |> Map.put(:multiplier, @pack_create_limit + 1)) =~
 | 
			
		||||
               "Invalid number of copies, must be between 1 and #{@pack_create_limit}. Was #{@pack_create_limit + 1}"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "updates pack in listing", %{conn: conn, pack: pack} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.pack_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> element(~s/a[aria-label="Edit pack of #{pack.count} bullets"]/)
 | 
			
		||||
             |> render_click() =~ "Edit ammo"
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.pack_index_path(conn, :edit, pack))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#pack-form")
 | 
			
		||||
             |> render_change(pack: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#pack-form")
 | 
			
		||||
        |> render_submit(pack: @update_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.pack_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ "Ammo updated successfully"
 | 
			
		||||
      assert html =~ "\n43\n"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "clones pack in listing", %{conn: conn, pack: pack} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.pack_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> element(~s/a[aria-label="Clone pack of #{pack.count} bullets"]/)
 | 
			
		||||
        |> render_click()
 | 
			
		||||
 | 
			
		||||
      assert html =~ "Add Ammo"
 | 
			
		||||
      assert html =~ "$#{display_currency(120.5)}"
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.pack_index_path(conn, :clone, pack))
 | 
			
		||||
 | 
			
		||||
      {:ok, _index_live, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#pack-form")
 | 
			
		||||
        |> render_submit()
 | 
			
		||||
        |> follow_redirect(conn, Routes.pack_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ "Ammo added successfully"
 | 
			
		||||
      assert html =~ "\n42\n"
 | 
			
		||||
      assert html =~ "$#{display_currency(120.5)}"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "checks validity when cloning", %{conn: conn, pack: pack} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.pack_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> element(~s/a[aria-label="Clone pack of #{pack.count} bullets"]/)
 | 
			
		||||
        |> render_click()
 | 
			
		||||
 | 
			
		||||
      assert html =~ "Add Ammo"
 | 
			
		||||
      assert html =~ "$#{display_currency(120.5)}"
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.pack_index_path(conn, :clone, pack))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#pack-form")
 | 
			
		||||
             |> render_change(pack: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "clones pack in listing with updates", %{conn: conn, pack: pack} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.pack_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> element(~s/a[aria-label="Clone pack of #{pack.count} bullets"]/)
 | 
			
		||||
        |> render_click()
 | 
			
		||||
 | 
			
		||||
      assert html =~ "Add Ammo"
 | 
			
		||||
      assert html =~ "$#{display_currency(120.5)}"
 | 
			
		||||
      assert_patch(index_live, Routes.pack_index_path(conn, :clone, pack))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#pack-form")
 | 
			
		||||
             |> render_change(pack: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#pack-form")
 | 
			
		||||
        |> render_submit(pack: @create_attrs |> Map.put(:count, 43))
 | 
			
		||||
        |> follow_redirect(conn, Routes.pack_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ "Ammo added successfully"
 | 
			
		||||
      assert html =~ "\n43\n"
 | 
			
		||||
      assert html =~ "$#{display_currency(120.5)}"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "deletes pack in listing", %{conn: conn, pack: pack} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.pack_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> element(~s/a[aria-label="Delete pack of #{pack.count} bullets"]/)
 | 
			
		||||
             |> render_click()
 | 
			
		||||
 | 
			
		||||
      refute has_element?(index_live, "#pack-#{pack.id}")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "saves new shot_record", %{conn: conn, pack: pack} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.pack_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element("a", "Record shots") |> render_click() =~ "Record shots"
 | 
			
		||||
      assert_patch(index_live, Routes.pack_index_path(conn, :add_shot_record, pack))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#shot-record-form")
 | 
			
		||||
             |> render_change(shot_record: @shot_record_invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#shot-record-form")
 | 
			
		||||
        |> render_submit(shot_record: @shot_record_create_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.pack_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ "Shots recorded successfully"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    @spec display_currency(float()) :: String.t()
 | 
			
		||||
    defp display_currency(float), do: :erlang.float_to_binary(float, decimals: 2)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "Index of empty pack" do
 | 
			
		||||
    setup [:register_and_log_in_user, :create_pack, :create_empty_pack]
 | 
			
		||||
 | 
			
		||||
    test "hides empty packs by default", %{
 | 
			
		||||
      conn: conn,
 | 
			
		||||
      empty_pack: pack,
 | 
			
		||||
      current_user: current_user
 | 
			
		||||
    } do
 | 
			
		||||
      {:ok, show_live, html} = live(conn, Routes.pack_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ "Show used"
 | 
			
		||||
      refute html =~ "$#{display_currency(50.00)}"
 | 
			
		||||
 | 
			
		||||
      percentage = pack |> Ammo.get_percentage_remaining(current_user)
 | 
			
		||||
      refute html =~ "\n#{"#{percentage}%"}\n"
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        show_live
 | 
			
		||||
        |> element(~s/input[type="checkbox"][aria-labelledby="toggle_show_used-label"}]/)
 | 
			
		||||
        |> render_click()
 | 
			
		||||
 | 
			
		||||
      assert html =~ "$#{display_currency(50.00)}"
 | 
			
		||||
      percentage = pack |> Ammo.get_percentage_remaining(current_user)
 | 
			
		||||
      assert html =~ "\n#{"#{percentage}%"}\n"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "Show pack" do
 | 
			
		||||
    setup [:register_and_log_in_user, :create_pack]
 | 
			
		||||
 | 
			
		||||
    test "displays pack", %{conn: conn, pack: pack} do
 | 
			
		||||
      {:ok, _show_live, html} = live(conn, Routes.pack_show_path(conn, :show, pack))
 | 
			
		||||
      pack = pack |> Repo.preload(:type)
 | 
			
		||||
      assert html =~ "Show Ammo"
 | 
			
		||||
      assert html =~ pack.type.name
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "updates pack within modal", %{conn: conn, pack: pack} do
 | 
			
		||||
      {:ok, show_live, _html} = live(conn, Routes.pack_show_path(conn, :show, pack))
 | 
			
		||||
 | 
			
		||||
      assert show_live
 | 
			
		||||
             |> element(~s/a[aria-label="Edit pack of #{pack.count} bullets"]/)
 | 
			
		||||
             |> render_click() =~ "Edit Ammo"
 | 
			
		||||
 | 
			
		||||
      assert_patch(show_live, Routes.pack_show_path(conn, :edit, pack))
 | 
			
		||||
 | 
			
		||||
      assert show_live
 | 
			
		||||
             |> form("#pack-form")
 | 
			
		||||
             |> render_change(pack: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        show_live
 | 
			
		||||
        |> form("#pack-form")
 | 
			
		||||
        |> render_submit(pack: @update_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.pack_show_path(conn, :show, pack))
 | 
			
		||||
 | 
			
		||||
      assert html =~ "Ammo updated successfully"
 | 
			
		||||
      assert html =~ "some updated notes"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "saves new shot_record", %{conn: conn, pack: pack} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.pack_show_path(conn, :show, pack))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element("a", "Record shots") |> render_click() =~ "Record shots"
 | 
			
		||||
      assert_patch(index_live, Routes.pack_show_path(conn, :add_shot_record, pack))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#shot-record-form")
 | 
			
		||||
             |> render_change(shot_record: @shot_record_invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#shot-record-form")
 | 
			
		||||
        |> render_submit(shot_record: @shot_record_create_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.pack_show_path(conn, :show, pack))
 | 
			
		||||
 | 
			
		||||
      assert html =~ "Shots recorded successfully"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "Show pack with shot record" do
 | 
			
		||||
    setup [:register_and_log_in_user, :create_pack, :create_shot_record]
 | 
			
		||||
 | 
			
		||||
    test "updates shot_record in listing",
 | 
			
		||||
         %{conn: conn, pack: pack, shot_record: shot_record} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.pack_show_path(conn, :edit, pack))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> element(~s/a[aria-label="Edit shot record of #{shot_record.count} shots"]/)
 | 
			
		||||
             |> render_click() =~ "Edit Shot Record"
 | 
			
		||||
 | 
			
		||||
      assert_patch(
 | 
			
		||||
        index_live,
 | 
			
		||||
        Routes.pack_show_path(conn, :edit_shot_record, pack, shot_record)
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#shot-record-form")
 | 
			
		||||
             |> render_change(shot_record: @shot_record_invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#shot-record-form")
 | 
			
		||||
        |> render_submit(shot_record: @shot_record_update_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.pack_show_path(conn, :show, pack))
 | 
			
		||||
 | 
			
		||||
      assert html =~ "Shot records updated successfully"
 | 
			
		||||
      assert html =~ "some updated notes"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "deletes shot_record in listing",
 | 
			
		||||
         %{conn: conn, pack: pack, shot_record: shot_record} do
 | 
			
		||||
      {:ok, index_live, _html} =
 | 
			
		||||
        live(conn, Routes.pack_show_path(conn, :edit_shot_record, pack, shot_record))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> element(~s/a[aria-label="Delete shot record of #{shot_record.count} shots"]/)
 | 
			
		||||
             |> render_click()
 | 
			
		||||
 | 
			
		||||
      refute has_element?(index_live, "#shot_record-#{shot_record.id}")
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -6,187 +6,177 @@ defmodule CanneryWeb.RangeLiveTest do
 | 
			
		||||
  use CanneryWeb.ConnCase
 | 
			
		||||
  import Phoenix.LiveViewTest
 | 
			
		||||
  import Cannery.Fixtures
 | 
			
		||||
  import CanneryWeb.Gettext
 | 
			
		||||
 | 
			
		||||
  @moduletag :range_live_test
 | 
			
		||||
  @create_attrs %{"ammo_left" => 5, "notes" => "some notes"}
 | 
			
		||||
  @update_attrs %{"count" => 16, "notes" => "some updated notes"}
 | 
			
		||||
  # @invalid_attrs %{"count" => nil, "notes" => nil}
 | 
			
		||||
  @create_attrs %{ammo_left: 5, notes: "some notes"}
 | 
			
		||||
  @update_attrs %{count: 16, notes: "some updated notes"}
 | 
			
		||||
  @invalid_attrs %{count: nil, notes: nil}
 | 
			
		||||
 | 
			
		||||
  defp create_shot_group(%{current_user: current_user}) do
 | 
			
		||||
    container = container_fixture(%{"staged" => true}, current_user)
 | 
			
		||||
    ammo_type = ammo_type_fixture(current_user)
 | 
			
		||||
  defp create_shot_record(%{current_user: current_user}) do
 | 
			
		||||
    container = container_fixture(%{staged: true}, current_user)
 | 
			
		||||
    type = type_fixture(current_user)
 | 
			
		||||
 | 
			
		||||
    {1, [ammo_group]} =
 | 
			
		||||
      ammo_group_fixture(%{"staged" => true}, ammo_type, container, current_user)
 | 
			
		||||
    {1, [pack]} = pack_fixture(%{staged: true}, type, container, current_user)
 | 
			
		||||
 | 
			
		||||
    shot_group =
 | 
			
		||||
      %{"count" => 5, "date" => ~N[2022-02-13 03:17:00], "notes" => "some notes"}
 | 
			
		||||
      |> shot_group_fixture(current_user, ammo_group)
 | 
			
		||||
    shot_record =
 | 
			
		||||
      %{count: 5, date: ~N[2022-02-13 03:17:00], notes: "some notes"}
 | 
			
		||||
      |> shot_record_fixture(current_user, pack)
 | 
			
		||||
 | 
			
		||||
    [
 | 
			
		||||
      container: container,
 | 
			
		||||
      ammo_type: ammo_type,
 | 
			
		||||
      ammo_group: ammo_group,
 | 
			
		||||
      shot_group: shot_group
 | 
			
		||||
      type: type,
 | 
			
		||||
      pack: pack,
 | 
			
		||||
      shot_record: shot_record
 | 
			
		||||
    ]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "Index" do
 | 
			
		||||
    setup [:register_and_log_in_user, :create_shot_group]
 | 
			
		||||
    setup [:register_and_log_in_user, :create_shot_record]
 | 
			
		||||
 | 
			
		||||
    test "lists all shot_groups", %{conn: conn, shot_group: shot_group} do
 | 
			
		||||
    test "lists all shot_records", %{conn: conn, shot_record: shot_record} do
 | 
			
		||||
      {:ok, _index_live, html} = live(conn, Routes.range_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ gettext("Range day")
 | 
			
		||||
      assert html =~ shot_group.notes
 | 
			
		||||
      assert html =~ "Range day"
 | 
			
		||||
      assert html =~ shot_record.notes
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "can sort by type",
 | 
			
		||||
         %{conn: conn, container: container, current_user: current_user} do
 | 
			
		||||
      rifle_ammo_type = ammo_type_fixture(%{"type" => "rifle"}, current_user)
 | 
			
		||||
      {1, [rifle_ammo_group]} = ammo_group_fixture(rifle_ammo_type, container, current_user)
 | 
			
		||||
      rifle_type = type_fixture(%{class: :rifle}, current_user)
 | 
			
		||||
      {1, [rifle_pack]} = pack_fixture(rifle_type, container, current_user)
 | 
			
		||||
 | 
			
		||||
      rifle_shot_group =
 | 
			
		||||
        shot_group_fixture(%{"notes" => "group_one"}, current_user, rifle_ammo_group)
 | 
			
		||||
      rifle_shot_record = shot_record_fixture(%{notes: "group_one"}, current_user, rifle_pack)
 | 
			
		||||
 | 
			
		||||
      shotgun_ammo_type = ammo_type_fixture(%{"type" => "shotgun"}, current_user)
 | 
			
		||||
      {1, [shotgun_ammo_group]} = ammo_group_fixture(shotgun_ammo_type, container, current_user)
 | 
			
		||||
      shotgun_type = type_fixture(%{class: :shotgun}, current_user)
 | 
			
		||||
      {1, [shotgun_pack]} = pack_fixture(shotgun_type, container, current_user)
 | 
			
		||||
 | 
			
		||||
      shotgun_shot_group =
 | 
			
		||||
        shot_group_fixture(%{"notes" => "group_two"}, current_user, shotgun_ammo_group)
 | 
			
		||||
      shotgun_shot_record = shot_record_fixture(%{notes: "group_two"}, current_user, shotgun_pack)
 | 
			
		||||
 | 
			
		||||
      pistol_ammo_type = ammo_type_fixture(%{"type" => "pistol"}, current_user)
 | 
			
		||||
      {1, [pistol_ammo_group]} = ammo_group_fixture(pistol_ammo_type, container, current_user)
 | 
			
		||||
      pistol_type = type_fixture(%{class: :pistol}, current_user)
 | 
			
		||||
      {1, [pistol_pack]} = pack_fixture(pistol_type, container, current_user)
 | 
			
		||||
 | 
			
		||||
      pistol_shot_group =
 | 
			
		||||
        shot_group_fixture(%{"notes" => "group_three"}, current_user, pistol_ammo_group)
 | 
			
		||||
      pistol_shot_record = shot_record_fixture(%{notes: "group_three"}, current_user, pistol_pack)
 | 
			
		||||
 | 
			
		||||
      {:ok, index_live, html} = live(conn, Routes.range_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ "All"
 | 
			
		||||
 | 
			
		||||
      assert html =~ rifle_shot_group.notes
 | 
			
		||||
      assert html =~ shotgun_shot_group.notes
 | 
			
		||||
      assert html =~ pistol_shot_group.notes
 | 
			
		||||
      assert html =~ rifle_shot_record.notes
 | 
			
		||||
      assert html =~ shotgun_shot_record.notes
 | 
			
		||||
      assert html =~ pistol_shot_record.notes
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_type"]/)
 | 
			
		||||
        |> render_change(ammo_type: %{type: :rifle})
 | 
			
		||||
        |> form(~s/form[phx-change="change_class"]/)
 | 
			
		||||
        |> render_change(type: %{class: :rifle})
 | 
			
		||||
 | 
			
		||||
      assert html =~ rifle_shot_group.notes
 | 
			
		||||
      refute html =~ shotgun_shot_group.notes
 | 
			
		||||
      refute html =~ pistol_shot_group.notes
 | 
			
		||||
      assert html =~ rifle_shot_record.notes
 | 
			
		||||
      refute html =~ shotgun_shot_record.notes
 | 
			
		||||
      refute html =~ pistol_shot_record.notes
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_type"]/)
 | 
			
		||||
        |> render_change(ammo_type: %{type: :shotgun})
 | 
			
		||||
        |> form(~s/form[phx-change="change_class"]/)
 | 
			
		||||
        |> render_change(type: %{class: :shotgun})
 | 
			
		||||
 | 
			
		||||
      refute html =~ rifle_shot_group.notes
 | 
			
		||||
      assert html =~ shotgun_shot_group.notes
 | 
			
		||||
      refute html =~ pistol_shot_group.notes
 | 
			
		||||
      refute html =~ rifle_shot_record.notes
 | 
			
		||||
      assert html =~ shotgun_shot_record.notes
 | 
			
		||||
      refute html =~ pistol_shot_record.notes
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_type"]/)
 | 
			
		||||
        |> render_change(ammo_type: %{type: :pistol})
 | 
			
		||||
        |> form(~s/form[phx-change="change_class"]/)
 | 
			
		||||
        |> render_change(type: %{class: :pistol})
 | 
			
		||||
 | 
			
		||||
      refute html =~ rifle_shot_group.notes
 | 
			
		||||
      refute html =~ shotgun_shot_group.notes
 | 
			
		||||
      assert html =~ pistol_shot_group.notes
 | 
			
		||||
      refute html =~ rifle_shot_record.notes
 | 
			
		||||
      refute html =~ shotgun_shot_record.notes
 | 
			
		||||
      assert html =~ pistol_shot_record.notes
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_type"]/)
 | 
			
		||||
        |> render_change(ammo_type: %{type: :all})
 | 
			
		||||
        |> form(~s/form[phx-change="change_class"]/)
 | 
			
		||||
        |> render_change(type: %{class: :all})
 | 
			
		||||
 | 
			
		||||
      assert html =~ rifle_shot_group.notes
 | 
			
		||||
      assert html =~ shotgun_shot_group.notes
 | 
			
		||||
      assert html =~ pistol_shot_group.notes
 | 
			
		||||
      assert html =~ rifle_shot_record.notes
 | 
			
		||||
      assert html =~ shotgun_shot_record.notes
 | 
			
		||||
      assert html =~ pistol_shot_record.notes
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "can search for shot_group", %{conn: conn, shot_group: shot_group} do
 | 
			
		||||
    test "can search for shot_record", %{conn: conn, shot_record: shot_record} do
 | 
			
		||||
      {:ok, index_live, html} = live(conn, Routes.range_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ shot_group.notes
 | 
			
		||||
      assert html =~ shot_record.notes
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/,
 | 
			
		||||
               search: %{search_term: shot_group.notes}
 | 
			
		||||
             )
 | 
			
		||||
             |> render_change() =~ shot_group.notes
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/)
 | 
			
		||||
             |> render_change(search: %{search_term: shot_record.notes}) =~ shot_record.notes
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.range_index_path(conn, :search, shot_group.notes))
 | 
			
		||||
      assert_patch(index_live, Routes.range_index_path(conn, :search, shot_record.notes))
 | 
			
		||||
 | 
			
		||||
      refute index_live
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/, search: %{search_term: "something_else"})
 | 
			
		||||
             |> render_change() =~ shot_group.notes
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/)
 | 
			
		||||
             |> render_change(search: %{search_term: "something_else"}) =~ shot_record.notes
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.range_index_path(conn, :search, "something_else"))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/, search: %{search_term: ""})
 | 
			
		||||
             |> render_change() =~ shot_group.notes
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/)
 | 
			
		||||
             |> render_change(search: %{search_term: ""}) =~ shot_record.notes
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.range_index_path(conn, :index))
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "saves new shot_group", %{conn: conn, ammo_group: ammo_group} do
 | 
			
		||||
    test "saves new shot_record", %{conn: conn, pack: pack} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.range_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element("a", dgettext("actions", "Record shots")) |> render_click() =~
 | 
			
		||||
               gettext("Record shots")
 | 
			
		||||
      assert index_live |> element("a", "Record shots") |> render_click() =~ "Record shots"
 | 
			
		||||
      assert_patch(index_live, Routes.range_index_path(conn, :add_shot_record, pack))
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.range_index_path(conn, :add_shot_group, ammo_group))
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#shot_group-form", shot_group: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "is invalid")
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#shot-record-form")
 | 
			
		||||
             |> render_change(shot_record: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#shot-group-form", shot_group: @create_attrs)
 | 
			
		||||
        |> render_submit()
 | 
			
		||||
        |> form("#shot-record-form")
 | 
			
		||||
        |> render_submit(shot_record: @create_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.range_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ dgettext("prompts", "Shots recorded successfully")
 | 
			
		||||
      assert html =~ "Shots recorded successfully"
 | 
			
		||||
      assert html =~ "some notes"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "updates shot_group in listing", %{conn: conn, shot_group: shot_group} do
 | 
			
		||||
    test "updates shot_record in listing", %{conn: conn, shot_record: shot_record} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.range_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> element(~s/a[aria-label="Edit shot record of #{shot_group.count} shots"]/)
 | 
			
		||||
             |> render_click() =~
 | 
			
		||||
               gettext("Edit Shot Records")
 | 
			
		||||
             |> element(~s/a[aria-label="Edit shot record of #{shot_record.count} shots"]/)
 | 
			
		||||
             |> render_click() =~ "Edit Shot Record"
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.range_index_path(conn, :edit, shot_group))
 | 
			
		||||
      assert_patch(index_live, Routes.range_index_path(conn, :edit, shot_record))
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#shot_group-form", shot_group: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "is invalid")
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#shot-record-form")
 | 
			
		||||
             |> render_change(shot_record: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#shot-group-form", shot_group: @update_attrs)
 | 
			
		||||
        |> form("#shot-record-form", shot_record: @update_attrs)
 | 
			
		||||
        |> render_submit()
 | 
			
		||||
        |> follow_redirect(conn, Routes.range_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ dgettext("actions", "Shot records updated successfully")
 | 
			
		||||
      assert html =~ "Shot records updated successfully"
 | 
			
		||||
      assert html =~ "some updated notes"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "deletes shot_group in listing", %{conn: conn, shot_group: shot_group} do
 | 
			
		||||
    test "deletes shot_record in listing", %{conn: conn, shot_record: shot_record} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.range_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> element(~s/a[aria-label="Delete shot record of #{shot_group.count} shots"]/)
 | 
			
		||||
             |> element(~s/a[aria-label="Delete shot record of #{shot_record.count} shots"]/)
 | 
			
		||||
             |> render_click()
 | 
			
		||||
 | 
			
		||||
      refute has_element?(index_live, "#shot_group-#{shot_group.id}")
 | 
			
		||||
      refute has_element?(index_live, "#shot_record-#{shot_record.id}")
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -5,26 +5,24 @@ defmodule CanneryWeb.TagLiveTest do
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb.ConnCase
 | 
			
		||||
  import Phoenix.LiveViewTest
 | 
			
		||||
  import CanneryWeb.Gettext
 | 
			
		||||
 | 
			
		||||
  @moduletag :tag_live_test
 | 
			
		||||
 | 
			
		||||
  @create_attrs %{
 | 
			
		||||
    "bg_color" => "#100000",
 | 
			
		||||
    "name" => "some name",
 | 
			
		||||
    "text_color" => "#000000"
 | 
			
		||||
    bg_color: "#100000",
 | 
			
		||||
    name: "some name",
 | 
			
		||||
    text_color: "#000000"
 | 
			
		||||
  }
 | 
			
		||||
  @update_attrs %{
 | 
			
		||||
    "bg_color" => "#100001",
 | 
			
		||||
    "name" => "some updated name",
 | 
			
		||||
    "text_color" => "#000001"
 | 
			
		||||
    bg_color: "#100001",
 | 
			
		||||
    name: "some updated name",
 | 
			
		||||
    text_color: "#000001"
 | 
			
		||||
  }
 | 
			
		||||
  @invalid_attrs %{
 | 
			
		||||
    bg_color: nil,
 | 
			
		||||
    name: nil,
 | 
			
		||||
    text_color: nil
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  # @invalid_attrs %{
 | 
			
		||||
  #   "bg_color" => nil,
 | 
			
		||||
  #   "name" => nil,
 | 
			
		||||
  #   "text_color" => nil
 | 
			
		||||
  # }
 | 
			
		||||
 | 
			
		||||
  def create_tag(%{current_user: current_user}) do
 | 
			
		||||
    tag = tag_fixture(current_user)
 | 
			
		||||
@@ -37,7 +35,7 @@ defmodule CanneryWeb.TagLiveTest do
 | 
			
		||||
    test "lists all tags", %{conn: conn, tag: tag} do
 | 
			
		||||
      {:ok, _index_live, html} = live(conn, Routes.tag_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ gettext("Tags")
 | 
			
		||||
      assert html =~ "Tags"
 | 
			
		||||
      assert html =~ tag.bg_color
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
@@ -47,22 +45,20 @@ defmodule CanneryWeb.TagLiveTest do
 | 
			
		||||
      assert html =~ tag.name
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/,
 | 
			
		||||
               search: %{search_term: tag.name}
 | 
			
		||||
             )
 | 
			
		||||
             |> render_change() =~ tag.name
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/)
 | 
			
		||||
             |> render_change(search: %{search_term: tag.name}) =~ tag.name
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.tag_index_path(conn, :search, tag.name))
 | 
			
		||||
 | 
			
		||||
      refute index_live
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/, search: %{search_term: "something_else"})
 | 
			
		||||
             |> render_change() =~ tag.name
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/)
 | 
			
		||||
             |> render_change(search: %{search_term: "something_else"}) =~ tag.name
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.tag_index_path(conn, :search, "something_else"))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/, search: %{search_term: ""})
 | 
			
		||||
             |> render_change() =~ tag.name
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/)
 | 
			
		||||
             |> render_change(search: %{search_term: ""}) =~ tag.name
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.tag_index_path(conn, :index))
 | 
			
		||||
    end
 | 
			
		||||
@@ -70,22 +66,20 @@ defmodule CanneryWeb.TagLiveTest do
 | 
			
		||||
    test "saves new tag", %{conn: conn} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.tag_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element("a", dgettext("actions", "New Tag")) |> render_click() =~
 | 
			
		||||
               dgettext("actions", "New Tag")
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element("a", "New Tag") |> render_click() =~ "New Tag"
 | 
			
		||||
      assert_patch(index_live, Routes.tag_index_path(conn, :new))
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#tag-form", tag: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "can't be blank")
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#tag-form")
 | 
			
		||||
             |> render_change(tag: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#tag-form", tag: @create_attrs)
 | 
			
		||||
        |> render_submit()
 | 
			
		||||
        |> form("#tag-form")
 | 
			
		||||
        |> render_submit(tag: @create_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.tag_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ dgettext("actions", "%{name} created successfully", name: "some name")
 | 
			
		||||
      assert html =~ "some name created successfully"
 | 
			
		||||
      assert html =~ "#100000"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
@@ -93,23 +87,21 @@ defmodule CanneryWeb.TagLiveTest do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.tag_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element(~s/a[aria-label="Edit #{tag.name}"]/) |> render_click() =~
 | 
			
		||||
               dgettext("actions", "Edit Tag")
 | 
			
		||||
               "Edit Tag"
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.tag_index_path(conn, :edit, tag))
 | 
			
		||||
 | 
			
		||||
      # assert index_live
 | 
			
		||||
      #        |> form("#tag-form", tag: @invalid_attrs)
 | 
			
		||||
      #        |> render_change() =~ dgettext("errors", "can't be blank")
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#tag-form")
 | 
			
		||||
             |> render_change(tag: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#tag-form", tag: @update_attrs)
 | 
			
		||||
        |> render_submit()
 | 
			
		||||
        |> form("#tag-form")
 | 
			
		||||
        |> render_submit(tag: @update_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.tag_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~
 | 
			
		||||
               dgettext("prompts", "%{name} updated successfully", name: "some updated name")
 | 
			
		||||
 | 
			
		||||
      assert html =~ "some updated name updated successfully"
 | 
			
		||||
      assert html =~ "#100001"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										400
									
								
								test/cannery_web/live/type_live_test.exs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										400
									
								
								test/cannery_web/live/type_live_test.exs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,400 @@
 | 
			
		||||
defmodule CanneryWeb.TypeLiveTest do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  Tests the type liveview
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb.ConnCase
 | 
			
		||||
  import Phoenix.LiveViewTest
 | 
			
		||||
  alias Cannery.{Ammo, Repo}
 | 
			
		||||
 | 
			
		||||
  @moduletag :type_live_test
 | 
			
		||||
 | 
			
		||||
  @create_attrs %{
 | 
			
		||||
    bullet_type: "some bullet_type",
 | 
			
		||||
    case_material: "some case_material",
 | 
			
		||||
    desc: "some desc",
 | 
			
		||||
    manufacturer: "some manufacturer",
 | 
			
		||||
    name: "some name",
 | 
			
		||||
    grains: 120
 | 
			
		||||
  }
 | 
			
		||||
  @update_attrs %{
 | 
			
		||||
    bullet_type: "some updated bullet_type",
 | 
			
		||||
    case_material: "some updated case_material",
 | 
			
		||||
    desc: "some updated desc",
 | 
			
		||||
    manufacturer: "some updated manufacturer",
 | 
			
		||||
    name: "some updated name",
 | 
			
		||||
    grains: 456
 | 
			
		||||
  }
 | 
			
		||||
  @invalid_attrs %{
 | 
			
		||||
    bullet_type: nil,
 | 
			
		||||
    case_material: nil,
 | 
			
		||||
    desc: nil,
 | 
			
		||||
    manufacturer: nil,
 | 
			
		||||
    name: nil,
 | 
			
		||||
    grains: nil
 | 
			
		||||
  }
 | 
			
		||||
  @pack_attrs %{
 | 
			
		||||
    notes: "some pack",
 | 
			
		||||
    count: 20
 | 
			
		||||
  }
 | 
			
		||||
  @shot_record_attrs %{
 | 
			
		||||
    notes: "some shot record",
 | 
			
		||||
    count: 20
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  defp create_type(%{current_user: current_user}) do
 | 
			
		||||
    [type: type_fixture(@create_attrs, current_user)]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp create_pack(%{type: type, current_user: current_user}) do
 | 
			
		||||
    container = container_fixture(current_user)
 | 
			
		||||
    {1, [pack]} = pack_fixture(@pack_attrs, type, container, current_user)
 | 
			
		||||
    [pack: pack, container: container]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp create_empty_pack(%{type: type, current_user: current_user}) do
 | 
			
		||||
    container = container_fixture(current_user)
 | 
			
		||||
    {1, [pack]} = pack_fixture(@pack_attrs, type, container, current_user)
 | 
			
		||||
    shot_record = shot_record_fixture(@shot_record_attrs, current_user, pack)
 | 
			
		||||
    pack = pack |> Repo.reload!()
 | 
			
		||||
    [pack: pack, container: container, shot_record: shot_record]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "Index" do
 | 
			
		||||
    setup [:register_and_log_in_user, :create_type]
 | 
			
		||||
 | 
			
		||||
    test "lists all types", %{conn: conn, type: type} do
 | 
			
		||||
      {:ok, _index_live, html} = live(conn, Routes.type_index_path(conn, :index))
 | 
			
		||||
      assert html =~ "Catalog"
 | 
			
		||||
      assert html =~ type.bullet_type
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "can sort by class", %{conn: conn, current_user: current_user} do
 | 
			
		||||
      rifle_type = type_fixture(%{class: :rifle}, current_user)
 | 
			
		||||
      shotgun_type = type_fixture(%{class: :shotgun}, current_user)
 | 
			
		||||
      pistol_type = type_fixture(%{class: :pistol}, current_user)
 | 
			
		||||
 | 
			
		||||
      {:ok, index_live, html} = live(conn, Routes.type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ "All"
 | 
			
		||||
 | 
			
		||||
      assert html =~ rifle_type.name
 | 
			
		||||
      assert html =~ shotgun_type.name
 | 
			
		||||
      assert html =~ pistol_type.name
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_class"]/)
 | 
			
		||||
        |> render_change(type: %{class: :rifle})
 | 
			
		||||
 | 
			
		||||
      assert html =~ rifle_type.name
 | 
			
		||||
      refute html =~ shotgun_type.name
 | 
			
		||||
      refute html =~ pistol_type.name
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_class"]/)
 | 
			
		||||
        |> render_change(type: %{class: :shotgun})
 | 
			
		||||
 | 
			
		||||
      refute html =~ rifle_type.name
 | 
			
		||||
      assert html =~ shotgun_type.name
 | 
			
		||||
      refute html =~ pistol_type.name
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_class"]/)
 | 
			
		||||
        |> render_change(type: %{class: :pistol})
 | 
			
		||||
 | 
			
		||||
      refute html =~ rifle_type.name
 | 
			
		||||
      refute html =~ shotgun_type.name
 | 
			
		||||
      assert html =~ pistol_type.name
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form(~s/form[phx-change="change_class"]/)
 | 
			
		||||
        |> render_change(type: %{class: :all})
 | 
			
		||||
 | 
			
		||||
      assert html =~ rifle_type.name
 | 
			
		||||
      assert html =~ shotgun_type.name
 | 
			
		||||
      assert html =~ pistol_type.name
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "can search for type", %{conn: conn, type: type} do
 | 
			
		||||
      {:ok, index_live, html} = live(conn, Routes.type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ type.bullet_type
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/)
 | 
			
		||||
             |> render_change(search: %{search_term: type.bullet_type}) =~
 | 
			
		||||
               type.bullet_type
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.type_index_path(conn, :search, type.bullet_type))
 | 
			
		||||
 | 
			
		||||
      refute index_live
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/)
 | 
			
		||||
             |> render_change(search: %{search_term: "something_else"}) =~ type.bullet_type
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.type_index_path(conn, :search, "something_else"))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form(~s/form[phx-change="search"]/)
 | 
			
		||||
             |> render_change(search: %{search_term: ""}) =~ type.bullet_type
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.type_index_path(conn, :index))
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "saves new type", %{conn: conn, current_user: current_user, type: type} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element("a", "New Type") |> render_click() =~ "New Type"
 | 
			
		||||
      assert_patch(index_live, Routes.type_index_path(conn, :new))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#type-form")
 | 
			
		||||
             |> render_change(type: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#type-form")
 | 
			
		||||
        |> render_submit(type: @create_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      type = type.id |> Ammo.get_type!(current_user)
 | 
			
		||||
      assert html =~ "#{type.name} created successfully"
 | 
			
		||||
      assert html =~ "some bullet_type"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "updates type in listing",
 | 
			
		||||
         %{conn: conn, current_user: current_user, type: type} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert index_live |> element(~s/a[aria-label="Edit #{type.name}"]/) |> render_click() =~
 | 
			
		||||
               "Edit #{type.name}"
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.type_index_path(conn, :edit, type))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#type-form")
 | 
			
		||||
             |> render_change(type: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#type-form")
 | 
			
		||||
        |> render_submit(type: @update_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      type = type.id |> Ammo.get_type!(current_user)
 | 
			
		||||
      assert html =~ "#{type.name} updated successfully"
 | 
			
		||||
      assert html =~ "some updated bullet_type"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "clones type in listing",
 | 
			
		||||
         %{conn: conn, current_user: current_user, type: type} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      html = index_live |> element(~s/a[aria-label="Clone #{type.name}"]/) |> render_click()
 | 
			
		||||
      assert html =~ "New Type"
 | 
			
		||||
      assert html =~ "some bullet_type"
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.type_index_path(conn, :clone, type))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#type-form")
 | 
			
		||||
             |> render_change(type: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#type-form")
 | 
			
		||||
        |> render_submit(type: @create_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      type = type.id |> Ammo.get_type!(current_user)
 | 
			
		||||
      assert html =~ "#{type.name} created successfully"
 | 
			
		||||
      assert html =~ "some bullet_type"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "clones type in listing with updates",
 | 
			
		||||
         %{conn: conn, current_user: current_user, type: type} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      html = index_live |> element(~s/a[aria-label="Clone #{type.name}"]/) |> render_click()
 | 
			
		||||
      assert html =~ "New Type"
 | 
			
		||||
      assert html =~ "some bullet_type"
 | 
			
		||||
 | 
			
		||||
      assert_patch(index_live, Routes.type_index_path(conn, :clone, type))
 | 
			
		||||
 | 
			
		||||
      assert index_live
 | 
			
		||||
             |> form("#type-form")
 | 
			
		||||
             |> render_change(type: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> form("#type-form")
 | 
			
		||||
        |> render_submit(
 | 
			
		||||
          type: Map.merge(@create_attrs, %{bullet_type: "some updated bullet_type"})
 | 
			
		||||
        )
 | 
			
		||||
        |> follow_redirect(conn, Routes.type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      type = type.id |> Ammo.get_type!(current_user)
 | 
			
		||||
      assert html =~ "#{type.name} created successfully"
 | 
			
		||||
      assert html =~ "some updated bullet_type"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "deletes type in listing", %{conn: conn, type: type} do
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.type_index_path(conn, :index))
 | 
			
		||||
      assert index_live |> element(~s/a[aria-label="Delete #{type.name}"]/) |> render_click()
 | 
			
		||||
      refute has_element?(index_live, "#type-#{type.id}")
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "Index with pack" do
 | 
			
		||||
    setup [:register_and_log_in_user, :create_type, :create_pack]
 | 
			
		||||
 | 
			
		||||
    test "shows used packs on toggle",
 | 
			
		||||
         %{conn: conn, pack: pack, current_user: current_user} do
 | 
			
		||||
      {:ok, index_live, html} = live(conn, Routes.type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      assert html =~ "Show used"
 | 
			
		||||
      refute html =~ "Used rounds"
 | 
			
		||||
      refute html =~ "Total ever rounds"
 | 
			
		||||
      refute html =~ "Used packs"
 | 
			
		||||
      refute html =~ "Total ever packs"
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> element(~s/input[type="checkbox"][aria-labelledby="toggle_show_used-label"}]/)
 | 
			
		||||
        |> render_click()
 | 
			
		||||
 | 
			
		||||
      assert html =~ "Used rounds"
 | 
			
		||||
      assert html =~ "Total ever rounds"
 | 
			
		||||
      assert html =~ "Used packs"
 | 
			
		||||
      assert html =~ "Total ever packs"
 | 
			
		||||
 | 
			
		||||
      assert html =~ "\n20\n"
 | 
			
		||||
      assert html =~ "\n0\n"
 | 
			
		||||
      assert html =~ "\n1\n"
 | 
			
		||||
 | 
			
		||||
      shot_record_fixture(%{count: 5}, current_user, pack)
 | 
			
		||||
 | 
			
		||||
      {:ok, index_live, _html} = live(conn, Routes.type_index_path(conn, :index))
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        index_live
 | 
			
		||||
        |> element(~s/input[type="checkbox"][aria-labelledby="toggle_show_used-label"}]/)
 | 
			
		||||
        |> render_click()
 | 
			
		||||
 | 
			
		||||
      assert html =~ "\n15\n"
 | 
			
		||||
      assert html =~ "\n5\n"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "Show type" do
 | 
			
		||||
    setup [:register_and_log_in_user, :create_type]
 | 
			
		||||
 | 
			
		||||
    test "displays type", %{
 | 
			
		||||
      conn: conn,
 | 
			
		||||
      type: %{name: name, bullet_type: bullet_type} = type
 | 
			
		||||
    } do
 | 
			
		||||
      {:ok, _show_live, html} = live(conn, Routes.type_show_path(conn, :show, type))
 | 
			
		||||
 | 
			
		||||
      assert html =~ name
 | 
			
		||||
      assert html =~ bullet_type
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "updates type within modal",
 | 
			
		||||
         %{conn: conn, current_user: current_user, type: %{name: name} = type} do
 | 
			
		||||
      {:ok, show_live, _html} = live(conn, Routes.type_show_path(conn, :show, type))
 | 
			
		||||
 | 
			
		||||
      assert show_live |> element(~s/a[aria-label="Edit #{type.name}"]/) |> render_click() =~
 | 
			
		||||
               "Edit #{name}"
 | 
			
		||||
 | 
			
		||||
      assert_patch(show_live, Routes.type_show_path(conn, :edit, type))
 | 
			
		||||
 | 
			
		||||
      assert show_live
 | 
			
		||||
             |> form("#type-form")
 | 
			
		||||
             |> render_change(type: @invalid_attrs) =~ "can't be blank"
 | 
			
		||||
 | 
			
		||||
      {:ok, _view, html} =
 | 
			
		||||
        show_live
 | 
			
		||||
        |> form("#type-form")
 | 
			
		||||
        |> render_submit(type: @update_attrs)
 | 
			
		||||
        |> follow_redirect(conn, Routes.type_show_path(conn, :show, type))
 | 
			
		||||
 | 
			
		||||
      type = type.id |> Ammo.get_type!(current_user)
 | 
			
		||||
      assert html =~ "#{type.name} updated successfully"
 | 
			
		||||
      assert html =~ "some updated bullet_type"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "Show type with pack" do
 | 
			
		||||
    setup [:register_and_log_in_user, :create_type, :create_pack]
 | 
			
		||||
 | 
			
		||||
    test "displays pack", %{
 | 
			
		||||
      conn: conn,
 | 
			
		||||
      type: %{name: type_name} = type,
 | 
			
		||||
      container: %{name: container_name}
 | 
			
		||||
    } do
 | 
			
		||||
      {:ok, _show_live, html} = live(conn, Routes.type_show_path(conn, :show, type))
 | 
			
		||||
 | 
			
		||||
      assert html =~ type_name
 | 
			
		||||
      assert html =~ "\n20\n"
 | 
			
		||||
      assert html =~ container_name
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "displays pack in table",
 | 
			
		||||
         %{conn: conn, type: type, container: %{name: container_name}} do
 | 
			
		||||
      {:ok, show_live, _html} = live(conn, Routes.type_show_path(conn, :show, type))
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        show_live
 | 
			
		||||
        |> element(~s/input[type="checkbox"][aria-labelledby="toggle_table-label"}]/)
 | 
			
		||||
        |> render_click()
 | 
			
		||||
 | 
			
		||||
      assert html =~ "\n20\n"
 | 
			
		||||
      assert html =~ container_name
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "Show type with empty pack" do
 | 
			
		||||
    setup [:register_and_log_in_user, :create_type, :create_empty_pack]
 | 
			
		||||
 | 
			
		||||
    test "displays empty packs on toggle",
 | 
			
		||||
         %{conn: conn, type: type, container: %{name: container_name}} do
 | 
			
		||||
      {:ok, show_live, html} = live(conn, Routes.type_show_path(conn, :show, type))
 | 
			
		||||
      assert html =~ "Show used"
 | 
			
		||||
      refute html =~ "\n20\n"
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        show_live
 | 
			
		||||
        |> element(~s/input[type="checkbox"][aria-labelledby="toggle_show_used-label"}]/)
 | 
			
		||||
        |> render_click()
 | 
			
		||||
 | 
			
		||||
      assert html =~ "\n20\n"
 | 
			
		||||
      assert html =~ "Empty"
 | 
			
		||||
      assert html =~ container_name
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "displays empty packs in table on toggle",
 | 
			
		||||
         %{conn: conn, type: type, container: %{name: container_name}} do
 | 
			
		||||
      {:ok, show_live, _html} = live(conn, Routes.type_show_path(conn, :show, type))
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        show_live
 | 
			
		||||
        |> element(~s/input[type="checkbox"][aria-labelledby="toggle_table-label"}]/)
 | 
			
		||||
        |> render_click()
 | 
			
		||||
 | 
			
		||||
      assert html =~ "Show used"
 | 
			
		||||
      refute html =~ "\n20\n"
 | 
			
		||||
 | 
			
		||||
      html =
 | 
			
		||||
        show_live
 | 
			
		||||
        |> element(~s/input[type="checkbox"][aria-labelledby="toggle_show_used-label"}]/)
 | 
			
		||||
        |> render_click()
 | 
			
		||||
 | 
			
		||||
      assert html =~ "\n20\n"
 | 
			
		||||
      assert html =~ "Empty"
 | 
			
		||||
      assert html =~ container_name
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -4,19 +4,16 @@ defmodule CanneryWeb.ErrorViewTest do
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb.ConnCase, async: true
 | 
			
		||||
  import CanneryWeb.Gettext
 | 
			
		||||
  # Bring render/3 and render_to_string/3 for testing custom views
 | 
			
		||||
  import Phoenix.View
 | 
			
		||||
 | 
			
		||||
  @moduletag :error_view_test
 | 
			
		||||
 | 
			
		||||
  test "renders 404.html" do
 | 
			
		||||
    assert render_to_string(CanneryWeb.ErrorView, "404.html", []) =~
 | 
			
		||||
             dgettext("errors", "Not found")
 | 
			
		||||
    assert render_to_string(CanneryWeb.ErrorView, "404.html", []) =~ "Not found"
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  test "renders 500.html" do
 | 
			
		||||
    assert render_to_string(CanneryWeb.ErrorView, "500.html", []) =~
 | 
			
		||||
             dgettext("errors", "Internal Server Error")
 | 
			
		||||
    assert render_to_string(CanneryWeb.ErrorView, "500.html", []) =~ "Internal Server Error"
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -8,10 +8,10 @@ defmodule Cannery.Fixtures do
 | 
			
		||||
  alias Cannery.{
 | 
			
		||||
    Accounts,
 | 
			
		||||
    Accounts.User,
 | 
			
		||||
    ActivityLog.ShotGroup,
 | 
			
		||||
    ActivityLog.ShotRecord,
 | 
			
		||||
    Ammo,
 | 
			
		||||
    Ammo.AmmoGroup,
 | 
			
		||||
    Ammo.AmmoType,
 | 
			
		||||
    Ammo.Pack,
 | 
			
		||||
    Ammo.Type,
 | 
			
		||||
    Containers,
 | 
			
		||||
    Containers.Container,
 | 
			
		||||
    Containers.Tag,
 | 
			
		||||
@@ -24,8 +24,8 @@ defmodule Cannery.Fixtures do
 | 
			
		||||
  def user_fixture(attrs \\ %{}) do
 | 
			
		||||
    attrs
 | 
			
		||||
    |> Enum.into(%{
 | 
			
		||||
      "email" => unique_user_email(),
 | 
			
		||||
      "password" => valid_user_password()
 | 
			
		||||
      email: unique_user_email(),
 | 
			
		||||
      password: valid_user_password()
 | 
			
		||||
    })
 | 
			
		||||
    |> Accounts.register_user()
 | 
			
		||||
    |> unwrap_ok_tuple()
 | 
			
		||||
@@ -36,8 +36,8 @@ defmodule Cannery.Fixtures do
 | 
			
		||||
  def admin_fixture(attrs \\ %{}) do
 | 
			
		||||
    attrs
 | 
			
		||||
    |> Enum.into(%{
 | 
			
		||||
      "email" => unique_user_email(),
 | 
			
		||||
      "password" => valid_user_password()
 | 
			
		||||
      email: unique_user_email(),
 | 
			
		||||
      password: valid_user_password()
 | 
			
		||||
    })
 | 
			
		||||
    |> Accounts.register_user()
 | 
			
		||||
    |> unwrap_ok_tuple()
 | 
			
		||||
@@ -69,18 +69,18 @@ defmodule Cannery.Fixtures do
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @doc """
 | 
			
		||||
  Generate a ShotGroup
 | 
			
		||||
  Generate a ShotRecord
 | 
			
		||||
  """
 | 
			
		||||
  @spec shot_group_fixture(User.t(), AmmoGroup.t()) :: ShotGroup.t()
 | 
			
		||||
  @spec shot_group_fixture(attrs :: map(), User.t(), AmmoGroup.t()) :: ShotGroup.t()
 | 
			
		||||
  def shot_group_fixture(attrs \\ %{}, %User{} = user, %AmmoGroup{} = ammo_group) do
 | 
			
		||||
  @spec shot_record_fixture(User.t(), Pack.t()) :: ShotRecord.t()
 | 
			
		||||
  @spec shot_record_fixture(attrs :: map(), User.t(), Pack.t()) :: ShotRecord.t()
 | 
			
		||||
  def shot_record_fixture(attrs \\ %{}, %User{} = user, %Pack{} = pack) do
 | 
			
		||||
    attrs
 | 
			
		||||
    |> Enum.into(%{
 | 
			
		||||
      "count" => 20,
 | 
			
		||||
      "date" => ~N[2022-02-13 03:17:00],
 | 
			
		||||
      "notes" => random_string()
 | 
			
		||||
      count: 20,
 | 
			
		||||
      date: ~N[2022-02-13 03:17:00],
 | 
			
		||||
      notes: random_string()
 | 
			
		||||
    })
 | 
			
		||||
    |> Cannery.ActivityLog.create_shot_group(user, ammo_group)
 | 
			
		||||
    |> Cannery.ActivityLog.create_shot_record(user, pack)
 | 
			
		||||
    |> unwrap_ok_tuple()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -91,52 +91,90 @@ defmodule Cannery.Fixtures do
 | 
			
		||||
  @spec container_fixture(attrs :: map(), User.t()) :: Container.t()
 | 
			
		||||
  def container_fixture(attrs \\ %{}, %User{} = user) do
 | 
			
		||||
    attrs
 | 
			
		||||
    |> Enum.into(%{"name" => random_string(), "type" => "Ammo can"})
 | 
			
		||||
    |> Enum.into(%{
 | 
			
		||||
      name: random_string(),
 | 
			
		||||
      type: "Ammo can",
 | 
			
		||||
      location: random_string(),
 | 
			
		||||
      desc: random_string()
 | 
			
		||||
    })
 | 
			
		||||
    |> Containers.create_container(user)
 | 
			
		||||
    |> unwrap_ok_tuple()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @doc """
 | 
			
		||||
  Generate a AmmoType
 | 
			
		||||
  Generate a Type
 | 
			
		||||
  """
 | 
			
		||||
  @spec ammo_type_fixture(User.t()) :: AmmoType.t()
 | 
			
		||||
  @spec ammo_type_fixture(attrs :: map(), User.t()) :: AmmoType.t()
 | 
			
		||||
  def ammo_type_fixture(attrs \\ %{}, %User{} = user) do
 | 
			
		||||
  @spec type_fixture(User.t()) :: Type.t()
 | 
			
		||||
  @spec type_fixture(attrs :: map(), User.t()) :: Type.t()
 | 
			
		||||
  def type_fixture(attrs \\ %{}, %User{} = user) do
 | 
			
		||||
    attrs
 | 
			
		||||
    |> Enum.into(%{"name" => random_string(), "type" => "rifle"})
 | 
			
		||||
    |> Ammo.create_ammo_type(user)
 | 
			
		||||
    |> Enum.into(%{
 | 
			
		||||
      name: random_string(),
 | 
			
		||||
      class: :rifle,
 | 
			
		||||
      desc: random_string(),
 | 
			
		||||
      bullet_type: random_string(),
 | 
			
		||||
      bullet_core: random_string(),
 | 
			
		||||
      cartridge: random_string(),
 | 
			
		||||
      caliber: random_string(),
 | 
			
		||||
      case_material: random_string(),
 | 
			
		||||
      jacket_type: random_string(),
 | 
			
		||||
      muzzle_velocity: 3,
 | 
			
		||||
      powder_type: random_string(),
 | 
			
		||||
      powder_grains_per_charge: 5,
 | 
			
		||||
      grains: 7,
 | 
			
		||||
      pressure: random_string(),
 | 
			
		||||
      primer_type: random_string(),
 | 
			
		||||
      firing_type: random_string(),
 | 
			
		||||
      wadding: random_string(),
 | 
			
		||||
      shot_type: random_string(),
 | 
			
		||||
      shot_material: random_string(),
 | 
			
		||||
      shot_size: random_string(),
 | 
			
		||||
      unfired_length: random_string(),
 | 
			
		||||
      brass_height: random_string(),
 | 
			
		||||
      chamber_size: random_string(),
 | 
			
		||||
      load_grains: 9,
 | 
			
		||||
      shot_charge_weight: random_string(),
 | 
			
		||||
      dram_equivalent: random_string(),
 | 
			
		||||
      tracer: false,
 | 
			
		||||
      incendiary: false,
 | 
			
		||||
      blank: false,
 | 
			
		||||
      corrosive: false,
 | 
			
		||||
      manufacturer: random_string(),
 | 
			
		||||
      upc: random_string()
 | 
			
		||||
    })
 | 
			
		||||
    |> Ammo.create_type(user)
 | 
			
		||||
    |> unwrap_ok_tuple()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @doc """
 | 
			
		||||
  Generate a AmmoGroup
 | 
			
		||||
  Generate a Pack
 | 
			
		||||
  """
 | 
			
		||||
  @spec ammo_group_fixture(AmmoType.t(), Container.t(), User.t()) ::
 | 
			
		||||
          {count :: non_neg_integer(), [AmmoGroup.t()]}
 | 
			
		||||
  @spec ammo_group_fixture(attrs :: map(), AmmoType.t(), Container.t(), User.t()) ::
 | 
			
		||||
          {count :: non_neg_integer(), [AmmoGroup.t()]}
 | 
			
		||||
  @spec ammo_group_fixture(
 | 
			
		||||
  @spec pack_fixture(Type.t(), Container.t(), User.t()) ::
 | 
			
		||||
          {count :: non_neg_integer(), [Pack.t()]}
 | 
			
		||||
  @spec pack_fixture(attrs :: map(), Type.t(), Container.t(), User.t()) ::
 | 
			
		||||
          {count :: non_neg_integer(), [Pack.t()]}
 | 
			
		||||
  @spec pack_fixture(
 | 
			
		||||
          attrs :: map(),
 | 
			
		||||
          multiplier :: non_neg_integer(),
 | 
			
		||||
          AmmoType.t(),
 | 
			
		||||
          Type.t(),
 | 
			
		||||
          Container.t(),
 | 
			
		||||
          User.t()
 | 
			
		||||
        ) :: {count :: non_neg_integer(), [AmmoGroup.t()]}
 | 
			
		||||
  def ammo_group_fixture(
 | 
			
		||||
        ) :: {count :: non_neg_integer(), [Pack.t()]}
 | 
			
		||||
  def pack_fixture(
 | 
			
		||||
        attrs \\ %{},
 | 
			
		||||
        multiplier \\ 1,
 | 
			
		||||
        %AmmoType{id: ammo_type_id},
 | 
			
		||||
        %Type{id: type_id},
 | 
			
		||||
        %Container{id: container_id},
 | 
			
		||||
        %User{} = user
 | 
			
		||||
      ) do
 | 
			
		||||
    attrs
 | 
			
		||||
    |> Enum.into(%{
 | 
			
		||||
      "ammo_type_id" => ammo_type_id,
 | 
			
		||||
      "container_id" => container_id,
 | 
			
		||||
      "count" => 20,
 | 
			
		||||
      "purchased_on" => Date.utc_today()
 | 
			
		||||
      type_id: type_id,
 | 
			
		||||
      container_id: container_id,
 | 
			
		||||
      count: 20,
 | 
			
		||||
      purchased_on: Date.utc_today()
 | 
			
		||||
    })
 | 
			
		||||
    |> Ammo.create_ammo_groups(multiplier, user)
 | 
			
		||||
    |> Ammo.create_packs(multiplier, user)
 | 
			
		||||
    |> unwrap_ok_tuple()
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -148,9 +186,9 @@ defmodule Cannery.Fixtures do
 | 
			
		||||
  def tag_fixture(attrs \\ %{}, %User{} = user) do
 | 
			
		||||
    attrs
 | 
			
		||||
    |> Enum.into(%{
 | 
			
		||||
      "bg_color" => "#100000",
 | 
			
		||||
      "name" => random_string(),
 | 
			
		||||
      "text_color" => "#000000"
 | 
			
		||||
      bg_color: "#100000",
 | 
			
		||||
      name: random_string(),
 | 
			
		||||
      text_color: "#000000"
 | 
			
		||||
    })
 | 
			
		||||
    |> Containers.create_tag(user)
 | 
			
		||||
    |> unwrap_ok_tuple()
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user