Compare commits
	
		
			19 Commits
		
	
	
		
			0.8.4
			...
			0cae7c2940
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 0cae7c2940 | |||
| 1e645b5bb8 | |||
| bab2b26c13 | |||
| 8c95536ffd | |||
| d9251c7e4c | |||
| fe4e4f4f17 | |||
| e5e5449e8b | |||
| 355752598c | |||
| 03f8a2e8a7 | |||
| 071eb1b3c9 | |||
| 2987e4ff37 | |||
| ca81924ebe | |||
| 40e4f6fe0a | |||
| 213dcca973 | |||
| b32edd581d | |||
| 2e372ca2ab | |||
| fd0bac3bbf | |||
| f83fbc5d99 | |||
| daab051026 | 
							
								
								
									
										26
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,3 +1,29 @@ | ||||
| # v0.9.1 | ||||
| - Rename ammo_type's type to class to avoid confusion | ||||
| - Code quality improvements | ||||
|  | ||||
| # v0.9.0 | ||||
| - Add length limits to all string fields | ||||
| - Add selectable ammo types | ||||
| - Improve onboarding experience slightly | ||||
| - Remove show used view from a container since it doesn't really make that much | ||||
|   sense | ||||
|  | ||||
| # v0.8.6 | ||||
| - Fix duplicate entries showing up | ||||
| - Show ammo packs under a type in a table by default | ||||
| - Only show historical ammo type information when displaying "Show used" in table | ||||
| - Only show historical ammo pack information when displaying "Show used" in table | ||||
| - Fix some values not being sorted in tables properly | ||||
| - Code quality improvements | ||||
| - Show link to ammo pack in ammo pack table while viewing ammo type | ||||
|  | ||||
| # v0.8.5 | ||||
| - Add link in readme to github mirror | ||||
| - Fix tables unable to sort on empty dates | ||||
| - Only show historical ammo type information when displaying "Show used" | ||||
| - Fix even more accessibility issues | ||||
|  | ||||
| # v0.8.4 | ||||
| - Improve accessibility | ||||
| - Code quality improvements | ||||
|   | ||||
| @@ -92,6 +92,15 @@ Cannery is licensed under AGPLv3 or later. A copy of the latest version of the | ||||
| license can be found at | ||||
| [LICENSE.md](https://gitea.bubbletea.dev/shibao/cannery/src/branch/stable/LICENSE.md). | ||||
|  | ||||
| # Links | ||||
|  | ||||
| - [Gitea](https://gitea.bubbletea.dev/shibao/cannery): Main repo, feature | ||||
|   requests and bug reports | ||||
| - [Github](https://github.com/shibaobun/cannery): Source code mirror, please | ||||
|   don't open pull requests to this repository | ||||
| - [Weblate](https://weblate.bubbletea.dev/engage/cannery): Contribute to | ||||
|   translations! | ||||
|  | ||||
| --- | ||||
|  | ||||
| [![Build | ||||
|   | ||||
| @@ -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]' } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|   | ||||
| @@ -48,8 +48,9 @@ defmodule Cannery.Accounts.Invite do | ||||
|     %__MODULE__{} | ||||
|     |> change(token: token, created_by_id: user_id) | ||||
|     |> cast(attrs, [:name, :uses_left, :disabled_at]) | ||||
|     |> validate_required([:name, :token, :created_by_id]) | ||||
|     |> validate_length(:name, max: 255) | ||||
|     |> validate_number(:uses_left, greater_than_or_equal_to: 0) | ||||
|     |> validate_required([:name, :token, :created_by_id]) | ||||
|   end | ||||
|  | ||||
|   @doc false | ||||
| @@ -57,7 +58,8 @@ defmodule Cannery.Accounts.Invite do | ||||
|   def update_changeset(invite, attrs) do | ||||
|     invite | ||||
|     |> cast(attrs, [:name, :uses_left, :disabled_at]) | ||||
|     |> validate_required([:name]) | ||||
|     |> validate_length(:name, max: 255) | ||||
|     |> validate_number(:uses_left, greater_than_or_equal_to: 0) | ||||
|     |> validate_required([:name]) | ||||
|   end | ||||
| end | ||||
|   | ||||
| @@ -79,6 +79,7 @@ defmodule Cannery.Accounts.User do | ||||
|     %User{} | ||||
|     |> cast(attrs, [:email, :password, :locale]) | ||||
|     |> put_change(:invite_id, if(invite, do: invite.id)) | ||||
|     |> validate_length(:locale, max: 255) | ||||
|     |> validate_email() | ||||
|     |> validate_password(opts) | ||||
|   end | ||||
| @@ -209,6 +210,7 @@ defmodule Cannery.Accounts.User do | ||||
|   def locale_changeset(user_or_changeset, locale) do | ||||
|     user_or_changeset | ||||
|     |> cast(%{"locale" => locale}, [:locale]) | ||||
|     |> validate_length(:locale, max: 255) | ||||
|     |> validate_required(:locale) | ||||
|   end | ||||
| end | ||||
|   | ||||
| @@ -6,64 +6,92 @@ defmodule Cannery.ActivityLog do | ||||
|   import Ecto.Query, warn: false | ||||
|   alias Cannery.Ammo.{AmmoGroup, AmmoType} | ||||
|   alias Cannery.{Accounts.User, ActivityLog.ShotGroup, Repo} | ||||
|   alias Ecto.Multi | ||||
|   alias Ecto.{Multi, Queryable} | ||||
|  | ||||
|   @doc """ | ||||
|   Returns the list of shot_groups. | ||||
|  | ||||
|   ## Examples | ||||
|  | ||||
|       iex> list_shot_groups(%User{id: 123}) | ||||
|       iex> list_shot_groups(:all, %User{id: 123}) | ||||
|       [%ShotGroup{}, ...] | ||||
|  | ||||
|       iex> list_shot_groups("cool", %User{id: 123}) | ||||
|       iex> list_shot_groups("cool", :all, %User{id: 123}) | ||||
|       [%ShotGroup{notes: "My cool shot group"}, ...] | ||||
|  | ||||
|       iex> list_shot_groups("cool", :rifle, %User{id: 123}) | ||||
|       [%ShotGroup{notes: "Shot some rifle rounds"}, ...] | ||||
|  | ||||
|   """ | ||||
|   @spec list_shot_groups(User.t()) :: [ShotGroup.t()] | ||||
|   @spec list_shot_groups(search :: nil | String.t(), User.t()) :: [ShotGroup.t()] | ||||
|   def list_shot_groups(search \\ nil, user) | ||||
|   @spec list_shot_groups(AmmoType.class() | :all, User.t()) :: [ShotGroup.t()] | ||||
|   @spec list_shot_groups(search :: nil | String.t(), AmmoType.class() | :all, User.t()) :: | ||||
|           [ShotGroup.t()] | ||||
|   def list_shot_groups(search \\ nil, type, %{id: user_id}) do | ||||
|     from(sg in ShotGroup, | ||||
|       as: :sg, | ||||
|       left_join: ag in AmmoGroup, | ||||
|       as: :ag, | ||||
|       on: sg.ammo_group_id == ag.id, | ||||
|       left_join: at in AmmoType, | ||||
|       as: :at, | ||||
|       on: ag.ammo_type_id == at.id, | ||||
|       where: sg.user_id == ^user_id, | ||||
|       distinct: sg.id | ||||
|     ) | ||||
|     |> list_shot_groups_search(search) | ||||
|     |> list_shot_groups_filter_type(type) | ||||
|     |> Repo.all() | ||||
|   end | ||||
|  | ||||
|   def list_shot_groups(search, %{id: user_id}) when search |> is_nil() or search == "", | ||||
|     do: Repo.all(from sg in ShotGroup, where: sg.user_id == ^user_id) | ||||
|   @spec list_shot_groups_search(Queryable.t(), search :: String.t() | nil) :: | ||||
|           Queryable.t() | ||||
|   defp list_shot_groups_search(query, search) when search in ["", nil], do: query | ||||
|  | ||||
|   def list_shot_groups(search, %{id: user_id}) when search |> is_binary() do | ||||
|   defp list_shot_groups_search(query, search) when search |> is_binary() do | ||||
|     trimmed_search = String.trim(search) | ||||
|  | ||||
|     Repo.all( | ||||
|       from sg in ShotGroup, | ||||
|         left_join: ag in AmmoGroup, | ||||
|         on: sg.ammo_group_id == ag.id, | ||||
|         left_join: at in AmmoType, | ||||
|         on: ag.ammo_type_id == at.id, | ||||
|         where: sg.user_id == ^user_id, | ||||
|         where: | ||||
|           fragment( | ||||
|             "? @@ websearch_to_tsquery('english', ?)", | ||||
|             sg.search, | ||||
|             ^trimmed_search | ||||
|           ) or | ||||
|             fragment( | ||||
|               "? @@ websearch_to_tsquery('english', ?)", | ||||
|               ag.search, | ||||
|               ^trimmed_search | ||||
|             ) or | ||||
|             fragment( | ||||
|               "? @@ websearch_to_tsquery('english', ?)", | ||||
|               at.search, | ||||
|               ^trimmed_search | ||||
|             ), | ||||
|         order_by: { | ||||
|           :desc, | ||||
|           fragment( | ||||
|             "ts_rank_cd(?, websearch_to_tsquery('english', ?), 4)", | ||||
|             sg.search, | ||||
|             ^trimmed_search | ||||
|           ) | ||||
|         } | ||||
|     query | ||||
|     |> where( | ||||
|       [sg: sg, ag: ag, at: at], | ||||
|       fragment( | ||||
|         "? @@ websearch_to_tsquery('english', ?)", | ||||
|         sg.search, | ||||
|         ^trimmed_search | ||||
|       ) or | ||||
|         fragment( | ||||
|           "? @@ websearch_to_tsquery('english', ?)", | ||||
|           ag.search, | ||||
|           ^trimmed_search | ||||
|         ) or | ||||
|         fragment( | ||||
|           "? @@ websearch_to_tsquery('english', ?)", | ||||
|           at.search, | ||||
|           ^trimmed_search | ||||
|         ) | ||||
|     ) | ||||
|     |> order_by([sg: sg], { | ||||
|       :desc, | ||||
|       fragment( | ||||
|         "ts_rank_cd(?, websearch_to_tsquery('english', ?), 4)", | ||||
|         sg.search, | ||||
|         ^trimmed_search | ||||
|       ) | ||||
|     }) | ||||
|   end | ||||
|  | ||||
|   @spec list_shot_groups_filter_type(Queryable.t(), AmmoType.class() | :all) :: | ||||
|           Queryable.t() | ||||
|   defp list_shot_groups_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.class == :pistol) | ||||
|  | ||||
|   defp list_shot_groups_filter_type(query, :shotgun), | ||||
|     do: query |> where([at: at], at.class == :shotgun) | ||||
|  | ||||
|   defp list_shot_groups_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}, | ||||
|   | ||||
| @@ -61,6 +61,7 @@ defmodule Cannery.ActivityLog.ShotGroup do | ||||
|     |> change(user_id: user_id) | ||||
|     |> change(ammo_group_id: ammo_group_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]) | ||||
|   end | ||||
| @@ -68,6 +69,7 @@ defmodule Cannery.ActivityLog.ShotGroup do | ||||
|   def create_changeset(shot_group, _invalid_user, _invalid_ammo_group, attrs) do | ||||
|     shot_group | ||||
|     |> cast(attrs, [:count, :notes, :date]) | ||||
|     |> validate_length(:notes, max: 255) | ||||
|     |> validate_required([:ammo_group_id, :user_id]) | ||||
|     |> add_error(:invalid, dgettext("errors", "Please select a valid user and ammo pack")) | ||||
|   end | ||||
| @@ -99,6 +101,7 @@ defmodule Cannery.ActivityLog.ShotGroup do | ||||
|   def update_changeset(%__MODULE__{} = shot_group, user, attrs) do | ||||
|     shot_group | ||||
|     |> 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) | ||||
|   | ||||
| @@ -9,7 +9,7 @@ defmodule Cannery.Ammo do | ||||
|   alias Cannery.Containers.{Container, ContainerTag, Tag} | ||||
|   alias Cannery.{ActivityLog, ActivityLog.ShotGroup} | ||||
|   alias Cannery.Ammo.{AmmoGroup, AmmoType} | ||||
|   alias Ecto.Changeset | ||||
|   alias Ecto.{Changeset, Queryable} | ||||
|  | ||||
|   @ammo_group_create_limit 10_000 | ||||
|   @ammo_group_preloads [:ammo_type] | ||||
| @@ -20,50 +20,70 @@ defmodule Cannery.Ammo do | ||||
|  | ||||
|   ## Examples | ||||
|  | ||||
|       iex> list_ammo_types(%User{id: 123}) | ||||
|       iex> list_ammo_types(%User{id: 123}, :all) | ||||
|       [%AmmoType{}, ...] | ||||
|  | ||||
|       iex> list_ammo_types("cool", %User{id: 123}) | ||||
|       [%AmmoType{name: "My cool ammo type"}, ...] | ||||
|       iex> list_ammo_types("cool", %User{id: 123}, :shotgun) | ||||
|       [%AmmoType{name: "My cool ammo type", class: :shotgun}, ...] | ||||
|  | ||||
|   """ | ||||
|   @spec list_ammo_types(User.t()) :: [AmmoType.t()] | ||||
|   @spec list_ammo_types(search :: nil | String.t(), User.t()) :: [AmmoType.t()] | ||||
|   def list_ammo_types(search \\ nil, user) | ||||
|   @spec list_ammo_types(User.t(), AmmoType.class() | :all) :: [AmmoType.t()] | ||||
|   @spec list_ammo_types(search :: nil | String.t(), User.t(), AmmoType.class() | :all) :: | ||||
|           [AmmoType.t()] | ||||
|   def list_ammo_types(search \\ nil, user, type) | ||||
|  | ||||
|   def list_ammo_types(search, %{id: user_id}) when search |> is_nil() or search == "" do | ||||
|     Repo.all( | ||||
|       from at in AmmoType, | ||||
|         where: at.user_id == ^user_id, | ||||
|         order_by: at.name, | ||||
|         preload: ^@ammo_type_preloads | ||||
|   def list_ammo_types(search, %{id: user_id}, type) do | ||||
|     from(at in AmmoType, | ||||
|       as: :at, | ||||
|       where: at.user_id == ^user_id, | ||||
|       preload: ^@ammo_type_preloads | ||||
|     ) | ||||
|     |> list_ammo_types_filter_type(type) | ||||
|     |> list_ammo_types_filter_search(search) | ||||
|     |> Repo.all() | ||||
|   end | ||||
|  | ||||
|   def list_ammo_types(search, %{id: user_id}) when search |> is_binary() do | ||||
|   @spec list_ammo_types_filter_search(Queryable.t(), search :: String.t() | nil) :: Queryable.t() | ||||
|   defp list_ammo_types_filter_search(query, search) when search in ["", nil], | ||||
|     do: query |> order_by([at: at], at.name) | ||||
|  | ||||
|   defp list_ammo_types_filter_search(query, search) when search |> is_binary() do | ||||
|     trimmed_search = String.trim(search) | ||||
|  | ||||
|     Repo.all( | ||||
|       from at in AmmoType, | ||||
|         where: at.user_id == ^user_id, | ||||
|         where: | ||||
|           fragment( | ||||
|             "? @@ websearch_to_tsquery('english', ?)", | ||||
|             at.search, | ||||
|             ^trimmed_search | ||||
|           ), | ||||
|         order_by: { | ||||
|           :desc, | ||||
|           fragment( | ||||
|             "ts_rank_cd(?, websearch_to_tsquery('english', ?), 4)", | ||||
|             at.search, | ||||
|             ^trimmed_search | ||||
|           ) | ||||
|         }, | ||||
|         preload: ^@ammo_type_preloads | ||||
|     query | ||||
|     |> where( | ||||
|       [at: at], | ||||
|       fragment( | ||||
|         "? @@ websearch_to_tsquery('english', ?)", | ||||
|         at.search, | ||||
|         ^trimmed_search | ||||
|       ) | ||||
|     ) | ||||
|     |> order_by( | ||||
|       [at: at], | ||||
|       { | ||||
|         :desc, | ||||
|         fragment( | ||||
|           "ts_rank_cd(?, websearch_to_tsquery('english', ?), 4)", | ||||
|           at.search, | ||||
|           ^trimmed_search | ||||
|         ) | ||||
|       } | ||||
|     ) | ||||
|   end | ||||
|  | ||||
|   @spec list_ammo_types_filter_type(Queryable.t(), AmmoType.class() | :all) :: Queryable.t() | ||||
|   defp list_ammo_types_filter_type(query, :rifle), | ||||
|     do: query |> where([at: at], at.class == :rifle) | ||||
|  | ||||
|   defp list_ammo_types_filter_type(query, :pistol), | ||||
|     do: query |> where([at: at], at.class == :pistol) | ||||
|  | ||||
|   defp list_ammo_types_filter_type(query, :shotgun), | ||||
|     do: query |> where([at: at], at.class == :shotgun) | ||||
|  | ||||
|   defp list_ammo_types_filter_type(query, _all), do: query | ||||
|  | ||||
|   @doc """ | ||||
|   Returns a count of ammo_types. | ||||
|  | ||||
| @@ -80,7 +100,7 @@ defmodule Cannery.Ammo do | ||||
|         where: at.user_id == ^user_id, | ||||
|         select: count(at.id), | ||||
|         distinct: true | ||||
|     ) | ||||
|     ) || 0 | ||||
|   end | ||||
|  | ||||
|   @doc """ | ||||
| @@ -375,36 +395,31 @@ defmodule Cannery.Ammo do | ||||
|  | ||||
|   """ | ||||
|   @spec list_ammo_groups_for_type(AmmoType.t(), User.t()) :: [AmmoGroup.t()] | ||||
|   @spec list_ammo_groups_for_type(AmmoType.t(), User.t(), include_empty :: boolean()) :: | ||||
|   @spec list_ammo_groups_for_type(AmmoType.t(), User.t(), show_used :: boolean()) :: | ||||
|           [AmmoGroup.t()] | ||||
|   def list_ammo_groups_for_type(ammo_type, user, include_empty \\ false) | ||||
|   def list_ammo_groups_for_type(ammo_type, user, show_used \\ false) | ||||
|  | ||||
|   def list_ammo_groups_for_type( | ||||
|         %AmmoType{id: ammo_type_id, user_id: user_id}, | ||||
|         %User{id: user_id}, | ||||
|         true = _include_empty | ||||
|         show_used | ||||
|       ) do | ||||
|     Repo.all( | ||||
|       from ag in AmmoGroup, | ||||
|         where: ag.ammo_type_id == ^ammo_type_id, | ||||
|         where: ag.user_id == ^user_id, | ||||
|         preload: ^@ammo_group_preloads | ||||
|     from(ag in AmmoGroup, | ||||
|       as: :ag, | ||||
|       where: ag.ammo_type_id == ^ammo_type_id, | ||||
|       where: ag.user_id == ^user_id, | ||||
|       preload: ^@ammo_group_preloads | ||||
|     ) | ||||
|     |> list_ammo_groups_for_type_show_used(show_used) | ||||
|     |> Repo.all() | ||||
|   end | ||||
|  | ||||
|   def list_ammo_groups_for_type( | ||||
|         %AmmoType{id: ammo_type_id, user_id: user_id}, | ||||
|         %User{id: user_id}, | ||||
|         false = _include_empty | ||||
|       ) do | ||||
|     Repo.all( | ||||
|       from ag in AmmoGroup, | ||||
|         where: ag.ammo_type_id == ^ammo_type_id, | ||||
|         where: ag.user_id == ^user_id, | ||||
|         where: not (ag.count == 0), | ||||
|         preload: ^@ammo_group_preloads | ||||
|     ) | ||||
|   end | ||||
|   @spec list_ammo_groups_for_type_show_used(Queryable.t(), show_used :: boolean()) :: | ||||
|           Queryable.t() | ||||
|   def list_ammo_groups_for_type_show_used(query, false), | ||||
|     do: query |> where([ag: ag], ag.count > 0) | ||||
|  | ||||
|   def list_ammo_groups_for_type_show_used(query, _true), do: query | ||||
|  | ||||
|   @doc """ | ||||
|   Returns the list of ammo_groups for a user and container. | ||||
| @@ -413,50 +428,86 @@ defmodule Cannery.Ammo do | ||||
|  | ||||
|       iex> list_ammo_groups_for_container( | ||||
|       ...>   %Container{id: 123, user_id: 456}, | ||||
|       ...>   :rifle, | ||||
|       ...>   %User{id: 456} | ||||
|       ...> ) | ||||
|       [%AmmoGroup{}, ...] | ||||
|  | ||||
|       iex> list_ammo_groups_for_container( | ||||
|       ...>   %Container{id: 123, user_id: 456}, | ||||
|       ...>   %User{id: 456}, | ||||
|       ...>   true | ||||
|       ...>   :all, | ||||
|       ...>   %User{id: 456} | ||||
|       ...> ) | ||||
|       [%AmmoGroup{}, %AmmoGroup{}, ...] | ||||
|  | ||||
|   """ | ||||
|   @spec list_ammo_groups_for_container(Container.t(), User.t()) :: [AmmoGroup.t()] | ||||
|   @spec list_ammo_groups_for_container(Container.t(), User.t(), include_empty :: boolean()) :: | ||||
|           [AmmoGroup.t()] | ||||
|   def list_ammo_groups_for_container(container, user, include_empty \\ false) | ||||
|  | ||||
|   @spec list_ammo_groups_for_container( | ||||
|           Container.t(), | ||||
|           AmmoType.t() | :all, | ||||
|           User.t() | ||||
|         ) :: [AmmoGroup.t()] | ||||
|   def list_ammo_groups_for_container( | ||||
|         %Container{id: container_id, user_id: user_id}, | ||||
|         %User{id: user_id}, | ||||
|         true = _include_empty | ||||
|         type, | ||||
|         %User{id: user_id} | ||||
|       ) do | ||||
|     Repo.all( | ||||
|       from ag in AmmoGroup, | ||||
|         where: ag.container_id == ^container_id, | ||||
|         where: ag.user_id == ^user_id, | ||||
|         preload: ^@ammo_group_preloads | ||||
|     from(ag in AmmoGroup, | ||||
|       as: :ag, | ||||
|       join: at in assoc(ag, :ammo_type), | ||||
|       as: :at, | ||||
|       where: ag.container_id == ^container_id, | ||||
|       where: ag.user_id == ^user_id, | ||||
|       where: ag.count > 0, | ||||
|       preload: ^@ammo_group_preloads | ||||
|     ) | ||||
|     |> list_ammo_groups_for_container_filter_type(type) | ||||
|     |> Repo.all() | ||||
|   end | ||||
|  | ||||
|   def list_ammo_groups_for_container( | ||||
|         %Container{id: container_id, user_id: user_id}, | ||||
|         %User{id: user_id}, | ||||
|         false = _include_empty | ||||
|       ) do | ||||
|     Repo.all( | ||||
|       from ag in AmmoGroup, | ||||
|         where: ag.container_id == ^container_id, | ||||
|         where: ag.user_id == ^user_id, | ||||
|         where: not (ag.count == 0), | ||||
|         preload: ^@ammo_group_preloads | ||||
|   @spec list_ammo_groups_for_container_filter_type(Queryable.t(), AmmoType.class() | :all) :: | ||||
|           Queryable.t() | ||||
|   defp list_ammo_groups_for_container_filter_type(query, :rifle), | ||||
|     do: query |> where([at: at], at.class == :rifle) | ||||
|  | ||||
|   defp list_ammo_groups_for_container_filter_type(query, :pistol), | ||||
|     do: query |> where([at: at], at.class == :pistol) | ||||
|  | ||||
|   defp list_ammo_groups_for_container_filter_type(query, :shotgun), | ||||
|     do: query |> where([at: at], at.class == :shotgun) | ||||
|  | ||||
|   defp list_ammo_groups_for_container_filter_type(query, _all), do: query | ||||
|  | ||||
|   @doc """ | ||||
|   Returns a count of ammo_groups. | ||||
|  | ||||
|   ## Examples | ||||
|  | ||||
|       iex> get_ammo_groups_count!(%User{id: 123}) | ||||
|       3 | ||||
|  | ||||
|       iex> get_ammo_groups_count!(%User{id: 123}, true) | ||||
|       4 | ||||
|  | ||||
|   """ | ||||
|   @spec get_ammo_groups_count!(User.t()) :: integer() | ||||
|   @spec get_ammo_groups_count!(User.t(), show_used :: boolean()) :: integer() | ||||
|   def get_ammo_groups_count!(%User{id: user_id}, show_used \\ false) do | ||||
|     from(ag in AmmoGroup, | ||||
|       as: :ag, | ||||
|       where: ag.user_id == ^user_id, | ||||
|       select: count(ag.id), | ||||
|       distinct: true | ||||
|     ) | ||||
|     |> get_ammo_groups_count_show_used(show_used) | ||||
|     |> Repo.one() || 0 | ||||
|   end | ||||
|  | ||||
|   @spec get_ammo_groups_count_show_used(Queryable.t(), show_used :: boolean()) :: Queryable.t() | ||||
|   defp get_ammo_groups_count_show_used(query, false), | ||||
|     do: query |> where([ag: ag], ag.count > 0) | ||||
|  | ||||
|   defp get_ammo_groups_count_show_used(query, _true), do: query | ||||
|  | ||||
|   @doc """ | ||||
|   Returns the count of ammo_groups for an ammo type. | ||||
|  | ||||
| @@ -477,15 +528,15 @@ defmodule Cannery.Ammo do | ||||
|  | ||||
|   """ | ||||
|   @spec get_ammo_groups_count_for_type(AmmoType.t(), User.t()) :: non_neg_integer() | ||||
|   @spec get_ammo_groups_count_for_type(AmmoType.t(), User.t(), include_empty :: boolean()) :: | ||||
|   @spec get_ammo_groups_count_for_type(AmmoType.t(), User.t(), show_used :: boolean()) :: | ||||
|           non_neg_integer() | ||||
|   def get_ammo_groups_count_for_type( | ||||
|         %AmmoType{id: ammo_type_id} = ammo_type, | ||||
|         user, | ||||
|         include_empty \\ false | ||||
|         show_used \\ false | ||||
|       ) do | ||||
|     [ammo_type] | ||||
|     |> get_ammo_groups_count_for_types(user, include_empty) | ||||
|     |> get_ammo_groups_count_for_types(user, show_used) | ||||
|     |> Map.get(ammo_type_id, 0) | ||||
|   end | ||||
|  | ||||
| @@ -510,28 +561,31 @@ defmodule Cannery.Ammo do | ||||
|   """ | ||||
|   @spec get_ammo_groups_count_for_types([AmmoType.t()], User.t()) :: | ||||
|           %{optional(AmmoType.id()) => non_neg_integer()} | ||||
|   @spec get_ammo_groups_count_for_types([AmmoType.t()], User.t(), include_empty :: boolean()) :: | ||||
|   @spec get_ammo_groups_count_for_types([AmmoType.t()], User.t(), show_used :: boolean()) :: | ||||
|           %{optional(AmmoType.id()) => non_neg_integer()} | ||||
|   def get_ammo_groups_count_for_types(ammo_types, %User{id: user_id}, include_empty \\ false) do | ||||
|   def get_ammo_groups_count_for_types(ammo_types, %User{id: user_id}, show_used \\ false) do | ||||
|     ammo_type_ids = | ||||
|       ammo_types | ||||
|       |> Enum.map(fn %AmmoType{id: ammo_type_id, user_id: ^user_id} -> ammo_type_id end) | ||||
|  | ||||
|     from(ag in AmmoGroup, | ||||
|       as: :ag, | ||||
|       where: ag.user_id == ^user_id, | ||||
|       where: ag.ammo_type_id in ^ammo_type_ids, | ||||
|       group_by: ag.ammo_type_id, | ||||
|       select: {ag.ammo_type_id, count(ag.id)} | ||||
|     ) | ||||
|     |> maybe_include_empty(include_empty) | ||||
|     |> get_ammo_groups_count_for_types_maybe_show_used(show_used) | ||||
|     |> Repo.all() | ||||
|     |> Map.new() | ||||
|   end | ||||
|  | ||||
|   defp maybe_include_empty(query, true), do: query | ||||
|   @spec get_ammo_groups_count_for_types_maybe_show_used(Queryable.t(), show_used :: boolean()) :: | ||||
|           Queryable.t() | ||||
|   defp get_ammo_groups_count_for_types_maybe_show_used(query, true), do: query | ||||
|  | ||||
|   defp maybe_include_empty(query, _false) do | ||||
|     query |> where([ag], not (ag.count == 0)) | ||||
|   defp get_ammo_groups_count_for_types_maybe_show_used(query, _false) do | ||||
|     query |> where([ag: ag], not (ag.count == 0)) | ||||
|   end | ||||
|  | ||||
|   @doc """ | ||||
| @@ -628,7 +682,7 @@ defmodule Cannery.Ammo do | ||||
|     Repo.all( | ||||
|       from ag in AmmoGroup, | ||||
|         where: ag.container_id in ^container_ids, | ||||
|         where: ag.count != 0, | ||||
|         where: ag.count > 0, | ||||
|         group_by: ag.container_id, | ||||
|         select: {ag.container_id, count(ag.id)} | ||||
|     ) | ||||
| @@ -690,17 +744,20 @@ defmodule Cannery.Ammo do | ||||
|       iex> list_ammo_groups(%User{id: 123}) | ||||
|       [%AmmoGroup{}, ...] | ||||
|  | ||||
|       iex> list_ammo_groups("cool", true, %User{id: 123}) | ||||
|       iex> list_ammo_groups("cool", %User{id: 123}, true) | ||||
|       [%AmmoGroup{notes: "My cool ammo group"}, ...] | ||||
|  | ||||
|   """ | ||||
|   @spec list_ammo_groups(User.t()) :: [AmmoGroup.t()] | ||||
|   @spec list_ammo_groups(search :: nil | String.t(), User.t()) :: [AmmoGroup.t()] | ||||
|   @spec list_ammo_groups(search :: nil | String.t(), include_empty :: boolean(), User.t()) :: | ||||
|   @spec list_ammo_groups(search :: String.t() | nil, AmmoType.class() | :all, User.t()) :: | ||||
|           [AmmoGroup.t()] | ||||
|   def list_ammo_groups(search \\ nil, include_empty \\ false, %{id: user_id}) do | ||||
|     from( | ||||
|       ag in AmmoGroup, | ||||
|   @spec list_ammo_groups( | ||||
|           search :: nil | String.t(), | ||||
|           AmmoType.class() | :all, | ||||
|           User.t(), | ||||
|           show_used :: boolean() | ||||
|         ) :: [AmmoGroup.t()] | ||||
|   def list_ammo_groups(search, type, %{id: user_id}, show_used \\ false) do | ||||
|     from(ag in AmmoGroup, | ||||
|       as: :ag, | ||||
|       join: at in assoc(ag, :ammo_type), | ||||
|       as: :at, | ||||
| @@ -715,19 +772,35 @@ defmodule Cannery.Ammo do | ||||
|       on: c.user_id == t.user_id, | ||||
|       as: :t, | ||||
|       where: ag.user_id == ^user_id, | ||||
|       distinct: ag.id, | ||||
|       preload: ^@ammo_group_preloads | ||||
|     ) | ||||
|     |> list_ammo_groups_include_empty(include_empty) | ||||
|     |> list_ammo_groups_filter_on_type(type) | ||||
|     |> list_ammo_groups_show_used(show_used) | ||||
|     |> list_ammo_groups_search(search) | ||||
|     |> Repo.all() | ||||
|   end | ||||
|  | ||||
|   defp list_ammo_groups_include_empty(query, true), do: query | ||||
|   @spec list_ammo_groups_filter_on_type(Queryable.t(), AmmoType.class() | :all) :: Queryable.t() | ||||
|   defp list_ammo_groups_filter_on_type(query, :rifle), | ||||
|     do: query |> where([at: at], at.class == :rifle) | ||||
|  | ||||
|   defp list_ammo_groups_include_empty(query, false) do | ||||
|     query |> where([ag], not (ag.count == 0)) | ||||
|   defp list_ammo_groups_filter_on_type(query, :pistol), | ||||
|     do: query |> where([at: at], at.class == :pistol) | ||||
|  | ||||
|   defp list_ammo_groups_filter_on_type(query, :shotgun), | ||||
|     do: query |> where([at: at], at.class == :shotgun) | ||||
|  | ||||
|   defp list_ammo_groups_filter_on_type(query, _all), do: query | ||||
|  | ||||
|   @spec list_ammo_groups_show_used(Queryable.t(), show_used :: boolean()) :: Queryable.t() | ||||
|   defp list_ammo_groups_show_used(query, true), do: query | ||||
|  | ||||
|   defp list_ammo_groups_show_used(query, _false) do | ||||
|     query |> where([ag: ag], not (ag.count == 0)) | ||||
|   end | ||||
|  | ||||
|   @spec list_ammo_groups_show_used(Queryable.t(), search :: String.t() | nil) :: Queryable.t() | ||||
|   defp list_ammo_groups_search(query, nil), do: query | ||||
|   defp list_ammo_groups_search(query, ""), do: query | ||||
|  | ||||
| @@ -842,12 +915,39 @@ defmodule Cannery.Ammo do | ||||
|  | ||||
|   """ | ||||
|   @spec get_percentage_remaining(AmmoGroup.t(), User.t()) :: non_neg_integer() | ||||
|   def get_percentage_remaining(%AmmoGroup{count: 0, user_id: user_id}, %User{id: user_id}) do | ||||
|     0 | ||||
|   def get_percentage_remaining(%AmmoGroup{id: ammo_group_id} = ammo_group, user) do | ||||
|     [ammo_group] | ||||
|     |> get_percentages_remaining(user) | ||||
|     |> Map.fetch!(ammo_group_id) | ||||
|   end | ||||
|  | ||||
|   def get_percentage_remaining(%AmmoGroup{count: count} = ammo_group, current_user) do | ||||
|     round(count / get_original_count(ammo_group, current_user) * 100) | ||||
|   @doc """ | ||||
|   Calculates the percentages remaining of multiple ammo groups out of 100 | ||||
|  | ||||
|   ## Examples | ||||
|  | ||||
|       iex> get_percentages_remaining( | ||||
|       ...>   [%AmmoGroup{id: 123, count: 5, user_id: 456}], | ||||
|       ...>   %User{id: 456} | ||||
|       ...> ) | ||||
|       %{123 => 100} | ||||
|  | ||||
|   """ | ||||
|   @spec get_percentages_remaining([AmmoGroup.t()], User.t()) :: | ||||
|           %{optional(AmmoGroup.id()) => non_neg_integer()} | ||||
|   def get_percentages_remaining(ammo_groups, %User{id: user_id} = user) do | ||||
|     original_counts = get_original_counts(ammo_groups, user) | ||||
|  | ||||
|     ammo_groups | ||||
|     |> Map.new(fn %AmmoGroup{id: ammo_group_id, count: count, user_id: ^user_id} -> | ||||
|       percentage = | ||||
|         case count do | ||||
|           0 -> 0 | ||||
|           count -> round(count / Map.fetch!(original_counts, ammo_group_id) * 100) | ||||
|         end | ||||
|  | ||||
|       {ammo_group_id, percentage} | ||||
|     end) | ||||
|   end | ||||
|  | ||||
|   @doc """ | ||||
| @@ -956,13 +1056,21 @@ defmodule Cannery.Ammo do | ||||
|   @spec create_ammo_groups(attrs :: map(), multiplier :: non_neg_integer(), User.t()) :: | ||||
|           {:ok, {count :: non_neg_integer(), [AmmoGroup.t()] | nil}} | ||||
|           | {:error, AmmoGroup.changeset()} | ||||
|   def create_ammo_groups( | ||||
|         %{"ammo_type_id" => ammo_type_id, "container_id" => container_id} = attrs, | ||||
|         multiplier, | ||||
|         %User{} = user | ||||
|       ) | ||||
|       when multiplier >= 1 and multiplier <= @ammo_group_create_limit and | ||||
|              not (ammo_type_id |> is_nil()) and not (container_id |> is_nil()) do | ||||
|   def create_ammo_groups(attrs, multiplier, %User{} = user) do | ||||
|     attrs | ||||
|     |> Map.new(fn {k, v} -> {to_string(k), v} end) | ||||
|     |> do_create_ammo_groups(multiplier, user) | ||||
|   end | ||||
|  | ||||
|   defp do_create_ammo_groups( | ||||
|          %{"ammo_type_id" => ammo_type_id, "container_id" => container_id} = attrs, | ||||
|          multiplier, | ||||
|          user | ||||
|        ) | ||||
|        when multiplier >= 1 and | ||||
|               multiplier <= @ammo_group_create_limit and | ||||
|               ammo_type_id |> is_binary() and | ||||
|               container_id |> is_binary() do | ||||
|     now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) | ||||
|  | ||||
|     changesets = | ||||
| @@ -998,12 +1106,12 @@ defmodule Cannery.Ammo do | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   def create_ammo_groups( | ||||
|         %{"ammo_type_id" => ammo_type_id, "container_id" => container_id} = attrs, | ||||
|         _multiplier, | ||||
|         user | ||||
|       ) | ||||
|       when is_binary(ammo_type_id) and is_binary(container_id) do | ||||
|   defp do_create_ammo_groups( | ||||
|          %{"ammo_type_id" => ammo_type_id, "container_id" => container_id} = attrs, | ||||
|          _multiplier, | ||||
|          user | ||||
|        ) | ||||
|        when is_binary(ammo_type_id) and is_binary(container_id) do | ||||
|     changeset = | ||||
|       %AmmoGroup{} | ||||
|       |> AmmoGroup.create_changeset( | ||||
| @@ -1017,7 +1125,7 @@ defmodule Cannery.Ammo do | ||||
|     {:error, changeset} | ||||
|   end | ||||
|  | ||||
|   def create_ammo_groups(invalid_attrs, _multiplier, user) do | ||||
|   defp do_create_ammo_groups(invalid_attrs, _multiplier, user) do | ||||
|     {:error, %AmmoGroup{} |> AmmoGroup.create_changeset(nil, nil, user, invalid_attrs)} | ||||
|   end | ||||
|  | ||||
|   | ||||
| @@ -42,30 +42,47 @@ defmodule Cannery.Ammo.AmmoType do | ||||
|     field :name, :string | ||||
|     field :desc, :string | ||||
|  | ||||
|     # https://en.wikipedia.org/wiki/Bullet#Abbreviations | ||||
|     field :class, Ecto.Enum, values: [:rifle, :shotgun, :pistol] | ||||
|  | ||||
|     # common fields | ||||
|     # https://shootersreference.com/reloadingdata/bullet_abbreviations/ | ||||
|     field :bullet_type, :string | ||||
|     field :bullet_core, :string | ||||
|     field :cartridge, :string | ||||
|     # also gauge for shotguns | ||||
|     field :caliber, :string | ||||
|     field :case_material, :string | ||||
|     field :jacket_type, :string | ||||
|     field :muzzle_velocity, :integer | ||||
|     field :powder_type, :string | ||||
|     field :powder_grains_per_charge, :integer | ||||
|     field :grains, :integer | ||||
|     field :pressure, :string | ||||
|     field :primer_type, :string | ||||
|     field :firing_type, :string | ||||
|     field :manufacturer, :string | ||||
|     field :upc, :string | ||||
|  | ||||
|     field :tracer, :boolean, default: false | ||||
|     field :incendiary, :boolean, default: false | ||||
|     field :blank, :boolean, default: false | ||||
|     field :corrosive, :boolean, default: false | ||||
|  | ||||
|     field :manufacturer, :string | ||||
|     field :upc, :string | ||||
|     # rifle/pistol fields | ||||
|     field :cartridge, :string | ||||
|     field :jacket_type, :string | ||||
|     field :powder_grains_per_charge, :integer | ||||
|     field :muzzle_velocity, :integer | ||||
|  | ||||
|     # shotgun fields | ||||
|     field :wadding, :string | ||||
|     field :shot_type, :string | ||||
|     field :shot_material, :string | ||||
|     field :shot_size, :string | ||||
|     field :unfired_length, :string | ||||
|     field :brass_height, :string | ||||
|     field :chamber_size, :string | ||||
|     field :load_grains, :integer | ||||
|     field :shot_charge_weight, :string | ||||
|     field :dram_equivalent, :string | ||||
|  | ||||
|     field :user_id, :binary_id | ||||
|  | ||||
|     has_many :ammo_groups, AmmoGroup | ||||
|  | ||||
|     timestamps() | ||||
| @@ -75,6 +92,7 @@ defmodule Cannery.Ammo.AmmoType do | ||||
|           id: id(), | ||||
|           name: String.t(), | ||||
|           desc: String.t() | nil, | ||||
|           class: class(), | ||||
|           bullet_type: String.t() | nil, | ||||
|           bullet_core: String.t() | nil, | ||||
|           cartridge: String.t() | nil, | ||||
| @@ -88,6 +106,16 @@ defmodule Cannery.Ammo.AmmoType do | ||||
|           pressure: String.t() | nil, | ||||
|           primer_type: String.t() | nil, | ||||
|           firing_type: String.t() | nil, | ||||
|           wadding: String.t() | nil, | ||||
|           shot_type: String.t() | nil, | ||||
|           shot_material: String.t() | nil, | ||||
|           shot_size: String.t() | nil, | ||||
|           unfired_length: String.t() | nil, | ||||
|           brass_height: String.t() | nil, | ||||
|           chamber_size: String.t() | nil, | ||||
|           load_grains: integer() | nil, | ||||
|           shot_charge_weight: String.t() | nil, | ||||
|           dram_equivalent: String.t() | nil, | ||||
|           tracer: boolean(), | ||||
|           incendiary: boolean(), | ||||
|           blank: boolean(), | ||||
| @@ -102,12 +130,14 @@ defmodule Cannery.Ammo.AmmoType do | ||||
|   @type new_ammo_type :: %__MODULE__{} | ||||
|   @type id :: UUID.t() | ||||
|   @type changeset :: Changeset.t(t() | new_ammo_type()) | ||||
|   @type class :: :rifle | :shotgun | :pistol | nil | ||||
|  | ||||
|   @spec changeset_fields() :: [atom()] | ||||
|   defp changeset_fields, | ||||
|     do: [ | ||||
|       :name, | ||||
|       :desc, | ||||
|       :class, | ||||
|       :bullet_type, | ||||
|       :bullet_core, | ||||
|       :cartridge, | ||||
| @@ -121,6 +151,16 @@ defmodule Cannery.Ammo.AmmoType do | ||||
|       :pressure, | ||||
|       :primer_type, | ||||
|       :firing_type, | ||||
|       :wadding, | ||||
|       :shot_type, | ||||
|       :shot_material, | ||||
|       :shot_size, | ||||
|       :unfired_length, | ||||
|       :brass_height, | ||||
|       :chamber_size, | ||||
|       :load_grains, | ||||
|       :shot_charge_weight, | ||||
|       :dram_equivalent, | ||||
|       :tracer, | ||||
|       :incendiary, | ||||
|       :blank, | ||||
| @@ -129,20 +169,55 @@ defmodule Cannery.Ammo.AmmoType do | ||||
|       :upc | ||||
|     ] | ||||
|  | ||||
|   @spec string_fields() :: [atom()] | ||||
|   defp string_fields, | ||||
|     do: [ | ||||
|       :name, | ||||
|       :bullet_type, | ||||
|       :bullet_core, | ||||
|       :cartridge, | ||||
|       :caliber, | ||||
|       :case_material, | ||||
|       :jacket_type, | ||||
|       :powder_type, | ||||
|       :pressure, | ||||
|       :primer_type, | ||||
|       :firing_type, | ||||
|       :wadding, | ||||
|       :shot_type, | ||||
|       :shot_material, | ||||
|       :shot_size, | ||||
|       :unfired_length, | ||||
|       :brass_height, | ||||
|       :chamber_size, | ||||
|       :shot_charge_weight, | ||||
|       :dram_equivalent, | ||||
|       :manufacturer, | ||||
|       :upc | ||||
|     ] | ||||
|  | ||||
|   @doc false | ||||
|   @spec create_changeset(new_ammo_type(), User.t(), attrs :: map()) :: changeset() | ||||
|   def create_changeset(ammo_type, %User{id: user_id}, attrs) do | ||||
|     ammo_type | ||||
|     |> change(user_id: user_id) | ||||
|     |> cast(attrs, changeset_fields()) | ||||
|     changeset = | ||||
|       ammo_type | ||||
|       |> change(user_id: user_id) | ||||
|       |> cast(attrs, changeset_fields()) | ||||
|  | ||||
|     string_fields() | ||||
|     |> Enum.reduce(changeset, fn field, acc -> acc |> validate_length(field, max: 255) end) | ||||
|     |> validate_required([:name, :user_id]) | ||||
|   end | ||||
|  | ||||
|   @doc false | ||||
|   @spec update_changeset(t() | new_ammo_type(), attrs :: map()) :: changeset() | ||||
|   def update_changeset(ammo_type, attrs) do | ||||
|     ammo_type | ||||
|     |> cast(attrs, changeset_fields()) | ||||
|     changeset = | ||||
|       ammo_type | ||||
|       |> cast(attrs, changeset_fields()) | ||||
|  | ||||
|     string_fields() | ||||
|     |> Enum.reduce(changeset, fn field, acc -> acc |> validate_length(field, max: 255) end) | ||||
|     |> validate_required(:name) | ||||
|   end | ||||
| end | ||||
|   | ||||
							
								
								
									
										12
									
								
								lib/cannery/comparable_date.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								lib/cannery/comparable_date.ex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| defmodule Cannery.ComparableDate do | ||||
|   @moduledoc """ | ||||
|   A custom `Date` module that provides a `compare/2` function that is comparable | ||||
|   with nil values | ||||
|   """ | ||||
|  | ||||
|   @spec compare(Date.t() | any(), Date.t() | any()) :: :lt | :gt | :eq | ||||
|   def compare(%Date{} = date_1, %Date{} = date_2), do: Date.compare(date_1, date_2) | ||||
|   def compare(%Date{}, _date_2), do: :lt | ||||
|   def compare(_date_1, %Date{}), do: :gt | ||||
|   def compare(_date_1, _date_2), do: :eq | ||||
| end | ||||
							
								
								
									
										15
									
								
								lib/cannery/comparable_datetime.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								lib/cannery/comparable_datetime.ex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| defmodule Cannery.ComparableDateTime do | ||||
|   @moduledoc """ | ||||
|   A custom `DateTime` module that provides a `compare/2` function that is | ||||
|   comparable with nil values | ||||
|   """ | ||||
|  | ||||
|   @spec compare(DateTime.t() | any(), DateTime.t() | any()) :: :lt | :gt | :eq | ||||
|   def compare(%DateTime{} = datetime_1, %DateTime{} = datetime_2) do | ||||
|     DateTime.compare(datetime_1, datetime_2) | ||||
|   end | ||||
|  | ||||
|   def compare(%DateTime{}, _datetime_2), do: :lt | ||||
|   def compare(_datetime_1, %DateTime{}), do: :gt | ||||
|   def compare(_datetime_1, _datetime_2), do: :eq | ||||
| end | ||||
| @@ -32,6 +32,7 @@ defmodule Cannery.Containers do | ||||
|       as: :t, | ||||
|       where: c.user_id == ^user_id, | ||||
|       order_by: c.name, | ||||
|       distinct: c.id, | ||||
|       preload: ^@container_preloads | ||||
|     ) | ||||
|     |> list_containers_search(search) | ||||
| @@ -91,7 +92,7 @@ defmodule Cannery.Containers do | ||||
|   @doc """ | ||||
|   Gets a single container. | ||||
|  | ||||
|   Raises `Ecto.NoResultsError` if the Container does not exist. | ||||
|   Raises `KeyError` if the Container does not exist. | ||||
|  | ||||
|   ## Examples | ||||
|  | ||||
| @@ -99,18 +100,37 @@ defmodule Cannery.Containers do | ||||
|       %Container{} | ||||
|  | ||||
|       iex> get_container!(456, %User{id: 123}) | ||||
|       ** (Ecto.NoResultsError) | ||||
|       ** (KeyError) | ||||
|  | ||||
|   """ | ||||
|   @spec get_container!(Container.id(), User.t()) :: Container.t() | ||||
|   def get_container!(id, %User{id: user_id}) do | ||||
|     Repo.one!( | ||||
|   def get_container!(id, user) do | ||||
|     [id] | ||||
|     |> get_containers(user) | ||||
|     |> Map.fetch!(id) | ||||
|   end | ||||
|  | ||||
|   @doc """ | ||||
|   Gets multiple containers. | ||||
|  | ||||
|  | ||||
|   ## Examples | ||||
|  | ||||
|       iex> get_containers([123], %User{id: 123}) | ||||
|       %{123 => %Container{}} | ||||
|  | ||||
|   """ | ||||
|   @spec get_containers([Container.id()], User.t()) :: %{optional(Container.id()) => Container.t()} | ||||
|   def get_containers(ids, %User{id: user_id}) do | ||||
|     Repo.all( | ||||
|       from c in Container, | ||||
|         where: c.user_id == ^user_id, | ||||
|         where: c.id == ^id, | ||||
|         where: c.id in ^ids, | ||||
|         order_by: c.name, | ||||
|         preload: ^@container_preloads | ||||
|         preload: ^@container_preloads, | ||||
|         select: {c.id, c} | ||||
|     ) | ||||
|     |> Map.new() | ||||
|   end | ||||
|  | ||||
|   @doc """ | ||||
|   | ||||
| @@ -53,6 +53,8 @@ defmodule Cannery.Containers.Container do | ||||
|     container | ||||
|     |> change(user_id: user_id) | ||||
|     |> cast(attrs, [:name, :desc, :type, :location]) | ||||
|     |> validate_length(:name, max: 255) | ||||
|     |> validate_length(:type, max: 255) | ||||
|     |> validate_required([:name, :type, :user_id]) | ||||
|   end | ||||
|  | ||||
| @@ -61,6 +63,8 @@ defmodule Cannery.Containers.Container do | ||||
|   def update_changeset(container, attrs) do | ||||
|     container | ||||
|     |> cast(attrs, [:name, :desc, :type, :location]) | ||||
|     |> validate_length(:name, max: 255) | ||||
|     |> validate_length(:type, max: 255) | ||||
|     |> validate_required([:name, :type]) | ||||
|   end | ||||
| end | ||||
|   | ||||
| @@ -47,6 +47,9 @@ defmodule Cannery.Containers.Tag do | ||||
|     tag | ||||
|     |> change(user_id: user_id) | ||||
|     |> cast(attrs, [:name, :bg_color, :text_color]) | ||||
|     |> validate_length(:name, max: 255) | ||||
|     |> validate_length(:bg_color, max: 12) | ||||
|     |> validate_length(:text_color, max: 12) | ||||
|     |> validate_required([:name, :bg_color, :text_color, :user_id]) | ||||
|   end | ||||
|  | ||||
| @@ -55,6 +58,9 @@ defmodule Cannery.Containers.Tag do | ||||
|   def update_changeset(tag, attrs) do | ||||
|     tag | ||||
|     |> cast(attrs, [:name, :bg_color, :text_color]) | ||||
|     |> validate_length(:name, max: 255) | ||||
|     |> validate_length(:bg_color, max: 12) | ||||
|     |> validate_length(:text_color, max: 12) | ||||
|     |> validate_required([:name, :bg_color, :text_color]) | ||||
|   end | ||||
| end | ||||
|   | ||||
| @@ -39,6 +39,7 @@ | ||||
|     <%= textarea(f, :notes, | ||||
|       id: "add-shot-group-form-notes", | ||||
|       class: "input input-primary col-span-2", | ||||
|       maxlength: 255, | ||||
|       placeholder: gettext("Really great weather"), | ||||
|       phx_hook: "MaintainAttrs", | ||||
|       phx_update: "ignore" | ||||
|   | ||||
| @@ -3,7 +3,9 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do | ||||
|   A component that displays a list of ammo groups | ||||
|   """ | ||||
|   use CanneryWeb, :live_component | ||||
|   alias Cannery.{Accounts.User, ActivityLog, Ammo, Ammo.AmmoGroup, Containers} | ||||
|   alias Cannery.{Accounts.User, Ammo.AmmoGroup, ComparableDate} | ||||
|   alias Cannery.{ActivityLog, Ammo, Containers} | ||||
|   alias CanneryWeb.Components.TableComponent | ||||
|   alias Ecto.UUID | ||||
|   alias Phoenix.LiveView.{Rendered, Socket} | ||||
|  | ||||
| @@ -13,6 +15,7 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do | ||||
|             required(:id) => UUID.t(), | ||||
|             required(:current_user) => User.t(), | ||||
|             required(:ammo_groups) => [AmmoGroup.t()], | ||||
|             required(:show_used) => boolean(), | ||||
|             optional(:ammo_type) => Rendered.t(), | ||||
|             optional(:range) => Rendered.t(), | ||||
|             optional(:container) => Rendered.t(), | ||||
| @@ -21,7 +24,11 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do | ||||
|           }, | ||||
|           Socket.t() | ||||
|         ) :: {:ok, Socket.t()} | ||||
|   def update(%{id: _id, ammo_groups: _ammo_group, current_user: _current_user} = assigns, socket) do | ||||
|   def update( | ||||
|         %{id: _id, ammo_groups: _ammo_group, current_user: _current_user, show_used: _show_used} = | ||||
|           assigns, | ||||
|         socket | ||||
|       ) do | ||||
|     socket = | ||||
|       socket | ||||
|       |> assign(assigns) | ||||
| @@ -42,61 +49,69 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do | ||||
|              ammo_type: ammo_type, | ||||
|              range: range, | ||||
|              container: container, | ||||
|              actions: actions | ||||
|              actions: actions, | ||||
|              show_used: show_used | ||||
|            } | ||||
|          } = socket | ||||
|        ) do | ||||
|     columns = | ||||
|       if actions == [] do | ||||
|         [] | ||||
|       else | ||||
|         [%{label: nil, key: :actions, sortable: false}] | ||||
|       end | ||||
|       [] | ||||
|       |> TableComponent.maybe_compose_columns( | ||||
|         %{label: gettext("Actions"), key: :actions, sortable: false}, | ||||
|         actions != [] | ||||
|       ) | ||||
|       |> TableComponent.maybe_compose_columns(%{ | ||||
|         label: gettext("Last used on"), | ||||
|         key: :used_up_on, | ||||
|         type: ComparableDate | ||||
|       }) | ||||
|       |> TableComponent.maybe_compose_columns(%{ | ||||
|         label: gettext("Purchased on"), | ||||
|         key: :purchased_on, | ||||
|         type: ComparableDate | ||||
|       }) | ||||
|       |> TableComponent.maybe_compose_columns( | ||||
|         %{label: gettext("Container"), key: :container}, | ||||
|         container != [] | ||||
|       ) | ||||
|       |> TableComponent.maybe_compose_columns( | ||||
|         %{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("% left"), key: :remaining}, | ||||
|         show_used | ||||
|       ) | ||||
|       |> TableComponent.maybe_compose_columns( | ||||
|         %{label: gettext("Original Count"), key: :original_count}, | ||||
|         show_used | ||||
|       ) | ||||
|       |> TableComponent.maybe_compose_columns(%{ | ||||
|         label: if(show_used, do: gettext("Current Count"), else: gettext("Count")), | ||||
|         key: :count | ||||
|       }) | ||||
|       |> TableComponent.maybe_compose_columns( | ||||
|         %{label: gettext("Ammo type"), key: :ammo_type}, | ||||
|         ammo_type != [] | ||||
|       ) | ||||
|  | ||||
|     columns = [ | ||||
|       %{label: gettext("Purchased on"), key: :purchased_on, type: Date}, | ||||
|       %{label: gettext("Last used on"), key: :used_up_on, type: Date} | columns | ||||
|     ] | ||||
|  | ||||
|     columns = | ||||
|       if container == [] do | ||||
|         columns | ||||
|       else | ||||
|         [%{label: gettext("Container"), key: :container} | columns] | ||||
|       end | ||||
|  | ||||
|     columns = | ||||
|       if range == [] do | ||||
|         columns | ||||
|       else | ||||
|         [%{label: gettext("Range"), key: :range} | columns] | ||||
|       end | ||||
|  | ||||
|     columns = [ | ||||
|       %{label: gettext("Count"), key: :count}, | ||||
|       %{label: gettext("Original Count"), key: :original_count}, | ||||
|       %{label: gettext("Price paid"), key: :price_paid}, | ||||
|       %{label: gettext("CPR"), key: :cpr}, | ||||
|       %{label: gettext("% left"), key: :remaining}, | ||||
|       %{label: gettext("Notes"), key: :notes} | ||||
|       | columns | ||||
|     ] | ||||
|  | ||||
|     columns = | ||||
|       if ammo_type == [] do | ||||
|         columns | ||||
|       else | ||||
|         [%{label: gettext("Ammo type"), key: :ammo_type} | columns] | ||||
|       end | ||||
|     containers = | ||||
|       ammo_groups | ||||
|       |> 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, | ||||
|       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), | ||||
|       actions: actions, | ||||
|       range: range | ||||
|     } | ||||
| @@ -114,12 +129,7 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do | ||||
|   def render(assigns) do | ||||
|     ~H""" | ||||
|     <div id={@id} class="w-full"> | ||||
|       <.live_component | ||||
|         module={CanneryWeb.Components.TableComponent} | ||||
|         id={"table-#{@id}"} | ||||
|         columns={@columns} | ||||
|         rows={@rows} | ||||
|       /> | ||||
|       <.live_component module={TableComponent} id={"table-#{@id}"} columns={@columns} rows={@rows} /> | ||||
|     </div> | ||||
|     """ | ||||
|   end | ||||
| @@ -147,10 +157,11 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do | ||||
|      """} | ||||
|   end | ||||
|  | ||||
|   defp get_value_for_key(:price_paid, %{price_paid: nil}, _additional_data), do: {"", nil} | ||||
|   defp get_value_for_key(:price_paid, %{price_paid: nil}, _additional_data), | ||||
|     do: {0, gettext("No cost information")} | ||||
|  | ||||
|   defp get_value_for_key(:price_paid, %{price_paid: price_paid}, _additional_data), | ||||
|     do: gettext("$%{amount}", amount: display_currency(price_paid)) | ||||
|     do: {price_paid, gettext("$%{amount}", amount: display_currency(price_paid))} | ||||
|  | ||||
|   defp get_value_for_key(:purchased_on, %{purchased_on: purchased_on} = assigns, _additional_data) do | ||||
|     {purchased_on, | ||||
| @@ -182,11 +193,14 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do | ||||
|      """} | ||||
|   end | ||||
|  | ||||
|   defp get_value_for_key(:remaining, ammo_group, %{current_user: current_user}), | ||||
|     do: | ||||
|       gettext("%{percentage}%", | ||||
|         percentage: ammo_group |> Ammo.get_percentage_remaining(current_user) | ||||
|       ) | ||||
|   defp get_value_for_key( | ||||
|          :remaining, | ||||
|          %{id: ammo_group_id}, | ||||
|          %{percentages_remaining: percentages_remaining} | ||||
|        ) do | ||||
|     percentage = Map.fetch!(percentages_remaining, ammo_group_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} | ||||
| @@ -201,12 +215,13 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do | ||||
|   defp get_value_for_key( | ||||
|          :container, | ||||
|          %{container_id: container_id} = ammo_group, | ||||
|          %{container: container, current_user: current_user} | ||||
|          %{container: container_block, containers: containers} | ||||
|        ) do | ||||
|     container = %{name: container_name} = Map.fetch!(containers, container_id) | ||||
|  | ||||
|     assigns = %{ | ||||
|       container: | ||||
|         %{name: container_name} = container_id |> Containers.get_container!(current_user), | ||||
|       container_block: container, | ||||
|       container: container, | ||||
|       container_block: container_block, | ||||
|       ammo_group: ammo_group | ||||
|     } | ||||
|  | ||||
| @@ -216,21 +231,24 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do | ||||
|      """} | ||||
|   end | ||||
|  | ||||
|   defp get_value_for_key(:original_count, %{id: ammo_group_id}, %{ | ||||
|          original_counts: original_counts | ||||
|        }) do | ||||
|   defp get_value_for_key( | ||||
|          :original_count, | ||||
|          %{id: ammo_group_id}, | ||||
|          %{original_counts: original_counts} | ||||
|        ) do | ||||
|     Map.fetch!(original_counts, ammo_group_id) | ||||
|   end | ||||
|  | ||||
|   defp get_value_for_key(:cpr, %{price_paid: nil}, _additional_data), | ||||
|     do: gettext("No cost information") | ||||
|     do: {0, gettext("No cost information")} | ||||
|  | ||||
|   defp get_value_for_key(:cpr, %{id: ammo_group_id}, %{cprs: cprs}) do | ||||
|     gettext("$%{amount}", amount: display_currency(Map.fetch!(cprs, ammo_group_id))) | ||||
|     amount = Map.fetch!(cprs, ammo_group_id) | ||||
|     {amount, gettext("$%{amount}", amount: display_currency(amount))} | ||||
|   end | ||||
|  | ||||
|   defp get_value_for_key(:count, %{count: count}, _additional_data), | ||||
|     do: if(count == 0, do: gettext("Empty"), else: count) | ||||
|     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) | ||||
|  | ||||
|   | ||||
| @@ -4,6 +4,7 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do | ||||
|   """ | ||||
|   use CanneryWeb, :live_component | ||||
|   alias Cannery.{Accounts.User, ActivityLog, Ammo, Ammo.AmmoType} | ||||
|   alias CanneryWeb.Components.TableComponent | ||||
|   alias Ecto.UUID | ||||
|   alias Phoenix.LiveView.{Rendered, Socket} | ||||
|  | ||||
| @@ -12,6 +13,7 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do | ||||
|           %{ | ||||
|             required(:id) => UUID.t(), | ||||
|             required(:current_user) => User.t(), | ||||
|             optional(:class) => AmmoType.class() | nil, | ||||
|             optional(:show_used) => boolean(), | ||||
|             optional(:ammo_types) => [AmmoType.t()], | ||||
|             optional(:actions) => Rendered.t(), | ||||
| @@ -24,6 +26,7 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do | ||||
|       socket | ||||
|       |> assign(assigns) | ||||
|       |> assign_new(:show_used, fn -> false end) | ||||
|       |> assign_new(:class, fn -> :all end) | ||||
|       |> assign_new(:actions, fn -> [] end) | ||||
|       |> display_ammo_types() | ||||
|  | ||||
| @@ -36,109 +39,135 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do | ||||
|              ammo_types: ammo_types, | ||||
|              current_user: current_user, | ||||
|              show_used: show_used, | ||||
|              class: class, | ||||
|              actions: actions | ||||
|            } | ||||
|          } = socket | ||||
|        ) do | ||||
|     columns = | ||||
|     filtered_columns = | ||||
|       [ | ||||
|         %{label: gettext("Name"), key: :name, type: :name}, | ||||
|         %{label: gettext("Bullet type"), key: :bullet_type, type: :string}, | ||||
|         %{label: gettext("Bullet core"), key: :bullet_core, type: :string}, | ||||
|         %{label: gettext("Cartridge"), key: :cartridge, type: :string}, | ||||
|         %{label: gettext("Caliber"), key: :caliber, type: :string}, | ||||
|         %{label: gettext("Case material"), key: :case_material, type: :string}, | ||||
|         %{ | ||||
|           label: if(class == :shotgun, do: gettext("Gauge"), else: gettext("Caliber")), | ||||
|           key: :caliber, | ||||
|           type: :string | ||||
|         }, | ||||
|         %{label: gettext("Unfired shell length"), key: :unfired_length, type: :string}, | ||||
|         %{label: gettext("Brass height"), key: :brass_height, type: :string}, | ||||
|         %{label: gettext("Chamber size"), key: :chamber_size, type: :string}, | ||||
|         %{label: gettext("Chamber size"), key: :chamber_size, type: :string}, | ||||
|         %{label: gettext("Grains"), key: :grains, type: :string}, | ||||
|         %{label: gettext("Bullet type"), key: :bullet_type, type: :string}, | ||||
|         %{ | ||||
|           label: if(class == :shotgun, do: gettext("Slug core"), else: gettext("Bullet core")), | ||||
|           key: :bullet_core, | ||||
|           type: :string | ||||
|         }, | ||||
|         %{label: gettext("Jacket type"), key: :jacket_type, type: :string}, | ||||
|         %{label: gettext("Muzzle velocity"), key: :muzzle_velocity, type: :string}, | ||||
|         %{label: gettext("Case material"), key: :case_material, type: :string}, | ||||
|         %{label: gettext("Wadding"), key: :wadding, type: :string}, | ||||
|         %{label: gettext("Shot type"), key: :shot_type, type: :string}, | ||||
|         %{label: gettext("Shot material"), key: :shot_material, type: :string}, | ||||
|         %{label: gettext("Shot size"), key: :shot_size, type: :string}, | ||||
|         %{label: gettext("Load grains"), key: :load_grains, type: :string}, | ||||
|         %{label: gettext("Shot charge weight"), key: :shot_charge_weight, type: :string}, | ||||
|         %{label: gettext("Powder type"), key: :powder_type, type: :string}, | ||||
|         %{ | ||||
|           label: gettext("Powder grains per charge"), | ||||
|           key: :powder_grains_per_charge, | ||||
|           type: :string | ||||
|         }, | ||||
|         %{label: gettext("Grains"), key: :grains, type: :string}, | ||||
|         %{label: gettext("Pressure"), key: :pressure, type: :string}, | ||||
|         %{label: gettext("Dram equivalent"), key: :dram_equivalent, type: :string}, | ||||
|         %{label: gettext("Muzzle velocity"), key: :muzzle_velocity, type: :string}, | ||||
|         %{label: gettext("Primer type"), key: :primer_type, type: :string}, | ||||
|         %{label: gettext("Firing type"), key: :firing_type, type: :string}, | ||||
|         %{label: gettext("Tracer"), key: :tracer, type: :boolean}, | ||||
|         %{label: gettext("Incendiary"), key: :incendiary, type: :boolean}, | ||||
|         %{label: gettext("Blank"), key: :blank, type: :boolean}, | ||||
|         %{label: gettext("Corrosive"), key: :corrosive, type: :boolean}, | ||||
|         %{label: gettext("Manufacturer"), key: :manufacturer, type: :string}, | ||||
|         %{label: gettext("UPC"), key: "upc", type: :string} | ||||
|         %{label: gettext("Tracer"), key: :tracer, type: :atom}, | ||||
|         %{label: gettext("Incendiary"), key: :incendiary, type: :atom}, | ||||
|         %{label: gettext("Blank"), key: :blank, type: :atom}, | ||||
|         %{label: gettext("Corrosive"), key: :corrosive, type: :atom}, | ||||
|         %{label: gettext("Manufacturer"), key: :manufacturer, type: :string} | ||||
|       ] | ||||
|       |> Enum.filter(fn %{key: key, type: type} -> | ||||
|         # remove columns if all values match defaults | ||||
|         default_value = if type == :boolean, do: false, else: nil | ||||
|         default_value = if type == :atom, do: false, else: nil | ||||
|  | ||||
|         ammo_types | ||||
|         |> Enum.any?(fn ammo_type -> | ||||
|           not (ammo_type |> Map.get(key) == default_value) | ||||
|         end) | ||||
|         |> Enum.any?(fn ammo_type -> Map.get(ammo_type, key, default_value) != default_value end) | ||||
|       end) | ||||
|       |> Kernel.++([ | ||||
|         %{label: gettext("Rounds"), key: :round_count, type: :round_count} | ||||
|       ]) | ||||
|       |> Kernel.++( | ||||
|         if show_used do | ||||
|           [ | ||||
|             %{ | ||||
|               label: gettext("Used rounds"), | ||||
|               key: :used_round_count, | ||||
|               type: :used_round_count | ||||
|             }, | ||||
|             %{ | ||||
|               label: gettext("Total ever rounds"), | ||||
|               key: :historical_round_count, | ||||
|               type: :historical_round_count | ||||
|             } | ||||
|           ] | ||||
|         else | ||||
|           [] | ||||
|         end | ||||
|  | ||||
|     columns = | ||||
|       [%{label: gettext("Actions"), key: "actions", type: :actions, sortable: false}] | ||||
|       |> TableComponent.maybe_compose_columns(%{ | ||||
|         label: gettext("Average CPR"), | ||||
|         key: :avg_price_paid, | ||||
|         type: :avg_price_paid | ||||
|       }) | ||||
|       |> TableComponent.maybe_compose_columns( | ||||
|         %{ | ||||
|           label: gettext("Total ever packs"), | ||||
|           key: :historical_pack_count, | ||||
|           type: :historical_pack_count | ||||
|         }, | ||||
|         show_used | ||||
|       ) | ||||
|       |> Kernel.++([%{label: gettext("Packs"), key: :ammo_count, type: :ammo_count}]) | ||||
|       |> Kernel.++( | ||||
|         if show_used do | ||||
|           [ | ||||
|             %{ | ||||
|               label: gettext("Used packs"), | ||||
|               key: :used_packs_count, | ||||
|               type: :used_packs_count | ||||
|             }, | ||||
|             %{ | ||||
|               label: gettext("Total ever packs"), | ||||
|               key: :historical_packs_count, | ||||
|               type: :historical_packs_count | ||||
|             } | ||||
|           ] | ||||
|         else | ||||
|           [] | ||||
|         end | ||||
|       |> TableComponent.maybe_compose_columns( | ||||
|         %{ | ||||
|           label: gettext("Used packs"), | ||||
|           key: :used_pack_count, | ||||
|           type: :used_pack_count | ||||
|         }, | ||||
|         show_used | ||||
|       ) | ||||
|       |> Kernel.++([ | ||||
|         %{label: gettext("Average CPR"), key: :avg_price_paid, type: :avg_price_paid}, | ||||
|         %{label: nil, key: "actions", type: :actions, sortable: false} | ||||
|       ]) | ||||
|       |> TableComponent.maybe_compose_columns(%{ | ||||
|         label: gettext("Packs"), | ||||
|         key: :ammo_count, | ||||
|         type: :ammo_count | ||||
|       }) | ||||
|       |> TableComponent.maybe_compose_columns( | ||||
|         %{ | ||||
|           label: gettext("Total ever rounds"), | ||||
|           key: :historical_round_count, | ||||
|           type: :historical_round_count | ||||
|         }, | ||||
|         show_used | ||||
|       ) | ||||
|       |> TableComponent.maybe_compose_columns( | ||||
|         %{ | ||||
|           label: gettext("Used rounds"), | ||||
|           key: :used_round_count, | ||||
|           type: :used_round_count | ||||
|         }, | ||||
|         show_used | ||||
|       ) | ||||
|       |> TableComponent.maybe_compose_columns(%{ | ||||
|         label: gettext("Rounds"), | ||||
|         key: :round_count, | ||||
|         type: :round_count | ||||
|       }) | ||||
|       |> TableComponent.maybe_compose_columns(filtered_columns) | ||||
|       |> TableComponent.maybe_compose_columns( | ||||
|         %{label: gettext("Class"), key: :type, 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) | ||||
|  | ||||
|     used_counts = | ||||
|       show_used && ammo_types |> ActivityLog.get_used_count_for_ammo_types(current_user) | ||||
|  | ||||
|     historical_round_counts = | ||||
|       show_used && ammo_types |> Ammo.get_historical_count_for_ammo_types(current_user) | ||||
|  | ||||
|     packs_count = ammo_types |> Ammo.get_ammo_groups_count_for_types(current_user) | ||||
|  | ||||
|     historical_packs_count = | ||||
|       show_used && ammo_types |> Ammo.get_ammo_groups_count_for_types(current_user, true) | ||||
|  | ||||
|     used_packs_count = | ||||
|       show_used && ammo_types |> Ammo.get_used_ammo_groups_count_for_types(current_user) | ||||
|  | ||||
|     average_costs = ammo_types |> Ammo.get_average_cost_for_ammo_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) | ||||
|         ] | ||||
|       else | ||||
|         [nil, nil, nil, nil] | ||||
|       end | ||||
|  | ||||
|     extra_data = %{ | ||||
|       actions: actions, | ||||
|       current_user: current_user, | ||||
| @@ -146,8 +175,8 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do | ||||
|       round_counts: round_counts, | ||||
|       historical_round_counts: historical_round_counts, | ||||
|       packs_count: packs_count, | ||||
|       used_packs_count: used_packs_count, | ||||
|       historical_packs_count: historical_packs_count, | ||||
|       used_pack_counts: used_pack_counts, | ||||
|       historical_pack_counts: historical_pack_counts, | ||||
|       average_costs: average_costs | ||||
|     } | ||||
|  | ||||
| @@ -164,12 +193,7 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do | ||||
|   def render(assigns) do | ||||
|     ~H""" | ||||
|     <div id={@id} class="w-full"> | ||||
|       <.live_component | ||||
|         module={CanneryWeb.Components.TableComponent} | ||||
|         id={"table-#{@id}"} | ||||
|         columns={@columns} | ||||
|         rows={@rows} | ||||
|       /> | ||||
|       <.live_component module={TableComponent} id={"table-#{@id}"} columns={@columns} rows={@rows} /> | ||||
|     </div> | ||||
|     """ | ||||
|   end | ||||
| @@ -181,58 +205,72 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do | ||||
|     end) | ||||
|   end | ||||
|  | ||||
|   defp get_ammo_type_value(:boolean, key, ammo_type, _other_data), | ||||
|   defp get_ammo_type_value(:atom, key, ammo_type, _other_data), | ||||
|     do: ammo_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) | ||||
|     do: Map.get(round_counts, ammo_type_id, 0) | ||||
|  | ||||
|   defp get_ammo_type_value( | ||||
|          :historical_round_count, | ||||
|          _key, | ||||
|          %{id: ammo_type_id}, | ||||
|          %{historical_round_counts: historical_round_counts} | ||||
|        ), | ||||
|        do: Map.get(historical_round_counts, ammo_type_id) | ||||
|  | ||||
|   defp get_ammo_type_value(:used_round_count, _key, %{id: ammo_type_id}, %{ | ||||
|          used_counts: used_counts | ||||
|        }), | ||||
|        do: Map.get(used_counts, ammo_type_id) | ||||
|        ) do | ||||
|     Map.get(historical_round_counts, ammo_type_id, 0) | ||||
|   end | ||||
|  | ||||
|   defp get_ammo_type_value( | ||||
|          :historical_packs_count, | ||||
|          :used_round_count, | ||||
|          _key, | ||||
|          %{id: ammo_type_id}, | ||||
|          %{historical_packs_count: historical_packs_count} | ||||
|        ), | ||||
|        do: Map.get(historical_packs_count, ammo_type_id) | ||||
|          %{used_counts: used_counts} | ||||
|        ) do | ||||
|     Map.get(used_counts, ammo_type_id, 0) | ||||
|   end | ||||
|  | ||||
|   defp get_ammo_type_value(:used_packs_count, _key, %{id: ammo_type_id}, %{ | ||||
|          used_packs_count: used_packs_count | ||||
|        }), | ||||
|        do: Map.get(used_packs_count, ammo_type_id) | ||||
|   defp get_ammo_type_value( | ||||
|          :historical_pack_count, | ||||
|          _key, | ||||
|          %{id: ammo_type_id}, | ||||
|          %{historical_pack_counts: historical_pack_counts} | ||||
|        ) do | ||||
|     Map.get(historical_pack_counts, ammo_type_id, 0) | ||||
|   end | ||||
|  | ||||
|   defp get_ammo_type_value( | ||||
|          :used_pack_count, | ||||
|          _key, | ||||
|          %{id: ammo_type_id}, | ||||
|          %{used_pack_counts: used_pack_counts} | ||||
|        ) do | ||||
|     Map.get(used_pack_counts, ammo_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_ammo_type_value(:avg_price_paid, _key, %{id: ammo_type_id}, %{ | ||||
|          average_costs: average_costs | ||||
|        }) do | ||||
|   defp get_ammo_type_value( | ||||
|          :avg_price_paid, | ||||
|          _key, | ||||
|          %{id: ammo_type_id}, | ||||
|          %{average_costs: average_costs} | ||||
|        ) do | ||||
|     case Map.get(average_costs, ammo_type_id) do | ||||
|       nil -> gettext("No cost information") | ||||
|       count -> gettext("$%{amount}", amount: display_currency(count)) | ||||
|       nil -> {0, gettext("No cost information")} | ||||
|       count -> {count, gettext("$%{amount}", amount: display_currency(count))} | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   defp get_ammo_type_value(:name, _key, ammo_type, _other_data) do | ||||
|   defp get_ammo_type_value(:name, _key, %{name: ammo_type_name} = ammo_type, _other_data) do | ||||
|     assigns = %{ammo_type: ammo_type} | ||||
|  | ||||
|     ~H""" | ||||
|     <.link navigate={Routes.ammo_type_show_path(Endpoint, :show, @ammo_type)} class="link"> | ||||
|       <%= @ammo_type.name %> | ||||
|     </.link> | ||||
|     """ | ||||
|     {ammo_type_name, | ||||
|      ~H""" | ||||
|      <.link navigate={Routes.ammo_type_show_path(Endpoint, :show, @ammo_type)} class="link"> | ||||
|        <%= @ammo_type.name %> | ||||
|      </.link> | ||||
|      """} | ||||
|   end | ||||
|  | ||||
|   defp get_ammo_type_value(:actions, _key, ammo_type, %{actions: actions}) do | ||||
|   | ||||
| @@ -64,7 +64,7 @@ defmodule CanneryWeb.Components.ContainerTableComponent do | ||||
|         %{label: gettext("Packs"), key: :packs, type: :integer}, | ||||
|         %{label: gettext("Rounds"), key: :rounds, type: :integer}, | ||||
|         %{label: gettext("Tags"), key: :tags, type: :tags}, | ||||
|         %{label: nil, key: :actions, sortable: false, type: :actions} | ||||
|         %{label: gettext("Actions"), key: :actions, sortable: false, type: :actions} | ||||
|       ]) | ||||
|  | ||||
|     extra_data = %{ | ||||
|   | ||||
| @@ -6,7 +6,7 @@ defmodule CanneryWeb.CoreComponents do | ||||
|   import CanneryWeb.{Gettext, ViewHelpers} | ||||
|   alias Cannery.{Accounts, Accounts.Invite, Accounts.User} | ||||
|   alias Cannery.{Ammo, Ammo.AmmoGroup} | ||||
|   alias Cannery.{Containers, Containers.Container, Containers.Tag} | ||||
|   alias Cannery.{Containers.Container, Containers.Tag} | ||||
|   alias CanneryWeb.{Endpoint, HomeLive} | ||||
|   alias CanneryWeb.Router.Helpers, as: Routes | ||||
|   alias Phoenix.LiveView.{JS, Rendered} | ||||
| @@ -91,7 +91,7 @@ defmodule CanneryWeb.CoreComponents do | ||||
|   attr :original_count, :integer, default: nil | ||||
|   attr :cpr, :integer, default: nil | ||||
|   attr :last_used_date, Date, default: nil | ||||
|   attr :show_container, :boolean, default: false | ||||
|   attr :container, Container, default: nil | ||||
|   slot(:inner_block) | ||||
|  | ||||
|   def ammo_group_card(assigns) | ||||
|   | ||||
| @@ -17,7 +17,10 @@ | ||||
|       <%= if @ammo_group.count == 0, do: gettext("Empty"), else: @ammo_group.count %> | ||||
|     </span> | ||||
|  | ||||
|     <span :if={@original_count != @ammo_group.count} class="rounded-lg title text-lg"> | ||||
|     <span | ||||
|       :if={@original_count && @original_count != @ammo_group.count} | ||||
|       class="rounded-lg title text-lg" | ||||
|     > | ||||
|       <%= gettext("Original Count:") %> | ||||
|       <%= @original_count %> | ||||
|     </span> | ||||
| @@ -27,7 +30,7 @@ | ||||
|       <%= @ammo_group.notes %> | ||||
|     </span> | ||||
|  | ||||
|     <span class="rounded-lg title text-lg"> | ||||
|     <span :if={@ammo_group.purchased_on} class="rounded-lg title text-lg"> | ||||
|       <%= gettext("Purchased on:") %> | ||||
|       <.date id={"#{@ammo_group.id}-purchased-on"} date={@ammo_group.purchased_on} /> | ||||
|     </span> | ||||
| @@ -47,17 +50,11 @@ | ||||
|       <%= gettext("$%{amount}", amount: display_currency(@cpr)) %> | ||||
|     </span> | ||||
|  | ||||
|     <span | ||||
|       :if={@show_container && Containers.get_container!(@ammo_group.container_id, @current_user)} | ||||
|       class="rounded-lg title text-lg" | ||||
|     > | ||||
|     <span :if={@container} class="rounded-lg title text-lg"> | ||||
|       <%= gettext("Container:") %> | ||||
|  | ||||
|       <.link | ||||
|         navigate={Routes.container_show_path(Endpoint, :show, @ammo_group.container_id)} | ||||
|         class="link" | ||||
|       > | ||||
|         <%= Containers.get_container!(@ammo_group.container_id, @current_user).name %> | ||||
|       <.link navigate={Routes.container_show_path(Endpoint, :show, @container)} class="link"> | ||||
|         <%= @container.name %> | ||||
|       </.link> | ||||
|     </span> | ||||
|   </div> | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
|     p-8 flex flex-col justify-center items-center cursor-auto" | ||||
|   style="background-color: rgba(0,0,0,0.4);" | ||||
|   phx-remove={hide_modal()} | ||||
|   aria-label={gettext("Close modal")} | ||||
| > | ||||
|   <span class="hidden"></span> | ||||
| </.link> | ||||
| @@ -31,6 +32,7 @@ | ||||
|         text-gray-500 hover:text-gray-800 | ||||
|         transition-all duration-500 ease-in-out" | ||||
|       phx-remove={hide_modal()} | ||||
|       aria-label={gettext("Close modal")} | ||||
|     > | ||||
|       <i class="fa-fw fa-lg fas fa-times"></i> | ||||
|     </.link> | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| <label for={@id || @action} class="inline-flex relative items-center cursor-pointer"> | ||||
| <label for={@id || @action} class="relative inline-flex items-center cursor-pointer"> | ||||
|   <input | ||||
|     id={@id || @action} | ||||
|     type="checkbox" | ||||
| @@ -23,7 +23,7 @@ | ||||
|   </div> | ||||
|   <span | ||||
|     id={"#{@id || @action}-label"} | ||||
|     class="ml-3 text-sm font-medium text-gray-900 dark:text-gray-300" | ||||
|     class="ml-3 text-sm font-medium text-gray-900 dark:text-gray-300 whitespace-nowrap" | ||||
|   > | ||||
|     <%= render_slot(@inner_block) %> | ||||
|   </span> | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| <nav role="navigation" class="mb-8 px-8 py-4 w-full bg-primary-400"> | ||||
| <nav role="navigation" class="mb-8 px-8 py-4 w-full bg-primary-500"> | ||||
|   <div class="flex flex-col sm:flex-row justify-between items-center"> | ||||
|     <div class="mb-4 sm:mb-0 sm:mr-8 flex flex-row justify-start items-center space-x-2"> | ||||
|       <.link | ||||
| @@ -87,6 +87,7 @@ | ||||
|             href={Routes.user_session_path(Endpoint, :delete)} | ||||
|             method="delete" | ||||
|             data-confirm={dgettext("prompts", "Are you sure you want to log out?")} | ||||
|             aria-label={gettext("Log out")} | ||||
|           > | ||||
|             <i class="fas fa-sign-out-alt"></i> | ||||
|           </.link> | ||||
| @@ -101,6 +102,7 @@ | ||||
|           <.link | ||||
|             navigate={Routes.live_dashboard_path(Endpoint, :home)} | ||||
|             class="text-white hover:underline" | ||||
|             aria-label={gettext("Live Dashboard")} | ||||
|           > | ||||
|             <i class="fas fa-gauge"></i> | ||||
|           </.link> | ||||
|   | ||||
| @@ -67,7 +67,7 @@ defmodule CanneryWeb.Components.MoveAmmoGroupComponent do | ||||
|       %{label: gettext("Container"), key: :name}, | ||||
|       %{label: gettext("Type"), key: :type}, | ||||
|       %{label: gettext("Location"), key: :location}, | ||||
|       %{label: nil, key: :actions, sortable: false} | ||||
|       %{label: gettext("Actions"), key: :actions, sortable: false} | ||||
|     ] | ||||
|  | ||||
|     rows = containers |> get_rows_for_containers(assigns, columns) | ||||
|   | ||||
| @@ -3,7 +3,7 @@ defmodule CanneryWeb.Components.ShotGroupTableComponent do | ||||
|   A component that displays a list of shot groups | ||||
|   """ | ||||
|   use CanneryWeb, :live_component | ||||
|   alias Cannery.{Accounts.User, ActivityLog.ShotGroup, Ammo} | ||||
|   alias Cannery.{Accounts.User, ActivityLog.ShotGroup, Ammo, ComparableDate} | ||||
|   alias Ecto.UUID | ||||
|   alias Phoenix.LiveView.{Rendered, Socket} | ||||
|  | ||||
| @@ -41,8 +41,8 @@ defmodule CanneryWeb.Components.ShotGroupTableComponent do | ||||
|       %{label: gettext("Ammo"), key: :name}, | ||||
|       %{label: gettext("Rounds shot"), key: :count}, | ||||
|       %{label: gettext("Notes"), key: :notes}, | ||||
|       %{label: gettext("Date"), key: :date, type: Date}, | ||||
|       %{label: nil, key: :actions, sortable: false} | ||||
|       %{label: gettext("Date"), key: :date, type: ComparableDate}, | ||||
|       %{label: gettext("Actions"), key: :actions, sortable: false} | ||||
|     ] | ||||
|  | ||||
|     ammo_groups = | ||||
|   | ||||
| @@ -135,4 +135,25 @@ defmodule CanneryWeb.Components.TableComponent do | ||||
|       sort_mode | ||||
|     ) | ||||
|   end | ||||
|  | ||||
|   @doc """ | ||||
|   Conditionally composes elements into the columns list, supports maps and | ||||
|   lists. Works tail to front in order for efficiency | ||||
|  | ||||
|       iex> [] | ||||
|       ...> |> maybe_compose_columns(%{label: "Column 3"}, true) | ||||
|       ...> |> maybe_compose_columns(%{label: "Column 2"}, false) | ||||
|       ...> |> maybe_compose_columns(%{label: "Column 1"}) | ||||
|       [%{label: "Column 1"}, %{label: "Column 3"}] | ||||
|  | ||||
|   """ | ||||
|   @spec maybe_compose_columns(list(), element_to_add :: list() | map()) :: list() | ||||
|   @spec maybe_compose_columns(list(), element_to_add :: list() | map(), boolean()) :: list() | ||||
|   def maybe_compose_columns(columns, element_or_elements, add? \\ true) | ||||
|  | ||||
|   def maybe_compose_columns(columns, elements, true) when is_list(elements), | ||||
|     do: Enum.concat(elements, columns) | ||||
|  | ||||
|   def maybe_compose_columns(columns, element, true) when is_map(element), do: [element | columns] | ||||
|   def maybe_compose_columns(columns, _element_or_elements, false), do: columns | ||||
| end | ||||
|   | ||||
| @@ -3,7 +3,7 @@ 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) | ||||
|     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) | ||||
| @@ -28,28 +28,27 @@ defmodule CanneryWeb.ExportController do | ||||
|         }) | ||||
|       end) | ||||
|  | ||||
|     ammo_groups = Ammo.list_ammo_groups(nil, true, current_user) | ||||
|     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) | ||||
|  | ||||
|     ammo_groups = | ||||
|       ammo_groups | ||||
|       |> Enum.map(fn %{id: ammo_group_id} = ammo_group -> | ||||
|         percentage_remaining = ammo_group |> Ammo.get_percentage_remaining(current_user) | ||||
|  | ||||
|         ammo_group | ||||
|         |> Jason.encode!() | ||||
|         |> Jason.decode!() | ||||
|         |> Map.merge(%{ | ||||
|           "used_count" => Map.get(used_counts, ammo_group_id), | ||||
|           "percentage_remaining" => percentage_remaining, | ||||
|           "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) | ||||
|         }) | ||||
|       end) | ||||
|  | ||||
|     shot_groups = ActivityLog.list_shot_groups(current_user) | ||||
|     shot_groups = ActivityLog.list_shot_groups(:all, current_user) | ||||
|  | ||||
|     containers = | ||||
|       Containers.list_containers(current_user) | ||||
|   | ||||
| @@ -26,7 +26,7 @@ defmodule CanneryWeb.AmmoGroupLive.FormComponent do | ||||
|       socket = | ||||
|       socket | ||||
|       |> assign(:ammo_group_create_limit, @ammo_group_create_limit) | ||||
|       |> assign(:ammo_types, Ammo.list_ammo_types(current_user)) | ||||
|       |> assign(:ammo_types, Ammo.list_ammo_types(current_user, :all)) | ||||
|       |> assign_new(:containers, fn -> Containers.list_containers(current_user) end) | ||||
|  | ||||
|     params = | ||||
|   | ||||
| @@ -8,11 +8,16 @@ defmodule CanneryWeb.AmmoGroupLive.Index do | ||||
|  | ||||
|   @impl true | ||||
|   def mount(%{"search" => search}, _session, socket) do | ||||
|     {:ok, socket |> assign(show_used: false, search: search) |> display_ammo_groups()} | ||||
|     socket = | ||||
|       socket | ||||
|       |> assign(class: :all, show_used: false, search: search) | ||||
|       |> display_ammo_groups() | ||||
|  | ||||
|     {:ok, socket} | ||||
|   end | ||||
|  | ||||
|   def mount(_params, _session, socket) do | ||||
|     {:ok, socket |> assign(show_used: false, search: nil) |> display_ammo_groups()} | ||||
|     {:ok, socket |> assign(class: :all, show_used: false, search: nil) |> display_ammo_groups()} | ||||
|   end | ||||
|  | ||||
|   @impl true | ||||
| @@ -119,10 +124,36 @@ defmodule CanneryWeb.AmmoGroupLive.Index do | ||||
|     {:noreply, socket} | ||||
|   end | ||||
|  | ||||
|   def handle_event("change_class", %{"ammo_type" => %{"class" => "rifle"}}, socket) do | ||||
|     {:noreply, socket |> assign(:class, :rifle) |> display_ammo_groups()} | ||||
|   end | ||||
|  | ||||
|   def handle_event("change_class", %{"ammo_type" => %{"class" => "shotgun"}}, socket) do | ||||
|     {:noreply, socket |> assign(:class, :shotgun) |> display_ammo_groups()} | ||||
|   end | ||||
|  | ||||
|   def handle_event("change_class", %{"ammo_type" => %{"class" => "pistol"}}, socket) do | ||||
|     {:noreply, socket |> assign(:class, :pistol) |> display_ammo_groups()} | ||||
|   end | ||||
|  | ||||
|   def handle_event("change_class", %{"ammo_type" => %{"class" => _all}}, socket) do | ||||
|     {:noreply, socket |> assign(:class, :all) |> display_ammo_groups()} | ||||
|   end | ||||
|  | ||||
|   defp display_ammo_groups( | ||||
|          %{assigns: %{search: search, current_user: current_user, show_used: show_used}} = socket | ||||
|          %{ | ||||
|            assigns: %{ | ||||
|              class: class, | ||||
|              search: search, | ||||
|              current_user: current_user, | ||||
|              show_used: show_used | ||||
|            } | ||||
|          } = socket | ||||
|        ) do | ||||
|     ammo_groups = Ammo.list_ammo_groups(search, show_used, current_user) | ||||
|     # get total number of ammo groups 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, class, current_user, show_used) | ||||
|     ammo_types_count = Ammo.get_ammo_types_count!(current_user) | ||||
|     containers_count = Containers.get_containers_count!(current_user) | ||||
|  | ||||
| @@ -130,7 +161,8 @@ defmodule CanneryWeb.AmmoGroupLive.Index do | ||||
|     |> assign( | ||||
|       ammo_groups: ammo_groups, | ||||
|       ammo_types_count: ammo_types_count, | ||||
|       containers_count: containers_count | ||||
|       containers_count: containers_count, | ||||
|       ammo_groups_count: ammo_groups_count | ||||
|     ) | ||||
|   end | ||||
| end | ||||
|   | ||||
| @@ -3,14 +3,6 @@ | ||||
|     <%= gettext("Ammo") %> | ||||
|   </h1> | ||||
|  | ||||
|   <h2 | ||||
|     :if={@ammo_groups |> Enum.empty?() and @search |> is_nil()} | ||||
|     class="title text-xl text-primary-600" | ||||
|   > | ||||
|     <%= gettext("No Ammo") %> | ||||
|     <%= display_emoji("😔") %> | ||||
|   </h2> | ||||
|  | ||||
|   <%= cond do %> | ||||
|     <% @containers_count == 0 -> %> | ||||
|       <div class="flex justify-center items-center"> | ||||
| @@ -32,7 +24,12 @@ | ||||
|           <%= dgettext("actions", "add an ammo type first") %> | ||||
|         </.link> | ||||
|       </div> | ||||
|     <% @ammo_groups |> Enum.empty?() and @search |> is_nil() -> %> | ||||
|     <% @ammo_groups_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"> | ||||
|         <%= dgettext("actions", "Add your first box!") %> | ||||
|       </.link> | ||||
| @@ -40,142 +37,170 @@ | ||||
|       <.link patch={Routes.ammo_group_index_path(Endpoint, :new)} class="btn btn-primary"> | ||||
|         <%= dgettext("actions", "Add Ammo") %> | ||||
|       </.link> | ||||
|   <% end %> | ||||
|  | ||||
|   <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-xl"> | ||||
|     <.form | ||||
|       :let={f} | ||||
|       for={%{}} | ||||
|       as={:search} | ||||
|       phx-change="search" | ||||
|       phx-submit="search" | ||||
|       class="grow self-stretch flex flex-col items-stretch" | ||||
|     > | ||||
|       <%= text_input(f, :search_term, | ||||
|         class: "input input-primary", | ||||
|         value: @search, | ||||
|         phx_debounce: 300, | ||||
|         placeholder: gettext("Search ammo") | ||||
|       ) %> | ||||
|     </.form> | ||||
|       <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_class" | ||||
|           phx-submit="change_class" | ||||
|           class="flex items-center" | ||||
|         > | ||||
|           <%= label(f, :class, gettext("Class"), | ||||
|             class: "title text-primary-600 text-lg text-center" | ||||
|           ) %> | ||||
|  | ||||
|     <.toggle_button action="toggle_show_used" value={@show_used}> | ||||
|       <span class="title text-lg text-primary-600"> | ||||
|         <%= gettext("Show used") %> | ||||
|       </span> | ||||
|     </.toggle_button> | ||||
|   </div> | ||||
|           <%= select( | ||||
|             f, | ||||
|             :class, | ||||
|             [ | ||||
|               {gettext("All"), :all}, | ||||
|               {gettext("Rifle"), :rifle}, | ||||
|               {gettext("Shotgun"), :shotgun}, | ||||
|               {gettext("Pistol"), :pistol} | ||||
|             ], | ||||
|             class: "mx-2 my-1 min-w-md input input-primary", | ||||
|             value: @class | ||||
|           ) %> | ||||
|         </.form> | ||||
|  | ||||
|   <%= if @ammo_groups |> 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} | ||||
|       current_user={@current_user} | ||||
|     > | ||||
|       <: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 %> | ||||
|         </.link> | ||||
|       </:ammo_type> | ||||
|       <:range :let={ammo_group}> | ||||
|         <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} | ||||
|           > | ||||
|             <%= if ammo_group.staged, | ||||
|               do: dgettext("actions", "Unstage"), | ||||
|               else: dgettext("actions", "Stage") %> | ||||
|           </button> | ||||
|         <.form | ||||
|           :let={f} | ||||
|           for={%{}} | ||||
|           as={:search} | ||||
|           phx-change="search" | ||||
|           phx-submit="search" | ||||
|           class="grow flex items-center" | ||||
|         > | ||||
|           <%= text_input(f, :search_term, | ||||
|             class: "grow input input-primary", | ||||
|             value: @search, | ||||
|             role: "search", | ||||
|             phx_debounce: 300, | ||||
|             placeholder: gettext("Search ammo") | ||||
|           ) %> | ||||
|         </.form> | ||||
|  | ||||
|           <.link | ||||
|             patch={Routes.ammo_group_index_path(Endpoint, :add_shot_group, ammo_group)} | ||||
|             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}}> | ||||
|         <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)} | ||||
|             class="mx-2 my-1 link" | ||||
|           > | ||||
|             <%= container_name %> | ||||
|           </.link> | ||||
|         <.toggle_button action="toggle_show_used" value={@show_used}> | ||||
|           <span class="title text-lg text-primary-600"> | ||||
|             <%= gettext("Show used") %> | ||||
|           </span> | ||||
|         </.toggle_button> | ||||
|       </div> | ||||
|  | ||||
|           <.link | ||||
|             patch={Routes.ammo_group_index_path(Endpoint, :move, ammo_group)} | ||||
|             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}> | ||||
|         <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)} | ||||
|             class="text-primary-600 link" | ||||
|             aria-label={ | ||||
|               dgettext("actions", "View ammo group of %{ammo_group_count} bullets", | ||||
|                 ammo_group_count: ammo_group_count | ||||
|               ) | ||||
|             } | ||||
|           > | ||||
|             <i class="fa-fw fa-lg fas fa-eye"></i> | ||||
|           </.link> | ||||
|       <%= if @ammo_groups |> 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} | ||||
|           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 %> | ||||
|             </.link> | ||||
|           </:ammo_type> | ||||
|           <:range :let={ammo_group}> | ||||
|             <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} | ||||
|               > | ||||
|                 <%= if ammo_group.staged, | ||||
|                   do: dgettext("actions", "Unstage"), | ||||
|                   else: dgettext("actions", "Stage") %> | ||||
|               </button> | ||||
|  | ||||
|           <.link | ||||
|             patch={Routes.ammo_group_index_path(Endpoint, :edit, ammo_group)} | ||||
|             class="text-primary-600 link" | ||||
|             aria-label={ | ||||
|               dgettext("actions", "Edit ammo group of %{ammo_group_count} bullets", | ||||
|                 ammo_group_count: ammo_group_count | ||||
|               ) | ||||
|             } | ||||
|           > | ||||
|             <i class="fa-fw fa-lg fas fa-edit"></i> | ||||
|           </.link> | ||||
|               <.link | ||||
|                 patch={Routes.ammo_group_index_path(Endpoint, :add_shot_group, ammo_group)} | ||||
|                 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}}> | ||||
|             <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)} | ||||
|                 class="mx-2 my-1 link" | ||||
|               > | ||||
|                 <%= container_name %> | ||||
|               </.link> | ||||
|  | ||||
|           <.link | ||||
|             patch={Routes.ammo_group_index_path(Endpoint, :clone, ammo_group)} | ||||
|             class="text-primary-600 link" | ||||
|             aria-label={ | ||||
|               dgettext("actions", "Clone ammo group of %{ammo_group_count} bullets", | ||||
|                 ammo_group_count: ammo_group_count | ||||
|               ) | ||||
|             } | ||||
|           > | ||||
|             <i class="fa-fw fa-lg fas fa-copy"></i> | ||||
|           </.link> | ||||
|               <.link | ||||
|                 patch={Routes.ammo_group_index_path(Endpoint, :move, ammo_group)} | ||||
|                 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}> | ||||
|             <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)} | ||||
|                 class="text-primary-600 link" | ||||
|                 aria-label={ | ||||
|                   dgettext("actions", "View ammo group of %{ammo_group_count} bullets", | ||||
|                     ammo_group_count: ammo_group_count | ||||
|                   ) | ||||
|                 } | ||||
|               > | ||||
|                 <i class="fa-fw fa-lg fas fa-eye"></i> | ||||
|               </.link> | ||||
|  | ||||
|           <.link | ||||
|             href="#" | ||||
|             class="text-primary-600 link" | ||||
|             phx-click="delete" | ||||
|             phx-value-id={ammo_group.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 | ||||
|               ) | ||||
|             } | ||||
|           > | ||||
|             <i class="fa-fw fa-lg fas fa-trash"></i> | ||||
|           </.link> | ||||
|         </div> | ||||
|       </:actions> | ||||
|     </.live_component> | ||||
|               <.link | ||||
|                 patch={Routes.ammo_group_index_path(Endpoint, :edit, ammo_group)} | ||||
|                 class="text-primary-600 link" | ||||
|                 aria-label={ | ||||
|                   dgettext("actions", "Edit ammo group of %{ammo_group_count} bullets", | ||||
|                     ammo_group_count: ammo_group_count | ||||
|                   ) | ||||
|                 } | ||||
|               > | ||||
|                 <i class="fa-fw fa-lg fas fa-edit"></i> | ||||
|               </.link> | ||||
|  | ||||
|               <.link | ||||
|                 patch={Routes.ammo_group_index_path(Endpoint, :clone, ammo_group)} | ||||
|                 class="text-primary-600 link" | ||||
|                 aria-label={ | ||||
|                   dgettext("actions", "Clone ammo group of %{ammo_group_count} bullets", | ||||
|                     ammo_group_count: ammo_group_count | ||||
|                   ) | ||||
|                 } | ||||
|               > | ||||
|                 <i class="fa-fw fa-lg fas fa-copy"></i> | ||||
|               </.link> | ||||
|  | ||||
|               <.link | ||||
|                 href="#" | ||||
|                 class="text-primary-600 link" | ||||
|                 phx-click="delete" | ||||
|                 phx-value-id={ammo_group.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 | ||||
|                   ) | ||||
|                 } | ||||
|               > | ||||
|                 <i class="fa-fw fa-lg fas fa-trash"></i> | ||||
|               </.link> | ||||
|             </div> | ||||
|           </:actions> | ||||
|         </.live_component> | ||||
|       <% end %> | ||||
|   <% end %> | ||||
| </div> | ||||
|  | ||||
|   | ||||
| @@ -6,7 +6,7 @@ defmodule CanneryWeb.AmmoGroupLive.Show do | ||||
|   use CanneryWeb, :live_view | ||||
|   alias Cannery.{ActivityLog, ActivityLog.ShotGroup} | ||||
|   alias Cannery.{Ammo, Ammo.AmmoGroup} | ||||
|   alias Cannery.Containers | ||||
|   alias Cannery.{ComparableDate, Containers} | ||||
|   alias CanneryWeb.Endpoint | ||||
|   alias Phoenix.LiveView.Socket | ||||
|  | ||||
| @@ -90,8 +90,8 @@ defmodule CanneryWeb.AmmoGroupLive.Show do | ||||
|     columns = [ | ||||
|       %{label: gettext("Rounds shot"), key: :count}, | ||||
|       %{label: gettext("Notes"), key: :notes}, | ||||
|       %{label: gettext("Date"), key: :date, type: Date}, | ||||
|       %{label: nil, key: :actions, sortable: false} | ||||
|       %{label: gettext("Date"), key: :date, type: ComparableDate}, | ||||
|       %{label: gettext("Actions"), key: :actions, sortable: false} | ||||
|     ] | ||||
|  | ||||
|     shot_groups = ActivityLog.list_shot_groups_for_ammo_group(ammo_group, current_user) | ||||
|   | ||||
| @@ -15,11 +15,24 @@ | ||||
|       :if={@changeset.action && not @changeset.valid?()} | ||||
|       class="invalid-feedback col-span-3 text-center" | ||||
|     > | ||||
|       <%= changeset_errors(@changeset) %> | ||||
|       <%= dgettext("errors", "Oops, something went wrong! Please check the errors below.") %> | ||||
|     </div> | ||||
|  | ||||
|     <%= label(f, :class, gettext("Class"), class: "title text-lg text-primary-600") %> | ||||
|     <%= select( | ||||
|       f, | ||||
|       :class, | ||||
|       [{gettext("Rifle"), :rifle}, {gettext("Shotgun"), :shotgun}, {gettext("Pistol"), :pistol}], | ||||
|       class: "text-center col-span-2 input input-primary", | ||||
|       maxlength: 255 | ||||
|     ) %> | ||||
|     <%= 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, class: "text-center col-span-2 input input-primary") %> | ||||
|     <%= text_input(f, :name, | ||||
|       class: "text-center col-span-2 input input-primary", | ||||
|       maxlength: 255 | ||||
|     ) %> | ||||
|     <%= error_tag(f, :name, "col-span-3 text-center") %> | ||||
|  | ||||
|     <%= label(f, :desc, gettext("Description"), class: "title text-lg text-primary-600") %> | ||||
| @@ -31,77 +44,70 @@ | ||||
|     ) %> | ||||
|     <%= error_tag(f, :desc, "col-span-3 text-center") %> | ||||
|  | ||||
|     <.link | ||||
|       href="https://shootersreference.com/reloadingdata/bullet_abbreviations/" | ||||
|       class="col-span-3 text-center link title text-md text-primary-600" | ||||
|     > | ||||
|       <%= gettext("Example bullet type abbreviations") %> | ||||
|     </.link> | ||||
|     <%= label(f, :bullet_type, gettext("Bullet type"), class: "title text-lg text-primary-600") %> | ||||
|     <%= text_input(f, :bullet_type, | ||||
|       class: "text-center col-span-2 input input-primary", | ||||
|       placeholder: gettext("FMJ") | ||||
|     ) %> | ||||
|     <%= error_tag(f, :bullet_type, "col-span-3 text-center") %> | ||||
|     <h2 class="text-center title text-lg text-primary-600 col-span-3"> | ||||
|       <%= gettext("Dimensions") %> | ||||
|     </h2> | ||||
|  | ||||
|     <%= label(f, :bullet_core, gettext("Bullet core"), class: "title text-lg text-primary-600") %> | ||||
|     <%= text_input(f, :bullet_core, | ||||
|       class: "text-center col-span-2 input input-primary", | ||||
|       placeholder: gettext("Steel") | ||||
|     ) %> | ||||
|     <%= error_tag(f, :bullet_core, "col-span-3 text-center") %> | ||||
|     <%= 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", | ||||
|         maxlength: 255, | ||||
|         placeholder: gettext("5.56x46mm NATO") | ||||
|       ) %> | ||||
|       <%= error_tag(f, :cartridge, "col-span-3 text-center") %> | ||||
|     <% else %> | ||||
|       <%= hidden_input(f, :cartridge, value: nil) %> | ||||
|     <% end %> | ||||
|  | ||||
|     <%= 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", | ||||
|       placeholder: gettext("5.56x46mm NATO") | ||||
|     <%= label( | ||||
|       f, | ||||
|       :caliber, | ||||
|       if(Changeset.get_field(@changeset, :class) == :shotgun, | ||||
|         do: gettext("Gauge"), | ||||
|         else: gettext("Caliber") | ||||
|       ), | ||||
|       class: "title text-lg text-primary-600" | ||||
|     ) %> | ||||
|     <%= error_tag(f, :cartridge, "col-span-3 text-center") %> | ||||
|  | ||||
|     <%= label(f, :caliber, gettext("Caliber"), class: "title text-lg text-primary-600") %> | ||||
|     <%= text_input(f, :caliber, | ||||
|       class: "text-center col-span-2 input input-primary", | ||||
|       maxlength: 255, | ||||
|       placeholder: gettext(".223") | ||||
|     ) %> | ||||
|     <%= error_tag(f, :caliber, "col-span-3 text-center") %> | ||||
|  | ||||
|     <%= label(f, :case_material, gettext("Case material"), class: "title text-lg text-primary-600") %> | ||||
|     <%= text_input(f, :case_material, | ||||
|       class: "text-center col-span-2 input input-primary", | ||||
|       placeholder: gettext("Brass") | ||||
|     ) %> | ||||
|     <%= error_tag(f, :case_material, "col-span-3 text-center") %> | ||||
|     <%= if Changeset.get_field(@changeset, :class) == :shotgun do %> | ||||
|       <%= label(f, :unfired_length, gettext("Unfired shell length"), | ||||
|         class: "title text-lg text-primary-600" | ||||
|       ) %> | ||||
|       <%= text_input(f, :unfired_length, | ||||
|         class: "text-center col-span-2 input input-primary", | ||||
|         maxlength: 255 | ||||
|       ) %> | ||||
|       <%= error_tag(f, :unfired_length, "col-span-3 text-center") %> | ||||
|  | ||||
|     <%= 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", | ||||
|       placeholder: gettext("Bimetal") | ||||
|     ) %> | ||||
|     <%= error_tag(f, :case_material, "col-span-3 text-center") %> | ||||
|       <%= label(f, :brass_height, gettext("Brass height"), class: "title text-lg text-primary-600") %> | ||||
|       <%= text_input(f, :brass_height, | ||||
|         class: "text-center col-span-2 input input-primary", | ||||
|         maxlength: 255 | ||||
|       ) %> | ||||
|       <%= error_tag(f, :brass_height, "col-span-3 text-center") %> | ||||
|  | ||||
|     <%= label(f, :muzzle_velocity, gettext("Muzzle velocity"), | ||||
|       class: "title text-lg text-primary-600" | ||||
|     ) %> | ||||
|     <%= number_input(f, :muzzle_velocity, | ||||
|       step: "1", | ||||
|       class: "text-center col-span-2 input input-primary", | ||||
|       min: 1 | ||||
|     ) %> | ||||
|     <%= error_tag(f, :muzzle_velocity, "col-span-3 text-center") %> | ||||
|       <%= label(f, :chamber_size, gettext("Chamber size"), class: "title text-lg text-primary-600") %> | ||||
|       <%= text_input(f, :chamber_size, | ||||
|         class: "text-center col-span-2 input input-primary", | ||||
|         maxlength: 255 | ||||
|       ) %> | ||||
|       <%= error_tag(f, :chamber_size, "col-span-3 text-center") %> | ||||
|     <% else %> | ||||
|       <%= hidden_input(f, :unfired_length, value: nil) %> | ||||
|       <%= hidden_input(f, :brass_height, value: nil) %> | ||||
|       <%= hidden_input(f, :chamber_size, value: nil) %> | ||||
|     <% end %> | ||||
|  | ||||
|     <%= label(f, :powder_type, gettext("Powder type"), class: "title text-lg text-primary-600") %> | ||||
|     <%= text_input(f, :powder_type, class: "text-center col-span-2 input input-primary") %> | ||||
|     <%= error_tag(f, :powder_type, "col-span-3 text-center") %> | ||||
|  | ||||
|     <%= label(f, :powder_grains_per_charge, gettext("Powder grains per charge"), | ||||
|       class: "title text-lg text-primary-600" | ||||
|     ) %> | ||||
|     <%= number_input(f, :powder_grains_per_charge, | ||||
|       step: "1", | ||||
|       class: "text-center col-span-2 input input-primary", | ||||
|       min: 1 | ||||
|     ) %> | ||||
|     <%= error_tag(f, :powder_grains_per_charge, "col-span-3 text-center") %> | ||||
|     <h2 class="text-center title text-lg text-primary-600 col-span-3"> | ||||
|       <%= gettext("Projectile") %> | ||||
|     </h2> | ||||
|  | ||||
|     <%= label(f, :grains, gettext("Grains"), class: "title text-lg text-primary-600") %> | ||||
|     <%= number_input(f, :grains, | ||||
| @@ -111,16 +117,186 @@ | ||||
|     ) %> | ||||
|     <%= error_tag(f, :grains, "col-span-3 text-center") %> | ||||
|  | ||||
|     <%= label f, :bullet_type, class: "flex title text-lg text-primary-600 space-x-2" do %> | ||||
|       <p><%= gettext("Bullet type") %></p> | ||||
|  | ||||
|       <.link | ||||
|         href="https://shootersreference.com/reloadingdata/bullet_abbreviations/" | ||||
|         class="link" | ||||
|         target="_blank" | ||||
|         rel="noopener noreferrer" | ||||
|       > | ||||
|         <i class="fas fa-md fa-external-link-alt"></i> | ||||
|       </.link> | ||||
|     <% end %> | ||||
|     <%= text_input(f, :bullet_type, | ||||
|       class: "text-center col-span-2 input input-primary", | ||||
|       maxlength: 255, | ||||
|       placeholder: gettext("FMJ") | ||||
|     ) %> | ||||
|     <%= error_tag(f, :bullet_type, "col-span-3 text-center") %> | ||||
|  | ||||
|     <%= label( | ||||
|       f, | ||||
|       :bullet_core, | ||||
|       if(Changeset.get_field(@changeset, :class) == :shotgun, | ||||
|         do: gettext("Slug core"), | ||||
|         else: gettext("Bullet core") | ||||
|       ), | ||||
|       class: "title text-lg text-primary-600" | ||||
|     ) %> | ||||
|     <%= text_input(f, :bullet_core, | ||||
|       class: "text-center col-span-2 input input-primary", | ||||
|       maxlength: 255, | ||||
|       placeholder: gettext("Steel") | ||||
|     ) %> | ||||
|     <%= error_tag(f, :bullet_core, "col-span-3 text-center") %> | ||||
|  | ||||
|     <%= 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", | ||||
|         maxlength: 255, | ||||
|         placeholder: gettext("Bimetal") | ||||
|       ) %> | ||||
|       <%= error_tag(f, :jacket_type, "col-span-3 text-center") %> | ||||
|     <% else %> | ||||
|       <%= hidden_input(f, :jacket_type, value: nil) %> | ||||
|     <% end %> | ||||
|  | ||||
|     <%= label(f, :case_material, gettext("Case material"), class: "title text-lg text-primary-600") %> | ||||
|     <%= text_input(f, :case_material, | ||||
|       class: "text-center col-span-2 input input-primary", | ||||
|       maxlength: 255, | ||||
|       placeholder: gettext("Brass") | ||||
|     ) %> | ||||
|     <%= error_tag(f, :case_material, "col-span-3 text-center") %> | ||||
|  | ||||
|     <%= 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", | ||||
|         maxlength: 255 | ||||
|       ) %> | ||||
|       <%= error_tag(f, :wadding, "col-span-3 text-center") %> | ||||
|  | ||||
|       <%= label(f, :shot_type, gettext("Shot type"), class: "title text-lg text-primary-600") %> | ||||
|       <%= text_input(f, :shot_type, | ||||
|         class: "text-center col-span-2 input input-primary", | ||||
|         maxlength: 255, | ||||
|         placeholder: gettext("Target, bird, buck, etc") | ||||
|       ) %> | ||||
|       <%= error_tag(f, :shot_type, "col-span-3 text-center") %> | ||||
|  | ||||
|       <%= label(f, :shot_material, gettext("Shot material"), | ||||
|         class: "title text-lg text-primary-600" | ||||
|       ) %> | ||||
|       <%= text_input(f, :shot_material, | ||||
|         class: "text-center col-span-2 input input-primary", | ||||
|         maxlength: 255 | ||||
|       ) %> | ||||
|       <%= error_tag(f, :shot_material, "col-span-3 text-center") %> | ||||
|  | ||||
|       <%= label(f, :shot_size, gettext("Shot size"), class: "title text-lg text-primary-600") %> | ||||
|       <%= text_input(f, :shot_size, | ||||
|         class: "text-center col-span-2 input input-primary", | ||||
|         maxlength: 255 | ||||
|       ) %> | ||||
|       <%= error_tag(f, :shot_size, "col-span-3 text-center") %> | ||||
|  | ||||
|       <%= label(f, :load_grains, gettext("Load grains"), class: "title text-lg text-primary-600") %> | ||||
|       <%= number_input(f, :load_grains, | ||||
|         step: "1", | ||||
|         class: "text-center col-span-2 input input-primary", | ||||
|         min: 1 | ||||
|       ) %> | ||||
|       <%= error_tag(f, :load_grains, "col-span-3 text-center") %> | ||||
|  | ||||
|       <%= label(f, :shot_charge_weight, gettext("Shot charge weight"), | ||||
|         class: "title text-lg text-primary-600" | ||||
|       ) %> | ||||
|       <%= text_input(f, :shot_charge_weight, | ||||
|         class: "text-center col-span-2 input input-primary", | ||||
|         maxlength: 255 | ||||
|       ) %> | ||||
|       <%= error_tag(f, :shot_charge_weight, "col-span-3 text-center") %> | ||||
|     <% else %> | ||||
|       <%= hidden_input(f, :wadding, value: nil) %> | ||||
|       <%= hidden_input(f, :shot_type, value: nil) %> | ||||
|       <%= hidden_input(f, :shot_material, value: nil) %> | ||||
|       <%= hidden_input(f, :shot_size, value: nil) %> | ||||
|       <%= hidden_input(f, :load_grains, value: nil) %> | ||||
|       <%= hidden_input(f, :shot_charge_weight, value: nil) %> | ||||
|     <% end %> | ||||
|  | ||||
|     <h2 class="text-center title text-lg text-primary-600 col-span-3"> | ||||
|       <%= gettext("Powder") %> | ||||
|     </h2> | ||||
|  | ||||
|     <%= label(f, :powder_type, gettext("Powder type"), class: "title text-lg text-primary-600") %> | ||||
|     <%= text_input(f, :powder_type, | ||||
|       class: "text-center col-span-2 input input-primary", | ||||
|       maxlength: 255 | ||||
|     ) %> | ||||
|     <%= error_tag(f, :powder_type, "col-span-3 text-center") %> | ||||
|  | ||||
|     <%= 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" | ||||
|       ) %> | ||||
|       <%= number_input(f, :powder_grains_per_charge, | ||||
|         step: "1", | ||||
|         class: "text-center col-span-2 input input-primary", | ||||
|         min: 1 | ||||
|       ) %> | ||||
|       <%= error_tag(f, :powder_grains_per_charge, "col-span-3 text-center") %> | ||||
|     <% else %> | ||||
|       <%= hidden_input(f, :powder_grains_per_charge, value: nil) %> | ||||
|     <% end %> | ||||
|  | ||||
|     <%= label(f, :pressure, gettext("Pressure"), class: "title text-lg text-primary-600") %> | ||||
|     <%= text_input(f, :pressure, | ||||
|       class: "text-center col-span-2 input input-primary", | ||||
|       maxlength: 255, | ||||
|       placeholder: gettext("+P") | ||||
|     ) %> | ||||
|     <%= error_tag(f, :pressure, "col-span-3 text-center") %> | ||||
|  | ||||
|     <%= if Changeset.get_field(@changeset, :class) == :shotgun do %> | ||||
|       <%= label(f, :dram_equivalent, gettext("Dram equivalent"), | ||||
|         class: "title text-lg text-primary-600" | ||||
|       ) %> | ||||
|       <%= text_input(f, :dram_equivalent, | ||||
|         class: "text-center col-span-2 input input-primary", | ||||
|         maxlength: 255 | ||||
|       ) %> | ||||
|       <%= error_tag(f, :dram_equivalent, "col-span-3 text-center") %> | ||||
|     <% else %> | ||||
|       <%= hidden_input(f, :dram_equivalent, value: nil) %> | ||||
|     <% end %> | ||||
|  | ||||
|     <%= if Changeset.get_field(@changeset, :class) in [:rifle, :pistol] do %> | ||||
|       <%= label(f, :muzzle_velocity, gettext("Muzzle velocity"), | ||||
|         class: "title text-lg text-primary-600" | ||||
|       ) %> | ||||
|       <%= number_input(f, :muzzle_velocity, | ||||
|         step: "1", | ||||
|         class: "text-center col-span-2 input input-primary", | ||||
|         min: 1 | ||||
|       ) %> | ||||
|       <%= error_tag(f, :muzzle_velocity, "col-span-3 text-center") %> | ||||
|     <% else %> | ||||
|       <%= hidden_input(f, :muzzle_velocity, value: nil) %> | ||||
|     <% end %> | ||||
|  | ||||
|     <h2 class="text-center title text-lg text-primary-600 col-span-3"> | ||||
|       <%= gettext("Primer") %> | ||||
|     </h2> | ||||
|  | ||||
|     <%= label(f, :primer_type, gettext("Primer type"), class: "title text-lg text-primary-600") %> | ||||
|     <%= text_input(f, :primer_type, | ||||
|       class: "text-center col-span-2 input input-primary", | ||||
|       maxlength: 255, | ||||
|       placeholder: gettext("Boxer") | ||||
|     ) %> | ||||
|     <%= error_tag(f, :primer_type, "col-span-3 text-center") %> | ||||
| @@ -128,10 +304,15 @@ | ||||
|     <%= label(f, :firing_type, gettext("Firing type"), class: "title text-lg text-primary-600") %> | ||||
|     <%= text_input(f, :firing_type, | ||||
|       class: "text-center col-span-2 input input-primary", | ||||
|       maxlength: 255, | ||||
|       placeholder: gettext("Centerfire") | ||||
|     ) %> | ||||
|     <%= error_tag(f, :firing_type, "col-span-3 text-center") %> | ||||
|  | ||||
|     <h2 class="text-center title text-lg text-primary-600 col-span-3"> | ||||
|       <%= gettext("Attributes") %> | ||||
|     </h2> | ||||
|  | ||||
|     <%= label(f, :tracer, gettext("Tracer"), class: "title text-lg text-primary-600") %> | ||||
|     <%= checkbox(f, :tracer, class: "text-center col-span-2 checkbox") %> | ||||
|     <%= error_tag(f, :tracer, "col-span-3 text-center") %> | ||||
| @@ -148,12 +329,22 @@ | ||||
|     <%= checkbox(f, :corrosive, class: "text-center col-span-2 checkbox") %> | ||||
|     <%= error_tag(f, :corrosive, "col-span-3 text-center") %> | ||||
|  | ||||
|     <h2 class="text-center title text-lg text-primary-600 col-span-3"> | ||||
|       <%= gettext("Manufacturer") %> | ||||
|     </h2> | ||||
|  | ||||
|     <%= label(f, :manufacturer, gettext("Manufacturer"), class: "title text-lg text-primary-600") %> | ||||
|     <%= text_input(f, :manufacturer, class: "text-center col-span-2 input input-primary") %> | ||||
|     <%= text_input(f, :manufacturer, | ||||
|       class: "text-center col-span-2 input input-primary", | ||||
|       maxlength: 255 | ||||
|     ) %> | ||||
|     <%= error_tag(f, :manufacturer, "col-span-3 text-center") %> | ||||
|  | ||||
|     <%= label(f, :upc, gettext("UPC"), class: "title text-lg text-primary-600") %> | ||||
|     <%= text_input(f, :upc, class: "text-center col-span-2 input input-primary") %> | ||||
|     <%= text_input(f, :upc, | ||||
|       class: "text-center col-span-2 input input-primary", | ||||
|       maxlength: 255 | ||||
|     ) %> | ||||
|     <%= error_tag(f, :upc, "col-span-3 text-center") %> | ||||
|  | ||||
|     <%= submit(dgettext("actions", "Save"), | ||||
|   | ||||
| @@ -8,11 +8,11 @@ defmodule CanneryWeb.AmmoTypeLive.Index do | ||||
|  | ||||
|   @impl true | ||||
|   def mount(%{"search" => search}, _session, socket) do | ||||
|     {:ok, socket |> assign(show_used: false, search: search) |> list_ammo_types()} | ||||
|     {:ok, socket |> assign(class: :all, show_used: false, search: search) |> list_ammo_types()} | ||||
|   end | ||||
|  | ||||
|   def mount(_params, _session, socket) do | ||||
|     {:ok, socket |> assign(show_used: false, search: nil) |> list_ammo_types()} | ||||
|     {:ok, socket |> assign(class: :all, show_used: false, search: nil) |> list_ammo_types()} | ||||
|   end | ||||
|  | ||||
|   @impl true | ||||
| @@ -86,7 +86,29 @@ defmodule CanneryWeb.AmmoTypeLive.Index do | ||||
|     {:noreply, socket |> push_patch(to: search_path)} | ||||
|   end | ||||
|  | ||||
|   defp list_ammo_types(%{assigns: %{search: search, current_user: current_user}} = socket) do | ||||
|     socket |> assign(ammo_types: Ammo.list_ammo_types(search, current_user)) | ||||
|   def handle_event("change_class", %{"ammo_type" => %{"class" => "rifle"}}, socket) do | ||||
|     {:noreply, socket |> assign(:class, :rifle) |> list_ammo_types()} | ||||
|   end | ||||
|  | ||||
|   def handle_event("change_class", %{"ammo_type" => %{"class" => "shotgun"}}, socket) do | ||||
|     {:noreply, socket |> assign(:class, :shotgun) |> list_ammo_types()} | ||||
|   end | ||||
|  | ||||
|   def handle_event("change_class", %{"ammo_type" => %{"class" => "pistol"}}, socket) do | ||||
|     {:noreply, socket |> assign(:class, :pistol) |> list_ammo_types()} | ||||
|   end | ||||
|  | ||||
|   def handle_event("change_class", %{"ammo_type" => %{"class" => _all}}, socket) do | ||||
|     {:noreply, socket |> assign(:class, :all) |> list_ammo_types()} | ||||
|   end | ||||
|  | ||||
|   defp list_ammo_types( | ||||
|          %{assigns: %{class: class, search: search, current_user: current_user}} = socket | ||||
|        ) do | ||||
|     socket | ||||
|     |> assign( | ||||
|       ammo_types: Ammo.list_ammo_types(search, current_user, class), | ||||
|       ammo_types_count: Ammo.get_ammo_types_count!(current_user) | ||||
|     ) | ||||
|   end | ||||
| end | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
|     <%= gettext("Catalog") %> | ||||
|   </h1> | ||||
|  | ||||
|   <%= if @ammo_types |> Enum.empty?() and @search |> is_nil() do %> | ||||
|   <%= if @ammo_types_count == 0 do %> | ||||
|     <h2 class="title text-xl text-primary-600"> | ||||
|       <%= gettext("No Ammo types") %> | ||||
|       <%= display_emoji("😔") %> | ||||
| @@ -17,18 +17,43 @@ | ||||
|       <%= dgettext("actions", "New Ammo 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-xl"> | ||||
|     <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_class" | ||||
|         phx-submit="change_class" | ||||
|         class="flex items-center" | ||||
|       > | ||||
|         <%= label(f, :class, gettext("Class"), class: "title text-primary-600 text-lg text-center") %> | ||||
|  | ||||
|         <%= select( | ||||
|           f, | ||||
|           :class, | ||||
|           [ | ||||
|             {gettext("All"), :all}, | ||||
|             {gettext("Rifle"), :rifle}, | ||||
|             {gettext("Shotgun"), :shotgun}, | ||||
|             {gettext("Pistol"), :pistol} | ||||
|           ], | ||||
|           class: "mx-2 my-1 min-w-md input input-primary", | ||||
|           value: @class | ||||
|         ) %> | ||||
|       </.form> | ||||
|  | ||||
|       <.form | ||||
|         :let={f} | ||||
|         for={%{}} | ||||
|         as={:search} | ||||
|         phx-change="search" | ||||
|         phx-submit="search" | ||||
|         class="grow self-stretch flex flex-col items-stretch" | ||||
|         class="grow flex items-center" | ||||
|       > | ||||
|         <%= text_input(f, :search_term, | ||||
|           class: "input input-primary", | ||||
|           class: "grow input input-primary", | ||||
|           value: @search, | ||||
|           role: "search", | ||||
|           phx_debounce: 300, | ||||
|           placeholder: gettext("Search catalog") | ||||
|         ) %> | ||||
| @@ -54,6 +79,7 @@ | ||||
|         ammo_types={@ammo_types} | ||||
|         current_user={@current_user} | ||||
|         show_used={@show_used} | ||||
|         class={@class} | ||||
|       > | ||||
|         <:actions :let={ammo_type}> | ||||
|           <div class="px-4 py-2 space-x-4 flex justify-center items-center"> | ||||
|   | ||||
| @@ -4,43 +4,16 @@ defmodule CanneryWeb.AmmoTypeLive.Show do | ||||
|   """ | ||||
|  | ||||
|   use CanneryWeb, :live_view | ||||
|   alias Cannery.{ActivityLog, Ammo, Ammo.AmmoType} | ||||
|   alias Cannery.{ActivityLog, Ammo, Ammo.AmmoType, Containers} | ||||
|   alias CanneryWeb.Endpoint | ||||
|  | ||||
|   @fields_list [ | ||||
|     %{label: gettext("Bullet type:"), key: :bullet_type, type: :string}, | ||||
|     %{label: gettext("Bullet core:"), key: :bullet_core, type: :string}, | ||||
|     %{label: gettext("Cartridge:"), key: :cartridge, type: :string}, | ||||
|     %{label: gettext("Caliber:"), key: :caliber, type: :string}, | ||||
|     %{label: gettext("Case material:"), key: :case_material, type: :string}, | ||||
|     %{label: gettext("Jacket type:"), key: :jacket_type, type: :string}, | ||||
|     %{label: gettext("Muzzle velocity:"), key: :muzzle_velocity, type: :string}, | ||||
|     %{label: gettext("Powder type:"), key: :powder_type, type: :string}, | ||||
|     %{label: gettext("Powder grains per charge:"), key: :powder_grains_per_charge, type: :string}, | ||||
|     %{label: gettext("Grains:"), key: :grains, type: :string}, | ||||
|     %{label: gettext("Pressure:"), key: :pressure, type: :string}, | ||||
|     %{label: gettext("Primer type:"), key: :primer_type, type: :string}, | ||||
|     %{label: gettext("Firing type:"), key: :firing_type, type: :string}, | ||||
|     %{label: gettext("Tracer:"), key: :tracer, type: :boolean}, | ||||
|     %{label: gettext("Incendiary:"), key: :incendiary, type: :boolean}, | ||||
|     %{label: gettext("Blank:"), key: :blank, type: :boolean}, | ||||
|     %{label: gettext("Corrosive:"), key: :corrosive, type: :boolean}, | ||||
|     %{label: gettext("Manufacturer:"), key: :manufacturer, type: :string}, | ||||
|     %{label: gettext("UPC:"), key: :upc, type: :string} | ||||
|   ] | ||||
|   @impl true | ||||
|   def mount(_params, _session, socket), | ||||
|     do: {:ok, socket |> assign(show_used: false, view_table: true)} | ||||
|  | ||||
|   @impl true | ||||
|   def mount(_params, _session, %{assigns: %{live_action: live_action}} = socket), | ||||
|     do: {:ok, socket |> assign(show_used: false, view_table: live_action == :table)} | ||||
|  | ||||
|   @impl true | ||||
|   def handle_params(%{"id" => id}, _params, %{assigns: %{live_action: live_action}} = socket) do | ||||
|     socket = | ||||
|       socket | ||||
|       |> assign(view_table: live_action == :table) | ||||
|       |> display_ammo_type(id) | ||||
|  | ||||
|     {:noreply, socket} | ||||
|   def handle_params(%{"id" => id}, _params, socket) do | ||||
|     {:noreply, socket |> display_ammo_type(id)} | ||||
|   end | ||||
|  | ||||
|   @impl true | ||||
| @@ -61,26 +34,17 @@ defmodule CanneryWeb.AmmoTypeLive.Show do | ||||
|     {:noreply, socket |> assign(:show_used, !show_used) |> display_ammo_type()} | ||||
|   end | ||||
|  | ||||
|   def handle_event( | ||||
|         "toggle_table", | ||||
|         _params, | ||||
|         %{assigns: %{view_table: view_table, ammo_type: ammo_type}} = socket | ||||
|       ) do | ||||
|     new_path = | ||||
|       if view_table, | ||||
|         do: Routes.ammo_type_show_path(Endpoint, :show, ammo_type), | ||||
|         else: Routes.ammo_type_show_path(Endpoint, :table, ammo_type) | ||||
|  | ||||
|     {:noreply, socket |> push_patch(to: new_path)} | ||||
|   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( | ||||
|          %{assigns: %{live_action: live_action, current_user: current_user, show_used: show_used}} = | ||||
|            socket, | ||||
|          %AmmoType{} = ammo_type | ||||
|          %AmmoType{name: ammo_type_name} = ammo_type | ||||
|        ) do | ||||
|     fields_to_display = | ||||
|       @fields_list | ||||
|     custom_fields? = | ||||
|       fields_to_display(ammo_type) | ||||
|       |> Enum.any?(fn %{key: field, type: type} -> | ||||
|         default_value = | ||||
|           case type do | ||||
| @@ -92,28 +56,55 @@ defmodule CanneryWeb.AmmoTypeLive.Show do | ||||
|       end) | ||||
|  | ||||
|     ammo_groups = ammo_type |> Ammo.list_ammo_groups_for_type(current_user, show_used) | ||||
|     original_counts = ammo_groups |> Ammo.get_original_counts(current_user) | ||||
|     cprs = ammo_groups |> Ammo.get_cprs(current_user) | ||||
|     historical_packs_count = ammo_type |> Ammo.get_ammo_groups_count_for_type(current_user, true) | ||||
|     last_used_dates = ammo_groups |> ActivityLog.get_last_used_dates(current_user) | ||||
|  | ||||
|     [ | ||||
|       original_counts, | ||||
|       used_packs_count, | ||||
|       historical_packs_count, | ||||
|       used_rounds, | ||||
|       historical_round_count | ||||
|     ] = | ||||
|       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) | ||||
|         ] | ||||
|       else | ||||
|         [nil, nil, nil, nil, nil] | ||||
|       end | ||||
|  | ||||
|     page_title = | ||||
|       case live_action do | ||||
|         :show -> ammo_type_name | ||||
|         :edit -> gettext("Edit %{ammo_type_name}", ammo_type_name: ammo_type_name) | ||||
|       end | ||||
|  | ||||
|     containers = | ||||
|       ammo_groups | ||||
|       |> Enum.map(fn %{container_id: container_id} -> container_id end) | ||||
|       |> Containers.get_containers(current_user) | ||||
|  | ||||
|     socket | ||||
|     |> assign( | ||||
|       page_title: page_title(live_action, ammo_type), | ||||
|       page_title: page_title, | ||||
|       ammo_type: ammo_type, | ||||
|       ammo_groups: ammo_groups, | ||||
|       original_counts: original_counts, | ||||
|       cprs: cprs, | ||||
|       last_used_dates: last_used_dates, | ||||
|       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), | ||||
|       used_rounds: ammo_type |> ActivityLog.get_used_count_for_ammo_type(current_user), | ||||
|       historical_round_count: ammo_type |> Ammo.get_historical_count_for_ammo_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), | ||||
|       used_packs_count: ammo_type |> Ammo.get_used_ammo_groups_count_for_type(current_user), | ||||
|       used_packs_count: used_packs_count, | ||||
|       historical_packs_count: historical_packs_count, | ||||
|       fields_list: @fields_list, | ||||
|       fields_to_display: fields_to_display | ||||
|       fields_to_display: fields_to_display(ammo_type), | ||||
|       custom_fields?: custom_fields? | ||||
|     ) | ||||
|   end | ||||
|  | ||||
| @@ -125,12 +116,48 @@ defmodule CanneryWeb.AmmoTypeLive.Show do | ||||
|     socket |> display_ammo_type(ammo_type) | ||||
|   end | ||||
|  | ||||
|   defp fields_to_display(%AmmoType{class: class}) do | ||||
|     [ | ||||
|       %{label: gettext("Cartridge:"), key: :cartridge, type: :string}, | ||||
|       %{ | ||||
|         label: if(class == :shotgun, do: gettext("Gauge:"), else: gettext("Caliber:")), | ||||
|         key: :caliber, | ||||
|         type: :string | ||||
|       }, | ||||
|       %{label: gettext("Unfired length:"), key: :unfired_length, type: :string}, | ||||
|       %{label: gettext("Brass height:"), key: :brass_height, type: :string}, | ||||
|       %{label: gettext("Chamber size:"), key: :chamber_size, type: :string}, | ||||
|       %{label: gettext("Grains:"), key: :grains, type: :string}, | ||||
|       %{label: gettext("Bullet type:"), key: :bullet_type, type: :string}, | ||||
|       %{label: gettext("Bullet core:"), key: :bullet_core, type: :string}, | ||||
|       %{label: gettext("Jacket type:"), key: :jacket_type, type: :string}, | ||||
|       %{label: gettext("Case material:"), key: :case_material, type: :string}, | ||||
|       %{label: gettext("Wadding:"), key: :wadding, type: :string}, | ||||
|       %{label: gettext("Shot type:"), key: :shot_type, type: :string}, | ||||
|       %{label: gettext("Shot material:"), key: :shot_material, type: :string}, | ||||
|       %{label: gettext("Shot size:"), key: :shot_size, type: :string}, | ||||
|       %{label: gettext("Load grains:"), key: :load_grains, type: :string}, | ||||
|       %{label: gettext("Shot charge weight:"), key: :shot_charge_weight, type: :string}, | ||||
|       %{label: gettext("Powder type:"), key: :powder_type, type: :string}, | ||||
|       %{ | ||||
|         label: gettext("Powder grains per charge:"), | ||||
|         key: :powder_grains_per_charge, | ||||
|         type: :string | ||||
|       }, | ||||
|       %{label: gettext("Pressure:"), key: :pressure, type: :string}, | ||||
|       %{label: gettext("Dram equivalent:"), key: :dram_equivalent, type: :string}, | ||||
|       %{label: gettext("Muzzle velocity:"), key: :muzzle_velocity, type: :string}, | ||||
|       %{label: gettext("Primer type:"), key: :primer_type, type: :string}, | ||||
|       %{label: gettext("Firing type:"), key: :firing_type, type: :string}, | ||||
|       %{label: gettext("Tracer:"), key: :tracer, type: :boolean}, | ||||
|       %{label: gettext("Incendiary:"), key: :incendiary, type: :boolean}, | ||||
|       %{label: gettext("Blank:"), key: :blank, type: :boolean}, | ||||
|       %{label: gettext("Corrosive:"), key: :corrosive, type: :boolean}, | ||||
|       %{label: gettext("Manufacturer:"), key: :manufacturer, type: :string}, | ||||
|       %{label: gettext("UPC:"), key: :upc, type: :string} | ||||
|     ] | ||||
|   end | ||||
|  | ||||
|   @spec display_currency(float()) :: String.t() | ||||
|   defp display_currency(float), do: :erlang.float_to_binary(float, decimals: 2) | ||||
|  | ||||
|   defp page_title(action, %{name: ammo_type_name}) when action in [:show, :table], | ||||
|     do: ammo_type_name | ||||
|  | ||||
|   defp page_title(:edit, %{name: ammo_type_name}), | ||||
|     do: gettext("Edit %{ammo_type_name}", ammo_type_name: ammo_type_name) | ||||
| end | ||||
|   | ||||
| @@ -42,9 +42,26 @@ | ||||
|  | ||||
|   <hr class="hr" /> | ||||
|  | ||||
|   <%= if @fields_to_display do %> | ||||
|   <%= if @ammo_type.class || @custom_fields? do %> | ||||
|     <div class="grid sm:grid-cols-2 gap-4 text-center justify-center items-center"> | ||||
|       <%= for %{label: label, key: key, type: type} <- @fields_list do %> | ||||
|       <h3 class="title text-lg"> | ||||
|         <%= gettext("Class") %> | ||||
|       </h3> | ||||
|  | ||||
|       <span class="text-primary-600"> | ||||
|         <%= case @ammo_type.class do %> | ||||
|           <% :shotgun -> %> | ||||
|             <%= gettext("Shotgun") %> | ||||
|           <% :rifle -> %> | ||||
|             <%= gettext("Rifle") %> | ||||
|           <% :pistol -> %> | ||||
|             <%= gettext("Pistol") %> | ||||
|           <% _ -> %> | ||||
|             <%= gettext("None specified") %> | ||||
|         <% end %> | ||||
|       </span> | ||||
|  | ||||
|       <%= for %{label: label, key: key, type: type} <- @fields_to_display do %> | ||||
|         <%= if @ammo_type |> Map.get(key) do %> | ||||
|           <h3 class="title text-lg"> | ||||
|             <%= label %> | ||||
| @@ -74,26 +91,24 @@ | ||||
|       <%= @rounds %> | ||||
|     </span> | ||||
|  | ||||
|     <h3 class="title text-lg"> | ||||
|       <%= gettext("Used rounds:") %> | ||||
|     </h3> | ||||
|     <%= if @show_used do %> | ||||
|       <h3 class="title text-lg"> | ||||
|         <%= gettext("Used rounds:") %> | ||||
|       </h3> | ||||
|  | ||||
|     <span class="text-primary-600"> | ||||
|       <%= @used_rounds %> | ||||
|     </span> | ||||
|       <span class="text-primary-600"> | ||||
|         <%= @used_rounds %> | ||||
|       </span> | ||||
|  | ||||
|     <h3 class="title text-lg"> | ||||
|       <%= gettext("Total ever rounds:") %> | ||||
|     </h3> | ||||
|       <h3 class="title text-lg"> | ||||
|         <%= gettext("Total ever rounds:") %> | ||||
|       </h3> | ||||
|  | ||||
|     <span class="text-primary-600"> | ||||
|       <%= @historical_round_count %> | ||||
|     </span> | ||||
|   </div> | ||||
|       <span class="text-primary-600"> | ||||
|         <%= @historical_round_count %> | ||||
|       </span> | ||||
|     <% end %> | ||||
|  | ||||
|   <hr class="hr" /> | ||||
|  | ||||
|   <div class="grid sm:grid-cols-2 gap-4 text-center justify-center items-center"> | ||||
|     <h3 class="title text-lg"> | ||||
|       <%= gettext("Packs:") %> | ||||
|     </h3> | ||||
| @@ -102,26 +117,24 @@ | ||||
|       <%= @packs_count %> | ||||
|     </span> | ||||
|  | ||||
|     <h3 class="title text-lg"> | ||||
|       <%= gettext("Used packs:") %> | ||||
|     </h3> | ||||
|     <%= if @show_used do %> | ||||
|       <h3 class="title text-lg"> | ||||
|         <%= gettext("Used packs:") %> | ||||
|       </h3> | ||||
|  | ||||
|     <span class="text-primary-600"> | ||||
|       <%= @used_packs_count %> | ||||
|     </span> | ||||
|       <span class="text-primary-600"> | ||||
|         <%= @used_packs_count %> | ||||
|       </span> | ||||
|  | ||||
|     <h3 class="title text-lg"> | ||||
|       <%= gettext("Total ever packs:") %> | ||||
|     </h3> | ||||
|       <h3 class="title text-lg"> | ||||
|         <%= gettext("Total ever packs:") %> | ||||
|       </h3> | ||||
|  | ||||
|     <span class="text-primary-600"> | ||||
|       <%= @historical_packs_count %> | ||||
|     </span> | ||||
|   </div> | ||||
|       <span class="text-primary-600"> | ||||
|         <%= @historical_packs_count %> | ||||
|       </span> | ||||
|     <% end %> | ||||
|  | ||||
|   <hr class="hr" /> | ||||
|  | ||||
|   <div class="grid sm:grid-cols-2 gap-4 text-center justify-center items-center"> | ||||
|     <h3 class="title text-lg"> | ||||
|       <%= gettext("Added on:") %> | ||||
|     </h3> | ||||
| @@ -174,6 +187,7 @@ | ||||
|           id="ammo-type-show-table" | ||||
|           ammo_groups={@ammo_groups} | ||||
|           current_user={@current_user} | ||||
|           show_used={@show_used} | ||||
|         > | ||||
|           <:container :let={{_ammo_group, %{name: container_name} = container}}> | ||||
|             <.link | ||||
| @@ -183,17 +197,32 @@ | ||||
|               <%= container_name %> | ||||
|             </.link> | ||||
|           </:container> | ||||
|           <:actions :let={%{count: ammo_group_count} = ammo_group}> | ||||
|             <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)} | ||||
|                 class="text-primary-600 link" | ||||
|                 aria-label={ | ||||
|                   dgettext("actions", "View ammo group of %{ammo_group_count} bullets", | ||||
|                     ammo_group_count: ammo_group_count | ||||
|                   ) | ||||
|                 } | ||||
|               > | ||||
|                 <i class="fa-fw fa-lg fas fa-eye"></i> | ||||
|               </.link> | ||||
|             </div> | ||||
|           </:actions> | ||||
|         </.live_component> | ||||
|       <% else %> | ||||
|         <div class="flex flex-wrap justify-center items-stretch"> | ||||
|           <.ammo_group_card | ||||
|             :for={%{id: ammo_group_id} = ammo_group <- @ammo_groups} | ||||
|             :for={%{id: ammo_group_id, container_id: container_id} = ammo_group <- @ammo_groups} | ||||
|             ammo_group={ammo_group} | ||||
|             original_count={Map.fetch!(@original_counts, ammo_group_id)} | ||||
|             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)} | ||||
|             current_user={@current_user} | ||||
|             show_container={true} | ||||
|             container={Map.fetch!(@containers, container_id)} | ||||
|           /> | ||||
|         </div> | ||||
|       <% end %> | ||||
|   | ||||
| @@ -21,7 +21,8 @@ | ||||
|     <%= label(f, :name, gettext("Name"), class: "title text-lg text-primary-600") %> | ||||
|     <%= text_input(f, :name, | ||||
|       class: "input input-primary col-span-2", | ||||
|       placeholder: gettext("My cool ammo can") | ||||
|       placeholder: gettext("My cool ammo can"), | ||||
|       maxlength: 255 | ||||
|     ) %> | ||||
|     <%= error_tag(f, :name, "col-span-3 text-center") %> | ||||
|  | ||||
| @@ -38,7 +39,8 @@ | ||||
|     <%= label(f, :type, gettext("Type"), class: "title text-lg text-primary-600") %> | ||||
|     <%= text_input(f, :type, | ||||
|       class: "input input-primary col-span-2", | ||||
|       placeholder: gettext("Magazine, Clip, Ammo Box, etc") | ||||
|       placeholder: gettext("Magazine, Clip, Ammo Box, etc"), | ||||
|       maxlength: 255 | ||||
|     ) %> | ||||
|     <%= error_tag(f, :type, "col-span-3 text-center") %> | ||||
|  | ||||
|   | ||||
| @@ -17,18 +17,19 @@ | ||||
|       <%= dgettext("actions", "New Container") %> | ||||
|     </.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-xl"> | ||||
|     <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={:search} | ||||
|         phx-change="search" | ||||
|         phx-submit="search" | ||||
|         class="grow self-stretch flex flex-col items-stretch" | ||||
|         class="grow flex items-center" | ||||
|       > | ||||
|         <%= text_input(f, :search_term, | ||||
|           class: "input input-primary", | ||||
|           class: "grow input input-primary", | ||||
|           value: @search, | ||||
|           role: "search", | ||||
|           phx_debounce: 300, | ||||
|           placeholder: gettext("Search containers") | ||||
|         ) %> | ||||
| @@ -40,80 +41,22 @@ | ||||
|         </span> | ||||
|       </.toggle_button> | ||||
|     </div> | ||||
|   <% end %> | ||||
|  | ||||
|   <%= if @containers |> Enum.empty?() do %> | ||||
|     <h2 class="title text-xl text-primary-600"> | ||||
|       <%= gettext("No containers") %> | ||||
|       <%= display_emoji("😔") %> | ||||
|     </h2> | ||||
|   <% else %> | ||||
|     <%= if @view_table do %> | ||||
|       <.live_component | ||||
|         module={CanneryWeb.Components.ContainerTableComponent} | ||||
|         id="containers_index_table" | ||||
|         action={@live_action} | ||||
|         containers={@containers} | ||||
|         current_user={@current_user} | ||||
|       > | ||||
|         <:tag_actions :let={container}> | ||||
|           <div class="mx-4 my-2"> | ||||
|             <.link | ||||
|               patch={Routes.container_index_path(Endpoint, :edit_tags, container)} | ||||
|               class="text-primary-600 link" | ||||
|               aria-label={ | ||||
|                 dgettext("actions", "Tag %{container_name}", container_name: container.name) | ||||
|               } | ||||
|             > | ||||
|               <i class="fa-fw fa-lg fas fa-tags"></i> | ||||
|             </.link> | ||||
|           </div> | ||||
|         </:tag_actions> | ||||
|         <:actions :let={container}> | ||||
|           <.link | ||||
|             patch={Routes.container_index_path(Endpoint, :edit, container)} | ||||
|             class="text-primary-600 link" | ||||
|             aria-label={ | ||||
|               dgettext("actions", "Edit %{container_name}", container_name: container.name) | ||||
|             } | ||||
|           > | ||||
|             <i class="fa-fw fa-lg fas fa-edit"></i> | ||||
|           </.link> | ||||
|  | ||||
|           <.link | ||||
|             patch={Routes.container_index_path(Endpoint, :clone, container)} | ||||
|             class="text-primary-600 link" | ||||
|             aria-label={ | ||||
|               dgettext("actions", "Clone %{container_name}", container_name: container.name) | ||||
|             } | ||||
|           > | ||||
|             <i class="fa-fw fa-lg fas fa-copy"></i> | ||||
|           </.link> | ||||
|  | ||||
|           <.link | ||||
|             href="#" | ||||
|             class="text-primary-600 link" | ||||
|             phx-click="delete" | ||||
|             phx-value-id={container.id} | ||||
|             data-confirm={ | ||||
|               dgettext("prompts", "Are you sure you want to delete %{name}?", name: container.name) | ||||
|             } | ||||
|             aria-label={ | ||||
|               dgettext("actions", "Delete %{container_name}", container_name: container.name) | ||||
|             } | ||||
|           > | ||||
|             <i class="fa-fw fa-lg fas fa-trash"></i> | ||||
|           </.link> | ||||
|         </:actions> | ||||
|       </.live_component> | ||||
|     <%= if @containers |> Enum.empty?() do %> | ||||
|       <h2 class="title text-xl text-primary-600"> | ||||
|         <%= gettext("No containers") %> | ||||
|         <%= display_emoji("😔") %> | ||||
|       </h2> | ||||
|     <% else %> | ||||
|       <div class="w-full flex flex-row flex-wrap justify-center items-stretch"> | ||||
|         <.container_card | ||||
|           :for={container <- @containers} | ||||
|           container={container} | ||||
|       <%= if @view_table do %> | ||||
|         <.live_component | ||||
|           module={CanneryWeb.Components.ContainerTableComponent} | ||||
|           id="containers_index_table" | ||||
|           action={@live_action} | ||||
|           containers={@containers} | ||||
|           current_user={@current_user} | ||||
|         > | ||||
|           <:tag_actions> | ||||
|           <:tag_actions :let={container}> | ||||
|             <div class="mx-4 my-2"> | ||||
|               <.link | ||||
|                 patch={Routes.container_index_path(Endpoint, :edit_tags, container)} | ||||
| @@ -126,42 +69,104 @@ | ||||
|               </.link> | ||||
|             </div> | ||||
|           </:tag_actions> | ||||
|           <.link | ||||
|             patch={Routes.container_index_path(Endpoint, :edit, container)} | ||||
|             class="text-primary-600 link" | ||||
|             aria-label={ | ||||
|               dgettext("actions", "Edit %{container_name}", container_name: container.name) | ||||
|             } | ||||
|           > | ||||
|             <i class="fa-fw fa-lg fas fa-edit"></i> | ||||
|           </.link> | ||||
|           <:actions :let={container}> | ||||
|             <.link | ||||
|               patch={Routes.container_index_path(Endpoint, :edit, container)} | ||||
|               class="text-primary-600 link" | ||||
|               aria-label={ | ||||
|                 dgettext("actions", "Edit %{container_name}", container_name: container.name) | ||||
|               } | ||||
|             > | ||||
|               <i class="fa-fw fa-lg fas fa-edit"></i> | ||||
|             </.link> | ||||
|  | ||||
|           <.link | ||||
|             patch={Routes.container_index_path(Endpoint, :clone, container)} | ||||
|             class="text-primary-600 link" | ||||
|             aria-label={ | ||||
|               dgettext("actions", "Clone %{container_name}", container_name: container.name) | ||||
|             } | ||||
|           > | ||||
|             <i class="fa-fw fa-lg fas fa-copy"></i> | ||||
|           </.link> | ||||
|             <.link | ||||
|               patch={Routes.container_index_path(Endpoint, :clone, container)} | ||||
|               class="text-primary-600 link" | ||||
|               aria-label={ | ||||
|                 dgettext("actions", "Clone %{container_name}", container_name: container.name) | ||||
|               } | ||||
|             > | ||||
|               <i class="fa-fw fa-lg fas fa-copy"></i> | ||||
|             </.link> | ||||
|  | ||||
|           <.link | ||||
|             href="#" | ||||
|             class="text-primary-600 link" | ||||
|             phx-click="delete" | ||||
|             phx-value-id={container.id} | ||||
|             data-confirm={ | ||||
|               dgettext("prompts", "Are you sure you want to delete %{name}?", name: container.name) | ||||
|             } | ||||
|             aria-label={ | ||||
|               dgettext("actions", "Delete %{container_name}", container_name: container.name) | ||||
|             } | ||||
|             <.link | ||||
|               href="#" | ||||
|               class="text-primary-600 link" | ||||
|               phx-click="delete" | ||||
|               phx-value-id={container.id} | ||||
|               data-confirm={ | ||||
|                 dgettext("prompts", "Are you sure you want to delete %{name}?", | ||||
|                   name: container.name | ||||
|                 ) | ||||
|               } | ||||
|               aria-label={ | ||||
|                 dgettext("actions", "Delete %{container_name}", container_name: container.name) | ||||
|               } | ||||
|             > | ||||
|               <i class="fa-fw fa-lg fas fa-trash"></i> | ||||
|             </.link> | ||||
|           </:actions> | ||||
|         </.live_component> | ||||
|       <% else %> | ||||
|         <div class="w-full flex flex-row flex-wrap justify-center items-stretch"> | ||||
|           <.container_card | ||||
|             :for={container <- @containers} | ||||
|             container={container} | ||||
|             current_user={@current_user} | ||||
|           > | ||||
|             <i class="fa-fw fa-lg fas fa-trash"></i> | ||||
|           </.link> | ||||
|         </.container_card> | ||||
|       </div> | ||||
|             <:tag_actions> | ||||
|               <div class="mx-4 my-2"> | ||||
|                 <.link | ||||
|                   patch={Routes.container_index_path(Endpoint, :edit_tags, container)} | ||||
|                   class="text-primary-600 link" | ||||
|                   aria-label={ | ||||
|                     dgettext("actions", "Tag %{container_name}", container_name: container.name) | ||||
|                   } | ||||
|                 > | ||||
|                   <i class="fa-fw fa-lg fas fa-tags"></i> | ||||
|                 </.link> | ||||
|               </div> | ||||
|             </:tag_actions> | ||||
|             <.link | ||||
|               patch={Routes.container_index_path(Endpoint, :edit, container)} | ||||
|               class="text-primary-600 link" | ||||
|               aria-label={ | ||||
|                 dgettext("actions", "Edit %{container_name}", container_name: container.name) | ||||
|               } | ||||
|             > | ||||
|               <i class="fa-fw fa-lg fas fa-edit"></i> | ||||
|             </.link> | ||||
|  | ||||
|             <.link | ||||
|               patch={Routes.container_index_path(Endpoint, :clone, container)} | ||||
|               class="text-primary-600 link" | ||||
|               aria-label={ | ||||
|                 dgettext("actions", "Clone %{container_name}", container_name: container.name) | ||||
|               } | ||||
|             > | ||||
|               <i class="fa-fw fa-lg fas fa-copy"></i> | ||||
|             </.link> | ||||
|  | ||||
|             <.link | ||||
|               href="#" | ||||
|               class="text-primary-600 link" | ||||
|               phx-click="delete" | ||||
|               phx-value-id={container.id} | ||||
|               data-confirm={ | ||||
|                 dgettext("prompts", "Are you sure you want to delete %{name}?", | ||||
|                   name: container.name | ||||
|                 ) | ||||
|               } | ||||
|               aria-label={ | ||||
|                 dgettext("actions", "Delete %{container_name}", container_name: container.name) | ||||
|               } | ||||
|             > | ||||
|               <i class="fa-fw fa-lg fas fa-trash"></i> | ||||
|             </.link> | ||||
|           </.container_card> | ||||
|         </div> | ||||
|       <% end %> | ||||
|     <% end %> | ||||
|   <% end %> | ||||
| </div> | ||||
|   | ||||
| @@ -11,13 +11,13 @@ defmodule CanneryWeb.ContainerLive.Show do | ||||
|  | ||||
|   @impl true | ||||
|   def mount(_params, _session, socket), | ||||
|     do: {:ok, socket |> assign(show_used: false, 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} | ||||
| @@ -82,29 +82,41 @@ defmodule CanneryWeb.ContainerLive.Show do | ||||
|     {:noreply, socket} | ||||
|   end | ||||
|  | ||||
|   def handle_event("toggle_show_used", _params, %{assigns: %{show_used: show_used}} = socket) do | ||||
|     {:noreply, socket |> assign(:show_used, !show_used) |> render_container()} | ||||
|   end | ||||
|  | ||||
|   def handle_event("toggle_table", _params, %{assigns: %{view_table: view_table}} = socket) do | ||||
|     {:noreply, socket |> assign(:view_table, !view_table) |> render_container()} | ||||
|   end | ||||
|  | ||||
|   def handle_event("change_class", %{"ammo_type" => %{"class" => "rifle"}}, socket) do | ||||
|     {:noreply, socket |> assign(:class, :rifle) |> render_container()} | ||||
|   end | ||||
|  | ||||
|   def handle_event("change_class", %{"ammo_type" => %{"class" => "shotgun"}}, socket) do | ||||
|     {:noreply, socket |> assign(:class, :shotgun) |> render_container()} | ||||
|   end | ||||
|  | ||||
|   def handle_event("change_class", %{"ammo_type" => %{"class" => "pistol"}}, socket) do | ||||
|     {:noreply, socket |> assign(:class, :pistol) |> render_container()} | ||||
|   end | ||||
|  | ||||
|   def handle_event("change_class", %{"ammo_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: %{live_action: live_action, show_used: show_used}} = 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, current_user, show_used) | ||||
|     ammo_groups = Ammo.list_ammo_groups_for_container(container, class, 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) | ||||
|  | ||||
|     page_title = | ||||
|       case live_action do | ||||
|         action when action in [:show, :table] -> container_name | ||||
|         :show -> container_name | ||||
|         :edit -> gettext("Edit %{name}", name: container_name) | ||||
|         :edit_tags -> gettext("Edit %{name} tags", name: container_name) | ||||
|       end | ||||
| @@ -113,6 +125,7 @@ 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, | ||||
|       original_counts: original_counts, | ||||
|       cprs: cprs, | ||||
|   | ||||
| @@ -18,22 +18,15 @@ | ||||
|     <%= @container.location %> | ||||
|   </span> | ||||
|  | ||||
|   <%= unless @ammo_groups |> Enum.empty?() do %> | ||||
|     <span class="rounded-lg title text-lg"> | ||||
|       <%= gettext("Packs:") %> | ||||
|       <%= @ammo_groups |> Enum.reject(fn %{count: count} -> count in [0, nil] end) |> Enum.count() %> | ||||
|     </span> | ||||
|   <span class="rounded-lg title text-lg"> | ||||
|     <%= gettext("Packs:") %> | ||||
|     <%= @ammo_groups_count %> | ||||
|   </span> | ||||
|  | ||||
|     <span :if={@show_used} class="rounded-lg title text-lg"> | ||||
|       <%= gettext("Total packs:") %> | ||||
|       <%= Enum.count(@ammo_groups) %> | ||||
|     </span> | ||||
|  | ||||
|     <span class="rounded-lg title text-lg"> | ||||
|       <%= gettext("Rounds:") %> | ||||
|       <%= @round_count %> | ||||
|     </span> | ||||
|   <% end %> | ||||
|   <span class="rounded-lg title text-lg"> | ||||
|     <%= gettext("Rounds:") %> | ||||
|     <%= @round_count %> | ||||
|   </span> | ||||
|  | ||||
|   <div class="flex space-x-4 justify-center items-center text-primary-600"> | ||||
|     <.link | ||||
| @@ -93,11 +86,29 @@ | ||||
|   <hr class="mb-4 hr" /> | ||||
|  | ||||
|   <div class="flex justify-center items-center space-x-4"> | ||||
|     <.toggle_button action="toggle_show_used" value={@show_used}> | ||||
|       <span class="title text-lg text-primary-600"> | ||||
|         <%= gettext("Show used") %> | ||||
|       </span> | ||||
|     </.toggle_button> | ||||
|     <.form | ||||
|       :let={f} | ||||
|       for={%{}} | ||||
|       as={:ammo_type} | ||||
|       phx-change="change_class" | ||||
|       phx-submit="change_class" | ||||
|       class="flex items-center" | ||||
|     > | ||||
|       <%= label(f, :class, gettext("Class"), class: "title text-primary-600 text-lg text-center") %> | ||||
|  | ||||
|       <%= select( | ||||
|         f, | ||||
|         :class, | ||||
|         [ | ||||
|           {gettext("All"), :all}, | ||||
|           {gettext("Rifle"), :rifle}, | ||||
|           {gettext("Shotgun"), :shotgun}, | ||||
|           {gettext("Pistol"), :pistol} | ||||
|         ], | ||||
|         class: "mx-2 my-1 min-w-md input input-primary", | ||||
|         value: @class | ||||
|       ) %> | ||||
|     </.form> | ||||
|  | ||||
|     <.toggle_button action="toggle_table" value={@view_table}> | ||||
|       <span class="title text-lg text-primary-600"> | ||||
| @@ -118,6 +129,7 @@ | ||||
|           id="ammo-type-show-table" | ||||
|           ammo_groups={@ammo_groups} | ||||
|           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"> | ||||
|   | ||||
| @@ -18,7 +18,10 @@ | ||||
|       <%= changeset_errors(@changeset) %> | ||||
|     </div> | ||||
|  | ||||
|     <%= label(f, :name, gettext("Name"), class: "title text-lg text-primary-600") %> | ||||
|     <%= label(f, :name, gettext("Name"), | ||||
|       class: "title text-lg text-primary-600", | ||||
|       maxlength: 255 | ||||
|     ) %> | ||||
|     <%= text_input(f, :name, class: "input input-primary col-span-2") %> | ||||
|     <%= error_tag(f, :name, "col-span-3") %> | ||||
|  | ||||
|   | ||||
| @@ -6,21 +6,11 @@ defmodule CanneryWeb.InviteLive.Index do | ||||
|   use CanneryWeb, :live_view | ||||
|   alias Cannery.Accounts | ||||
|   alias Cannery.Accounts.{Invite, Invites} | ||||
|   alias CanneryWeb.HomeLive | ||||
|   alias Phoenix.LiveView.JS | ||||
|  | ||||
|   @impl true | ||||
|   def mount(_params, _session, %{assigns: %{current_user: current_user}} = socket) do | ||||
|     socket = | ||||
|       if current_user |> Map.get(:role) == :admin do | ||||
|         socket |> display_invites() | ||||
|       else | ||||
|         prompt = dgettext("errors", "You are not authorized to view this page") | ||||
|         return_to = Routes.live_path(Endpoint, HomeLive) | ||||
|         socket |> put_flash(:error, prompt) |> push_redirect(to: return_to) | ||||
|       end | ||||
|  | ||||
|     {:ok, socket} | ||||
|   def mount(_params, _session, socket) do | ||||
|     {:ok, socket |> display_invites()} | ||||
|   end | ||||
|  | ||||
|   @impl true | ||||
|   | ||||
| @@ -31,6 +31,7 @@ | ||||
|     <%= textarea(f, :notes, | ||||
|       id: "shot-group-form-notes", | ||||
|       class: "input input-primary col-span-2", | ||||
|       maxlength: 255, | ||||
|       placeholder: gettext("Really great weather"), | ||||
|       phx_hook: "MaintainAttrs", | ||||
|       phx_update: "ignore" | ||||
|   | ||||
| @@ -10,11 +10,11 @@ defmodule CanneryWeb.RangeLive.Index do | ||||
|  | ||||
|   @impl true | ||||
|   def mount(%{"search" => search}, _session, socket) do | ||||
|     {:ok, socket |> assign(search: search) |> display_shot_groups()} | ||||
|     {:ok, socket |> assign(class: :all, search: search) |> display_shot_groups()} | ||||
|   end | ||||
|  | ||||
|   def mount(_params, _session, socket) do | ||||
|     {:ok, socket |> assign(search: nil) |> display_shot_groups()} | ||||
|     {:ok, socket |> assign(class: :all, search: nil) |> display_shot_groups()} | ||||
|   end | ||||
|  | ||||
|   @impl true | ||||
| @@ -102,9 +102,27 @@ defmodule CanneryWeb.RangeLive.Index do | ||||
|     {:noreply, socket |> push_patch(to: Routes.range_index_path(Endpoint, :search, search_term))} | ||||
|   end | ||||
|  | ||||
|   def handle_event("change_class", %{"ammo_type" => %{"class" => "rifle"}}, socket) do | ||||
|     {:noreply, socket |> assign(:class, :rifle) |> display_shot_groups()} | ||||
|   end | ||||
|  | ||||
|   def handle_event("change_class", %{"ammo_type" => %{"class" => "shotgun"}}, socket) do | ||||
|     {:noreply, socket |> assign(:class, :shotgun) |> display_shot_groups()} | ||||
|   end | ||||
|  | ||||
|   def handle_event("change_class", %{"ammo_type" => %{"class" => "pistol"}}, socket) do | ||||
|     {:noreply, socket |> assign(:class, :pistol) |> display_shot_groups()} | ||||
|   end | ||||
|  | ||||
|   def handle_event("change_class", %{"ammo_type" => %{"class" => _all}}, socket) do | ||||
|     {:noreply, socket |> assign(:class, :all) |> display_shot_groups()} | ||||
|   end | ||||
|  | ||||
|   @spec display_shot_groups(Socket.t()) :: Socket.t() | ||||
|   defp display_shot_groups(%{assigns: %{search: search, current_user: current_user}} = socket) do | ||||
|     shot_groups = ActivityLog.list_shot_groups(search, current_user) | ||||
|   defp display_shot_groups( | ||||
|          %{assigns: %{class: class, search: search, current_user: current_user}} = socket | ||||
|        ) do | ||||
|     shot_groups = ActivityLog.list_shot_groups(search, class, 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) | ||||
|   | ||||
| @@ -74,18 +74,43 @@ | ||||
|       <%= dgettext("errors", "Your browser does not support the canvas element.") %> | ||||
|     </canvas> | ||||
|  | ||||
|     <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-xl"> | ||||
|     <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_class" | ||||
|         phx-submit="change_class" | ||||
|         class="flex items-center" | ||||
|       > | ||||
|         <%= label(f, :class, gettext("Class"), class: "title text-primary-600 text-lg text-center") %> | ||||
|  | ||||
|         <%= select( | ||||
|           f, | ||||
|           :class, | ||||
|           [ | ||||
|             {gettext("All"), :all}, | ||||
|             {gettext("Rifle"), :rifle}, | ||||
|             {gettext("Shotgun"), :shotgun}, | ||||
|             {gettext("Pistol"), :pistol} | ||||
|           ], | ||||
|           class: "mx-2 my-1 min-w-md input input-primary", | ||||
|           value: @class | ||||
|         ) %> | ||||
|       </.form> | ||||
|  | ||||
|       <.form | ||||
|         :let={f} | ||||
|         for={%{}} | ||||
|         as={:search} | ||||
|         phx-change="search" | ||||
|         phx-submit="search" | ||||
|         class="grow self-stretch flex flex-col items-stretch" | ||||
|         class="grow flex items-center" | ||||
|       > | ||||
|         <%= text_input(f, :search_term, | ||||
|           class: "input input-primary", | ||||
|           class: "grow input input-primary", | ||||
|           value: @search, | ||||
|           role: "search", | ||||
|           phx_debounce: 300, | ||||
|           placeholder: gettext("Search shot records") | ||||
|         ) %> | ||||
|   | ||||
| @@ -19,7 +19,7 @@ | ||||
|     </div> | ||||
|  | ||||
|     <%= label(f, :name, gettext("Name"), class: "title text-lg text-primary-600") %> | ||||
|     <%= text_input(f, :name, class: "input input-primary col-span-2") %> | ||||
|     <%= text_input(f, :name, class: "input input-primary col-span-2", maxlength: 255) %> | ||||
|     <%= error_tag(f, :name, "col-span-3") %> | ||||
|  | ||||
|     <%= label(f, :bg_color, gettext("Background color"), class: "title text-lg text-primary-600") %> | ||||
|   | ||||
| @@ -18,56 +18,57 @@ | ||||
|     <.link patch={Routes.tag_index_path(Endpoint, :new)} class="btn btn-primary"> | ||||
|       <%= dgettext("actions", "New Tag") %> | ||||
|     </.link> | ||||
|   <% end %> | ||||
|  | ||||
|   <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-xl"> | ||||
|     <.form | ||||
|       :let={f} | ||||
|       for={%{}} | ||||
|       as={:search} | ||||
|       phx-change="search" | ||||
|       phx-submit="search" | ||||
|       class="grow self-stretch flex flex-col items-stretch" | ||||
|     > | ||||
|       <%= text_input(f, :search_term, | ||||
|         class: "input input-primary", | ||||
|         value: @search, | ||||
|         phx_debounce: 300, | ||||
|         placeholder: gettext("Search tags") | ||||
|       ) %> | ||||
|     </.form> | ||||
|   </div> | ||||
|  | ||||
|   <%= if @tags |> Enum.empty?() do %> | ||||
|     <h2 class="title text-xl text-primary-600"> | ||||
|       <%= gettext("No tags") %> | ||||
|       <%= display_emoji("😔") %> | ||||
|     </h2> | ||||
|   <% else %> | ||||
|     <div class="flex flex-row flex-wrap justify-center items-stretch"> | ||||
|       <.tag_card :for={tag <- @tags} tag={tag}> | ||||
|         <.link | ||||
|           patch={Routes.tag_index_path(Endpoint, :edit, tag)} | ||||
|           class="text-primary-600 link" | ||||
|           aria-label={dgettext("actions", "Edit %{tag_name}", tag_name: tag.name)} | ||||
|         > | ||||
|           <i class="fa-fw fa-lg fas fa-edit"></i> | ||||
|         </.link> | ||||
|  | ||||
|         <.link | ||||
|           href="#" | ||||
|           class="text-primary-600 link" | ||||
|           phx-click="delete" | ||||
|           phx-value-id={tag.id} | ||||
|           data-confirm={ | ||||
|             dgettext("prompts", "Are you sure you want to delete %{name}?", name: tag.name) | ||||
|           } | ||||
|           aria-label={dgettext("actions", "Delete %{tag_name}", tag_name: tag.name)} | ||||
|         > | ||||
|           <i class="fa-fw fa-lg fas fa-trash"></i> | ||||
|         </.link> | ||||
|       </.tag_card> | ||||
|     <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={:search} | ||||
|         phx-change="search" | ||||
|         phx-submit="search" | ||||
|         class="grow flex items-center" | ||||
|       > | ||||
|         <%= text_input(f, :search_term, | ||||
|           class: "grow input input-primary", | ||||
|           value: @search, | ||||
|           role: "search", | ||||
|           phx_debounce: 300, | ||||
|           placeholder: gettext("Search tags") | ||||
|         ) %> | ||||
|       </.form> | ||||
|     </div> | ||||
|  | ||||
|     <%= if @tags |> Enum.empty?() do %> | ||||
|       <h2 class="title text-xl text-primary-600"> | ||||
|         <%= gettext("No tags") %> | ||||
|         <%= display_emoji("😔") %> | ||||
|       </h2> | ||||
|     <% else %> | ||||
|       <div class="flex flex-row flex-wrap justify-center items-stretch"> | ||||
|         <.tag_card :for={tag <- @tags} tag={tag}> | ||||
|           <.link | ||||
|             patch={Routes.tag_index_path(Endpoint, :edit, tag)} | ||||
|             class="text-primary-600 link" | ||||
|             aria-label={dgettext("actions", "Edit %{tag_name}", tag_name: tag.name)} | ||||
|           > | ||||
|             <i class="fa-fw fa-lg fas fa-edit"></i> | ||||
|           </.link> | ||||
|  | ||||
|           <.link | ||||
|             href="#" | ||||
|             class="text-primary-600 link" | ||||
|             phx-click="delete" | ||||
|             phx-value-id={tag.id} | ||||
|             data-confirm={ | ||||
|               dgettext("prompts", "Are you sure you want to delete %{name}?", name: tag.name) | ||||
|             } | ||||
|             aria-label={dgettext("actions", "Delete %{tag_name}", tag_name: tag.name)} | ||||
|           > | ||||
|             <i class="fa-fw fa-lg fas fa-trash"></i> | ||||
|           </.link> | ||||
|         </.tag_card> | ||||
|       </div> | ||||
|     <% end %> | ||||
|   <% end %> | ||||
| </div> | ||||
|  | ||||
|   | ||||
| @@ -77,7 +77,6 @@ defmodule CanneryWeb.Router do | ||||
|  | ||||
|     live "/type/:id", AmmoTypeLive.Show, :show | ||||
|     live "/type/:id/edit", AmmoTypeLive.Show, :edit | ||||
|     live "/type/:id/table", AmmoTypeLive.Show, :table | ||||
|  | ||||
|     live "/containers", ContainerLive.Index, :index | ||||
|     live "/containers/new", ContainerLive.Index, :new | ||||
|   | ||||
| @@ -107,9 +107,9 @@ | ||||
|     action={Routes.user_settings_path(@conn, :update)} | ||||
|     class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center" | ||||
|   > | ||||
|     <h3 class="title text-primary-600 text-lg text-center col-span-3"> | ||||
|       <%= dgettext("actions", "Change Language") %> | ||||
|     </h3> | ||||
|     <%= label(f, :locale, dgettext("actions", "Change Language"), | ||||
|       class: "title text-primary-600 text-lg text-center col-span-3" | ||||
|     ) %> | ||||
|  | ||||
|     <div | ||||
|       :if={@locale_changeset.action && not @locale_changeset.valid?()} | ||||
|   | ||||
							
								
								
									
										2
									
								
								mix.exs
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								mix.exs
									
									
									
									
									
								
							| @@ -4,7 +4,7 @@ defmodule Cannery.MixProject do | ||||
|   def project do | ||||
|     [ | ||||
|       app: :cannery, | ||||
|       version: "0.8.4", | ||||
|       version: "0.9.0", | ||||
|       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:41 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.ex:59 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.ex:67 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:38 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Add Ammo" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:37 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:34 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Add your first box!" | ||||
| msgstr "" | ||||
| @@ -66,7 +66,7 @@ msgstr "" | ||||
| msgid "Invite someone new!" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:122 | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:124 | ||||
| #: lib/cannery_web/templates/user_confirmation/new.html.heex:32 | ||||
| #: lib/cannery_web/templates/user_registration/new.html.heex:44 | ||||
| #: lib/cannery_web/templates/user_reset_password/edit.html.heex:45 | ||||
| @@ -97,7 +97,7 @@ msgstr "" | ||||
| msgid "New Tag" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:114 | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:116 | ||||
| #: lib/cannery_web/templates/user_confirmation/new.html.heex:29 | ||||
| #: lib/cannery_web/templates/user_registration/new.html.heex:3 | ||||
| #: lib/cannery_web/templates/user_registration/new.html.heex:37 | ||||
| @@ -120,12 +120,12 @@ msgstr "" | ||||
| msgid "Reset password" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/components/add_shot_group_component.html.heex:56 | ||||
| #: 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:159 | ||||
| #: lib/cannery_web/live/container_live/form_component.html.heex:55 | ||||
| #: lib/cannery_web/live/invite_live/form_component.html.heex:32 | ||||
| #: lib/cannery_web/live/range_live/form_component.html.heex:44 | ||||
| #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:350 | ||||
| #: 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/range_live/form_component.html.heex:45 | ||||
| #: lib/cannery_web/live/tag_live/form_component.html.heex:37 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Save" | ||||
| @@ -136,7 +136,7 @@ msgstr "" | ||||
| msgid "Send instructions to reset password" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:75 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:68 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Why not add one?" | ||||
| msgstr "" | ||||
| @@ -156,7 +156,7 @@ msgstr "" | ||||
| msgid "Why not get some ready to shoot?" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:103 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:127 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:103 | ||||
| #: lib/cannery_web/live/range_live/index.html.heex:45 | ||||
| #, elixir-autogen, elixir-format | ||||
| @@ -178,7 +178,7 @@ msgstr "" | ||||
| msgid "Copy to clipboard" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:22 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:14 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "add a container first" | ||||
| msgstr "" | ||||
| @@ -188,7 +188,7 @@ msgstr "" | ||||
| msgid "Create" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/templates/user_settings/edit.html.heex:111 | ||||
| #: lib/cannery_web/templates/user_settings/edit.html.heex:110 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Change Language" | ||||
| msgstr "" | ||||
| @@ -203,13 +203,13 @@ msgstr "" | ||||
| msgid "View in Catalog" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:32 | ||||
| #: 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:120 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:144 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:96 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Move ammo" | ||||
| @@ -237,13 +237,13 @@ msgstr "" | ||||
| msgid "Export Data as JSON" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:84 | ||||
| #: 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:143 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:145 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Clone %{container_name}" | ||||
| msgstr "" | ||||
| @@ -253,15 +253,15 @@ msgstr "" | ||||
| msgid "Copy invite link for %{invite_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:103 | ||||
| #: 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:102 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:158 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:55 | ||||
| #: 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 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Delete %{container_name}" | ||||
| msgstr "" | ||||
| @@ -277,20 +277,20 @@ 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:130 | ||||
| #: 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:74 | ||||
| #: 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:133 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:42 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:135 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:35 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Edit %{container_name}" | ||||
| msgstr "" | ||||
| @@ -300,7 +300,7 @@ msgstr "" | ||||
| msgid "Edit %{tag_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:142 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:166 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:62 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Edit ammo group of %{ammo_group_count} bullets" | ||||
| @@ -316,44 +316,45 @@ msgstr "" | ||||
| msgid "Edit shot group of %{shot_group_count} shots" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/range_live/index.html.heex:113 | ||||
| #: 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:96 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:120 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Stage" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:65 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:122 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:124 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Tag %{container_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:95 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:119 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Unstage" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:64 | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:90 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "View %{ammo_type_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:154 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:178 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Clone ammo group of %{ammo_group_count} bullets" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:169 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:193 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:76 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Delete ammo group of %{ammo_group_count} bullets" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:130 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:154 | ||||
| #: lib/cannery_web/live/ammo_type_live/show.html.heex:206 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "View ammo group of %{ammo_group_count} bullets" | ||||
| 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:41 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.ex:59 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.ex:67 | ||||
| #: lib/cannery_web/live/ammo_group_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:37 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:34 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Add your first box!" | ||||
| msgstr "Fügen Sie ihre erste Box hinzu!" | ||||
| @@ -79,7 +79,7 @@ msgstr "Passwort vergessen?" | ||||
| msgid "Invite someone new!" | ||||
| msgstr "Laden Sie jemanden ein!" | ||||
|  | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:122 | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:124 | ||||
| #: lib/cannery_web/templates/user_confirmation/new.html.heex:32 | ||||
| #: lib/cannery_web/templates/user_registration/new.html.heex:44 | ||||
| #: lib/cannery_web/templates/user_reset_password/edit.html.heex:45 | ||||
| @@ -110,7 +110,7 @@ msgstr "Neuer Behälter" | ||||
| msgid "New Tag" | ||||
| msgstr "Neuer Tag" | ||||
|  | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:114 | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:116 | ||||
| #: lib/cannery_web/templates/user_confirmation/new.html.heex:29 | ||||
| #: lib/cannery_web/templates/user_registration/new.html.heex:3 | ||||
| #: lib/cannery_web/templates/user_registration/new.html.heex:37 | ||||
| @@ -133,12 +133,12 @@ msgstr "Bestätigungsmail erneut senden" | ||||
| msgid "Reset password" | ||||
| msgstr "Passwort zurücksetzen" | ||||
|  | ||||
| #: lib/cannery_web/components/add_shot_group_component.html.heex:56 | ||||
| #: 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:159 | ||||
| #: lib/cannery_web/live/container_live/form_component.html.heex:55 | ||||
| #: lib/cannery_web/live/invite_live/form_component.html.heex:32 | ||||
| #: lib/cannery_web/live/range_live/form_component.html.heex:44 | ||||
| #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:350 | ||||
| #: 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/range_live/form_component.html.heex:45 | ||||
| #: lib/cannery_web/live/tag_live/form_component.html.heex:37 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Save" | ||||
| @@ -149,7 +149,7 @@ msgstr "Speichern" | ||||
| msgid "Send instructions to reset password" | ||||
| msgstr "Anleitung zum Passwort zurücksetzen zusenden" | ||||
|  | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:75 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:68 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Why not add one?" | ||||
| msgstr "Warum fügen Sie keine hinzu?" | ||||
| @@ -169,7 +169,7 @@ 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:103 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:127 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:103 | ||||
| #: lib/cannery_web/live/range_live/index.html.heex:45 | ||||
| #, elixir-autogen, elixir-format | ||||
| @@ -191,7 +191,7 @@ msgstr "Markieren" | ||||
| msgid "Copy to clipboard" | ||||
| msgstr "In die Zwischenablage kopieren" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:22 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:14 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "add a container first" | ||||
| msgstr "Zuerst einen Behälter hinzufügen" | ||||
| @@ -201,7 +201,7 @@ msgstr "Zuerst einen Behälter hinzufügen" | ||||
| msgid "Create" | ||||
| msgstr "Erstellen" | ||||
|  | ||||
| #: lib/cannery_web/templates/user_settings/edit.html.heex:111 | ||||
| #: lib/cannery_web/templates/user_settings/edit.html.heex:110 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Change Language" | ||||
| msgstr "Sprache wechseln" | ||||
| @@ -216,13 +216,13 @@ msgstr "Sprache wechseln" | ||||
| msgid "View in Catalog" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:32 | ||||
| #: 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:120 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:144 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:96 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Move ammo" | ||||
| @@ -250,13 +250,13 @@ msgstr "" | ||||
| msgid "Export Data as JSON" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:84 | ||||
| #: 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:143 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:145 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Clone %{container_name}" | ||||
| msgstr "" | ||||
| @@ -266,15 +266,15 @@ msgstr "" | ||||
| msgid "Copy invite link for %{invite_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:103 | ||||
| #: 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:102 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:158 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:55 | ||||
| #: 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 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Delete %{container_name}" | ||||
| msgstr "" | ||||
| @@ -290,20 +290,20 @@ 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:130 | ||||
| #: 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:74 | ||||
| #: 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:133 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:42 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:135 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:35 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Edit %{container_name}" | ||||
| msgstr "" | ||||
| @@ -313,7 +313,7 @@ msgstr "" | ||||
| msgid "Edit %{tag_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:142 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:166 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:62 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Edit ammo group of %{ammo_group_count} bullets" | ||||
| @@ -329,44 +329,45 @@ msgstr "" | ||||
| msgid "Edit shot group of %{shot_group_count} shots" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/range_live/index.html.heex:113 | ||||
| #: 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:96 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:120 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Stage" | ||||
| msgstr "Munition markieren" | ||||
|  | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:65 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:122 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:124 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Tag %{container_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:95 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:119 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Unstage" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:64 | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:90 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "View %{ammo_type_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:154 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:178 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Clone ammo group of %{ammo_group_count} bullets" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:169 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:193 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:76 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Delete ammo group of %{ammo_group_count} bullets" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:130 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:154 | ||||
| #: lib/cannery_web/live/ammo_type_live/show.html.heex:206 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "View ammo group of %{ammo_group_count} bullets" | ||||
| msgstr "" | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -23,7 +23,7 @@ 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/containers.ex:200 | ||||
| #: lib/cannery/containers.ex:220 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Container must be empty before deleting" | ||||
| msgstr "Behälter muss vor dem Löschen leer sein" | ||||
| @@ -69,6 +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/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 | ||||
| @@ -112,32 +113,27 @@ msgstr "Unbefugt" | ||||
| msgid "User confirmation link is invalid or it has expired." | ||||
| msgstr "Nutzerkonto Bestätigungslink ist ungültig oder abgelaufen." | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:18 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "You are not authorized to view this page" | ||||
| msgstr "Sie sind nicht berechtigt, diese Seite aufzurufen" | ||||
|  | ||||
| #: lib/cannery_web/controllers/user_auth.ex:177 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "You are not authorized to view this page." | ||||
| msgstr "Sie sind nicht berechtigt, diese Seite aufzurufen." | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:144 | ||||
| #: lib/cannery/accounts/user.ex:145 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "did not change" | ||||
| msgstr "hat sich nicht geändert" | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:165 | ||||
| #: lib/cannery/accounts/user.ex:166 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "does not match password" | ||||
| msgstr "Passwort stimmt nicht überein" | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:202 | ||||
| #: lib/cannery/accounts/user.ex:203 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "is not valid" | ||||
| msgstr "ist nicht gültig" | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:99 | ||||
| #: lib/cannery/accounts/user.ex:100 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "must have the @ sign and no spaces" | ||||
| msgstr "Muss ein @ Zeichen und keine Leerzeichen haben" | ||||
| @@ -177,7 +173,7 @@ msgstr "" | ||||
| "Ungültige Nummer an Kopien. Muss zwischen 1 and %{max} liegen. War " | ||||
| "%{multiplier}" | ||||
|  | ||||
| #: lib/cannery/ammo.ex:1015 | ||||
| #: lib/cannery/ammo.ex:1123 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Invalid multiplier" | ||||
| msgstr "" | ||||
| @@ -192,27 +188,27 @@ msgstr "" | ||||
| msgid "Your browser does not support the canvas element." | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:72 | ||||
| #: lib/cannery/activity_log/shot_group.ex:74 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Please select a valid user and ammo pack" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:86 | ||||
| #: lib/cannery/activity_log/shot_group.ex:88 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Ammo left can be at most %{count} rounds" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:82 | ||||
| #: lib/cannery/activity_log/shot_group.ex:84 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Ammo left must be at least 0" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:119 | ||||
| #: lib/cannery/activity_log/shot_group.ex:122 | ||||
| #, 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:78 | ||||
| #: lib/cannery/activity_log/shot_group.ex:80 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "can't be blank" | ||||
| msgstr "" | ||||
|   | ||||
| @@ -32,7 +32,7 @@ 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:54 | ||||
| #: lib/cannery_web/live/ammo_type_live/show.ex:27 | ||||
| #: lib/cannery_web/live/tag_live/index.ex:65 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "%{name} deleted succesfully" | ||||
| @@ -66,14 +66,14 @@ msgstr "" | ||||
| "zurückgenommen werden!" | ||||
|  | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:99 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:155 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:52 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:157 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:45 | ||||
| #: lib/cannery_web/live/tag_live/index.html.heex:63 | ||||
| #, elixir-autogen, elixir-format | ||||
| 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:167 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:191 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:74 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Are you sure you want to delete this ammo?" | ||||
| @@ -128,12 +128,12 @@ 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:58 | ||||
| #: 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:160 | ||||
| #: lib/cannery_web/live/container_live/form_component.html.heex:57 | ||||
| #: lib/cannery_web/live/invite_live/form_component.html.heex:34 | ||||
| #: lib/cannery_web/live/range_live/form_component.html.heex:46 | ||||
| #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:351 | ||||
| #: 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/range_live/form_component.html.heex:47 | ||||
| #: lib/cannery_web/live/tag_live/form_component.html.heex:39 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Saving..." | ||||
| @@ -177,7 +177,7 @@ 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/range_live/index.html.heex:127 | ||||
| #: 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?" | ||||
| @@ -203,7 +203,7 @@ msgstr "%{email} erfolgreich bestätigt." | ||||
| msgid "Ammo moved to %{name} successfully" | ||||
| msgstr "Munition erfolgreich zu %{name} verschoben" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:126 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:116 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Copied to clipboard" | ||||
| msgstr "Der Zwischenablage hinzugefügt" | ||||
| @@ -213,8 +213,8 @@ msgstr "Der Zwischenablage hinzugefügt" | ||||
| msgid "%{name} removed successfully" | ||||
| msgstr "%{name} erfolgreich entfernt" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:18 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:28 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:10 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:20 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "You'll need to" | ||||
| msgstr "Sie müssen" | ||||
| @@ -234,7 +234,7 @@ 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/index.ex:94 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.ex:55 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Ammo deleted succesfully" | ||||
| @@ -257,7 +257,7 @@ 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:96 | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:122 | ||||
| #: lib/cannery_web/live/ammo_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!" | ||||
| @@ -268,27 +268,27 @@ msgstr "Sind Sie sicher, dass sie %{name} löschen möchten?" | ||||
| msgid "Register to setup Cannery" | ||||
| msgstr "Registrieren Sie sich, um %{name} zu bearbeiten" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:53 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:43 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{invite_name} deleted succesfully" | ||||
| msgstr "%{name} erfolgreich gelöscht" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:114 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:104 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{invite_name} disabled succesfully" | ||||
| msgstr "%{name} erfolgreich deaktiviert" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:90 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:80 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{invite_name} enabled succesfully" | ||||
| msgstr "%{name} erfolgreich aktiviert" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:68 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:58 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{invite_name} updated succesfully" | ||||
| msgstr "%{name} erfolgreich aktualisiert" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:135 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:125 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{user_email} deleted succesfully" | ||||
| msgstr "%{name} erfolgreich gelöscht" | ||||
|   | ||||
										
											
												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:41 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.ex:59 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.ex:67 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:38 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Add Ammo" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:37 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:34 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Add your first box!" | ||||
| msgstr "" | ||||
| @@ -66,7 +66,7 @@ msgstr "" | ||||
| msgid "Invite someone new!" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:122 | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:124 | ||||
| #: lib/cannery_web/templates/user_confirmation/new.html.heex:32 | ||||
| #: lib/cannery_web/templates/user_registration/new.html.heex:44 | ||||
| #: lib/cannery_web/templates/user_reset_password/edit.html.heex:45 | ||||
| @@ -97,7 +97,7 @@ msgstr "" | ||||
| msgid "New Tag" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:114 | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:116 | ||||
| #: lib/cannery_web/templates/user_confirmation/new.html.heex:29 | ||||
| #: lib/cannery_web/templates/user_registration/new.html.heex:3 | ||||
| #: lib/cannery_web/templates/user_registration/new.html.heex:37 | ||||
| @@ -120,12 +120,12 @@ msgstr "" | ||||
| msgid "Reset password" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/components/add_shot_group_component.html.heex:56 | ||||
| #: 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:159 | ||||
| #: lib/cannery_web/live/container_live/form_component.html.heex:55 | ||||
| #: lib/cannery_web/live/invite_live/form_component.html.heex:32 | ||||
| #: lib/cannery_web/live/range_live/form_component.html.heex:44 | ||||
| #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:350 | ||||
| #: 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/range_live/form_component.html.heex:45 | ||||
| #: lib/cannery_web/live/tag_live/form_component.html.heex:37 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Save" | ||||
| @@ -136,7 +136,7 @@ msgstr "" | ||||
| msgid "Send instructions to reset password" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:75 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:68 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Why not add one?" | ||||
| msgstr "" | ||||
| @@ -156,7 +156,7 @@ msgstr "" | ||||
| msgid "Why not get some ready to shoot?" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:103 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:127 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:103 | ||||
| #: lib/cannery_web/live/range_live/index.html.heex:45 | ||||
| #, elixir-autogen, elixir-format | ||||
| @@ -178,7 +178,7 @@ msgstr "" | ||||
| msgid "Copy to clipboard" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:22 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:14 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "add a container first" | ||||
| msgstr "" | ||||
| @@ -188,7 +188,7 @@ msgstr "" | ||||
| msgid "Create" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/templates/user_settings/edit.html.heex:111 | ||||
| #: lib/cannery_web/templates/user_settings/edit.html.heex:110 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Change Language" | ||||
| msgstr "" | ||||
| @@ -203,13 +203,13 @@ msgstr "" | ||||
| msgid "View in Catalog" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:32 | ||||
| #: 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:120 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:144 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:96 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Move ammo" | ||||
| @@ -237,13 +237,13 @@ msgstr "" | ||||
| msgid "Export Data as JSON" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:84 | ||||
| #: 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:143 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:145 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Clone %{container_name}" | ||||
| msgstr "" | ||||
| @@ -253,15 +253,15 @@ msgstr "" | ||||
| msgid "Copy invite link for %{invite_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:103 | ||||
| #: 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:102 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:158 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:55 | ||||
| #: 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 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Delete %{container_name}" | ||||
| msgstr "" | ||||
| @@ -277,20 +277,20 @@ 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:130 | ||||
| #: 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:74 | ||||
| #: 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:133 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:42 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:135 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:35 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Edit %{container_name}" | ||||
| msgstr "" | ||||
| @@ -300,7 +300,7 @@ msgstr "" | ||||
| msgid "Edit %{tag_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:142 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:166 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:62 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Edit ammo group of %{ammo_group_count} bullets" | ||||
| @@ -316,44 +316,45 @@ msgstr "" | ||||
| msgid "Edit shot group of %{shot_group_count} shots" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/range_live/index.html.heex:113 | ||||
| #: 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:96 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:120 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Stage" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:65 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:122 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:124 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Tag %{container_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:95 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:119 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Unstage" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:64 | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:90 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "View %{ammo_type_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:154 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:178 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Clone ammo group of %{ammo_group_count} bullets" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:169 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:193 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:76 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Delete ammo group of %{ammo_group_count} bullets" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:130 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:154 | ||||
| #: lib/cannery_web/live/ammo_type_live/show.html.heex:206 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "View ammo group of %{ammo_group_count} bullets" | ||||
| msgstr "" | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -10,7 +10,7 @@ msgid "" | ||||
| msgstr "" | ||||
| "Language: en\n" | ||||
|  | ||||
| #: lib/cannery/containers.ex:200 | ||||
| #: lib/cannery/containers.ex:220 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Container must be empty before deleting" | ||||
| msgstr "" | ||||
| @@ -56,6 +56,7 @@ msgstr "" | ||||
| msgid "Not found" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_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 | ||||
| @@ -98,33 +99,28 @@ msgstr "" | ||||
| msgid "User confirmation link is invalid or it has expired." | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:18 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "You are not authorized to view this page" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/controllers/user_auth.ex:177 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "You are not authorized to view this page." | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:144 | ||||
| #: lib/cannery/accounts/user.ex:145 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "did not change" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:165 | ||||
| #: lib/cannery/accounts/user.ex:166 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "does not match password" | ||||
| msgstr "" | ||||
|  | ||||
| ## From Ecto.Changeset.put_change/3 | ||||
| #: lib/cannery/accounts/user.ex:202 | ||||
| #: lib/cannery/accounts/user.ex:203 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "is not valid" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:99 | ||||
| #: lib/cannery/accounts/user.ex:100 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "must have the @ sign and no spaces" | ||||
| msgstr "" | ||||
| @@ -160,7 +156,7 @@ msgstr "" | ||||
| msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/ammo.ex:1015 | ||||
| #: lib/cannery/ammo.ex:1123 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Invalid multiplier" | ||||
| msgstr "" | ||||
| @@ -175,27 +171,27 @@ msgstr "" | ||||
| msgid "Your browser does not support the canvas element." | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:72 | ||||
| #: lib/cannery/activity_log/shot_group.ex:74 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Please select a valid user and ammo pack" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:86 | ||||
| #: lib/cannery/activity_log/shot_group.ex:88 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Ammo left can be at most %{count} rounds" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:82 | ||||
| #: lib/cannery/activity_log/shot_group.ex:84 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Ammo left must be at least 0" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:119 | ||||
| #: lib/cannery/activity_log/shot_group.ex:122 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Count can be at most %{count} shots" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:78 | ||||
| #: lib/cannery/activity_log/shot_group.ex:80 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "can't be blank" | ||||
| msgstr "" | ||||
|   | ||||
| @@ -19,7 +19,7 @@ msgid "%{name} created successfully" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.ex:72 | ||||
| #: lib/cannery_web/live/ammo_type_live/show.ex:54 | ||||
| #: lib/cannery_web/live/ammo_type_live/show.ex:27 | ||||
| #: lib/cannery_web/live/tag_live/index.ex:65 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "%{name} deleted succesfully" | ||||
| @@ -51,14 +51,14 @@ msgid "Are you sure you want to delete %{email}? This action is permanent!" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:99 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:155 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:52 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:157 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:45 | ||||
| #: lib/cannery_web/live/tag_live/index.html.heex:63 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Are you sure you want to delete %{name}?" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:167 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:191 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:74 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Are you sure you want to delete this ammo?" | ||||
| @@ -109,12 +109,12 @@ msgstr "" | ||||
| msgid "Please check your email to verify your account" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/components/add_shot_group_component.html.heex:58 | ||||
| #: 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:160 | ||||
| #: lib/cannery_web/live/container_live/form_component.html.heex:57 | ||||
| #: lib/cannery_web/live/invite_live/form_component.html.heex:34 | ||||
| #: lib/cannery_web/live/range_live/form_component.html.heex:46 | ||||
| #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:351 | ||||
| #: 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/range_live/form_component.html.heex:47 | ||||
| #: lib/cannery_web/live/tag_live/form_component.html.heex:39 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Saving..." | ||||
| @@ -156,7 +156,7 @@ 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/range_live/index.html.heex:127 | ||||
| #: 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 "" | ||||
| @@ -182,7 +182,7 @@ msgstr "" | ||||
| msgid "Ammo moved to %{name} successfully" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:126 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:116 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Copied to clipboard" | ||||
| msgstr "" | ||||
| @@ -192,8 +192,8 @@ msgstr "" | ||||
| msgid "%{name} removed successfully" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:18 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:28 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:10 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:20 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "You'll need to" | ||||
| msgstr "" | ||||
| @@ -213,7 +213,7 @@ msgstr "" | ||||
| msgid "Language updated successfully." | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.ex:89 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.ex:94 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.ex:55 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Ammo deleted succesfully" | ||||
| @@ -236,7 +236,7 @@ msgid_plural "Ammo added successfully" | ||||
| msgstr[0] "" | ||||
| msgstr[1] "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:96 | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:122 | ||||
| #: lib/cannery_web/live/ammo_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!" | ||||
| @@ -247,27 +247,27 @@ msgstr "" | ||||
| msgid "Register to setup Cannery" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:53 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:43 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{invite_name} deleted succesfully" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:114 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:104 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{invite_name} disabled succesfully" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:90 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:80 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{invite_name} enabled succesfully" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:68 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:58 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{invite_name} updated succesfully" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:135 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:125 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{user_email} deleted succesfully" | ||||
| msgstr "" | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
| msgid "" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/containers.ex:200 | ||||
| #: lib/cannery/containers.ex:220 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Container must be empty before deleting" | ||||
| msgstr "" | ||||
| @@ -56,6 +56,7 @@ msgstr "" | ||||
| msgid "Not found" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_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 | ||||
| @@ -98,32 +99,27 @@ msgstr "" | ||||
| msgid "User confirmation link is invalid or it has expired." | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:18 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "You are not authorized to view this page" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/controllers/user_auth.ex:177 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "You are not authorized to view this page." | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:144 | ||||
| #: lib/cannery/accounts/user.ex:145 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "did not change" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:165 | ||||
| #: lib/cannery/accounts/user.ex:166 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "does not match password" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:202 | ||||
| #: lib/cannery/accounts/user.ex:203 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "is not valid" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:99 | ||||
| #: lib/cannery/accounts/user.ex:100 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "must have the @ sign and no spaces" | ||||
| msgstr "" | ||||
| @@ -159,7 +155,7 @@ msgstr "" | ||||
| msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/ammo.ex:1015 | ||||
| #: lib/cannery/ammo.ex:1123 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Invalid multiplier" | ||||
| msgstr "" | ||||
| @@ -174,27 +170,27 @@ msgstr "" | ||||
| msgid "Your browser does not support the canvas element." | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:72 | ||||
| #: lib/cannery/activity_log/shot_group.ex:74 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Please select a valid user and ammo pack" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:86 | ||||
| #: lib/cannery/activity_log/shot_group.ex:88 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Ammo left can be at most %{count} rounds" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:82 | ||||
| #: lib/cannery/activity_log/shot_group.ex:84 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Ammo left must be at least 0" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:119 | ||||
| #: lib/cannery/activity_log/shot_group.ex:122 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Count can be at most %{count} shots" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:78 | ||||
| #: lib/cannery/activity_log/shot_group.ex:80 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "can't be blank" | ||||
| 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:41 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.ex:59 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.ex:67 | ||||
| #: lib/cannery_web/live/ammo_group_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:37 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:34 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Add your first box!" | ||||
| msgstr "¡Añade tu primera caja!" | ||||
| @@ -79,7 +79,7 @@ msgstr "¿Has olvidado tu contraseña?" | ||||
| msgid "Invite someone new!" | ||||
| msgstr "¡Invita a alguien nuevo!" | ||||
|  | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:122 | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:124 | ||||
| #: lib/cannery_web/templates/user_confirmation/new.html.heex:32 | ||||
| #: lib/cannery_web/templates/user_registration/new.html.heex:44 | ||||
| #: lib/cannery_web/templates/user_reset_password/edit.html.heex:45 | ||||
| @@ -110,7 +110,7 @@ msgstr "Nuevo Contenedor" | ||||
| msgid "New Tag" | ||||
| msgstr "Nueva Etiqueta" | ||||
|  | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:114 | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:116 | ||||
| #: lib/cannery_web/templates/user_confirmation/new.html.heex:29 | ||||
| #: lib/cannery_web/templates/user_registration/new.html.heex:3 | ||||
| #: lib/cannery_web/templates/user_registration/new.html.heex:37 | ||||
| @@ -133,12 +133,12 @@ msgstr "Reenviar instrucciones de confirmación" | ||||
| msgid "Reset password" | ||||
| msgstr "Resetear contraseña" | ||||
|  | ||||
| #: lib/cannery_web/components/add_shot_group_component.html.heex:56 | ||||
| #: 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:159 | ||||
| #: lib/cannery_web/live/container_live/form_component.html.heex:55 | ||||
| #: lib/cannery_web/live/invite_live/form_component.html.heex:32 | ||||
| #: lib/cannery_web/live/range_live/form_component.html.heex:44 | ||||
| #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:350 | ||||
| #: 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/range_live/form_component.html.heex:45 | ||||
| #: lib/cannery_web/live/tag_live/form_component.html.heex:37 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Save" | ||||
| @@ -149,7 +149,7 @@ msgstr "Guardar" | ||||
| msgid "Send instructions to reset password" | ||||
| msgstr "Enviar instrucciones para reestablecer contraseña" | ||||
|  | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:75 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:68 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Why not add one?" | ||||
| msgstr "¿Por qué no añadir una?" | ||||
| @@ -169,7 +169,7 @@ 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:103 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:127 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:103 | ||||
| #: lib/cannery_web/live/range_live/index.html.heex:45 | ||||
| #, elixir-autogen, elixir-format | ||||
| @@ -191,7 +191,7 @@ msgstr "Seleccionar" | ||||
| msgid "Copy to clipboard" | ||||
| msgstr "Copiar al portapapeles" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:22 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:14 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "add a container first" | ||||
| msgstr "añade primero un contenedor" | ||||
| @@ -201,7 +201,7 @@ msgstr "añade primero un contenedor" | ||||
| msgid "Create" | ||||
| msgstr "Crear" | ||||
|  | ||||
| #: lib/cannery_web/templates/user_settings/edit.html.heex:111 | ||||
| #: lib/cannery_web/templates/user_settings/edit.html.heex:110 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Change Language" | ||||
| msgstr "Cambiar Lenguaje" | ||||
| @@ -216,13 +216,13 @@ msgstr "Cambiar lenguaje" | ||||
| msgid "View in Catalog" | ||||
| msgstr "Ver en Catalogo" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:32 | ||||
| #: 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:120 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:144 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:96 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Move ammo" | ||||
| @@ -250,13 +250,13 @@ 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:84 | ||||
| #: 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:143 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:145 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Clone %{container_name}" | ||||
| msgstr "" | ||||
| @@ -266,15 +266,15 @@ msgstr "" | ||||
| msgid "Copy invite link for %{invite_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:103 | ||||
| #: 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:102 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:158 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:55 | ||||
| #: 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 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Delete %{container_name}" | ||||
| msgstr "" | ||||
| @@ -290,20 +290,20 @@ 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:130 | ||||
| #: 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:74 | ||||
| #: 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:133 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:42 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:135 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:35 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Edit %{container_name}" | ||||
| msgstr "" | ||||
| @@ -313,7 +313,7 @@ msgstr "" | ||||
| msgid "Edit %{tag_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:142 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:166 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:62 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Edit ammo group of %{ammo_group_count} bullets" | ||||
| @@ -329,44 +329,45 @@ msgstr "" | ||||
| msgid "Edit shot group of %{shot_group_count} shots" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/range_live/index.html.heex:113 | ||||
| #: 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:96 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:120 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Stage" | ||||
| msgstr "Preparar munición" | ||||
|  | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:65 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:122 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:124 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Tag %{container_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:95 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:119 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Unstage" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:64 | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:90 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "View %{ammo_type_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:154 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:178 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Clone ammo group of %{ammo_group_count} bullets" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:169 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:193 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:76 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Delete ammo group of %{ammo_group_count} bullets" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:130 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:154 | ||||
| #: lib/cannery_web/live/ammo_type_live/show.html.heex:206 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "View ammo group of %{ammo_group_count} bullets" | ||||
| msgstr "" | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -23,7 +23,7 @@ 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/containers.ex:200 | ||||
| #: lib/cannery/containers.ex:220 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Container must be empty before deleting" | ||||
| msgstr "El contenedor debe estar vacío antes de ser borrado" | ||||
| @@ -69,6 +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/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 | ||||
| @@ -114,32 +115,27 @@ msgstr "No autorizado" | ||||
| msgid "User confirmation link is invalid or it has expired." | ||||
| msgstr "El enlace de confirmación de usuario no es válido o ha caducado." | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:18 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "You are not authorized to view this page" | ||||
| msgstr "No está autorizado a ver esta página" | ||||
|  | ||||
| #: lib/cannery_web/controllers/user_auth.ex:177 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "You are not authorized to view this page." | ||||
| msgstr "No está autorizado a ver esta página." | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:144 | ||||
| #: lib/cannery/accounts/user.ex:145 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "did not change" | ||||
| msgstr "no cambió" | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:165 | ||||
| #: lib/cannery/accounts/user.ex:166 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "does not match password" | ||||
| msgstr "no coincide con la contraseña" | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:202 | ||||
| #: lib/cannery/accounts/user.ex:203 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "is not valid" | ||||
| msgstr "no es válido" | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:99 | ||||
| #: lib/cannery/accounts/user.ex:100 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "must have the @ sign and no spaces" | ||||
| msgstr "debe tener el signo @ y no contener espacios" | ||||
| @@ -175,7 +171,7 @@ msgstr "No se ha podido procesar el número de copias" | ||||
| 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:1015 | ||||
| #: lib/cannery/ammo.ex:1123 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Invalid multiplier" | ||||
| msgstr "Multiplicador inválido" | ||||
| @@ -190,27 +186,27 @@ msgstr "Por favor escoja un tipo de munición y un contenedor" | ||||
| 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:72 | ||||
| #: lib/cannery/activity_log/shot_group.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:86 | ||||
| #: lib/cannery/activity_log/shot_group.ex:88 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Ammo left can be at most %{count} rounds" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:82 | ||||
| #: lib/cannery/activity_log/shot_group.ex:84 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Ammo left must be at least 0" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:119 | ||||
| #: lib/cannery/activity_log/shot_group.ex:122 | ||||
| #, 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:78 | ||||
| #: lib/cannery/activity_log/shot_group.ex:80 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "can't be blank" | ||||
| msgstr "" | ||||
|   | ||||
| @@ -32,7 +32,7 @@ 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:54 | ||||
| #: lib/cannery_web/live/ammo_type_live/show.ex:27 | ||||
| #: lib/cannery_web/live/tag_live/index.ex:65 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "%{name} deleted succesfully" | ||||
| @@ -66,14 +66,14 @@ msgid "Are you sure you want to delete %{email}? This action is permanent!" | ||||
| msgstr "Está seguro que desea eliminar %{email}? Esta acción es permanente!" | ||||
|  | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:99 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:155 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:52 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:157 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:45 | ||||
| #: lib/cannery_web/live/tag_live/index.html.heex:63 | ||||
| #, elixir-autogen, elixir-format | ||||
| 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:167 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:191 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:74 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Are you sure you want to delete this ammo?" | ||||
| @@ -128,12 +128,12 @@ 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:58 | ||||
| #: 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:160 | ||||
| #: lib/cannery_web/live/container_live/form_component.html.heex:57 | ||||
| #: lib/cannery_web/live/invite_live/form_component.html.heex:34 | ||||
| #: lib/cannery_web/live/range_live/form_component.html.heex:46 | ||||
| #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:351 | ||||
| #: 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/range_live/form_component.html.heex:47 | ||||
| #: lib/cannery_web/live/tag_live/form_component.html.heex:39 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Saving..." | ||||
| @@ -176,7 +176,7 @@ 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/range_live/index.html.heex:127 | ||||
| #: 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?" | ||||
| @@ -202,7 +202,7 @@ msgstr "%{email} confirmado exitosamente." | ||||
| msgid "Ammo moved to %{name} successfully" | ||||
| msgstr "Munición movida a %{name} exitosamente" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:126 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:116 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Copied to clipboard" | ||||
| msgstr "Copiado al portapapeles" | ||||
| @@ -212,8 +212,8 @@ msgstr "Copiado al portapapeles" | ||||
| msgid "%{name} removed successfully" | ||||
| msgstr "%{name} eliminado exitosamente" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:18 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:28 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:10 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:20 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "You'll need to" | ||||
| msgstr "Necesitará hacerlo" | ||||
| @@ -233,7 +233,7 @@ 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/index.ex:94 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.ex:55 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Ammo deleted succesfully" | ||||
| @@ -256,7 +256,7 @@ 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:96 | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:122 | ||||
| #: lib/cannery_web/live/ammo_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!" | ||||
| @@ -269,27 +269,27 @@ msgstr "" | ||||
| msgid "Register to setup Cannery" | ||||
| msgstr "Regístrese para configurar %{name}" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:53 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:43 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{invite_name} deleted succesfully" | ||||
| msgstr "%{name} borrado exitosamente" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:114 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:104 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{invite_name} disabled succesfully" | ||||
| msgstr "%{name} desactivado exitosamente" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:90 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:80 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{invite_name} enabled succesfully" | ||||
| msgstr "%{name} activado exitosamente" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:68 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:58 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{invite_name} updated succesfully" | ||||
| msgstr "%{name} actualizado exitosamente" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:135 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:125 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{user_email} deleted succesfully" | ||||
| msgstr "%{name} borrado exitosamente" | ||||
|   | ||||
| @@ -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:41 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.ex:59 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.ex:67 | ||||
| #: lib/cannery_web/live/ammo_group_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:37 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:34 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Add your first box!" | ||||
| msgstr "Ajoutez votre première caisse !" | ||||
| @@ -79,7 +79,7 @@ msgstr "Mot de passe oublié ?" | ||||
| msgid "Invite someone new!" | ||||
| msgstr "Invitez une nouvelle personne !" | ||||
|  | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:122 | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:124 | ||||
| #: lib/cannery_web/templates/user_confirmation/new.html.heex:32 | ||||
| #: lib/cannery_web/templates/user_registration/new.html.heex:44 | ||||
| #: lib/cannery_web/templates/user_reset_password/edit.html.heex:45 | ||||
| @@ -110,7 +110,7 @@ msgstr "Nouveau conteneur" | ||||
| msgid "New Tag" | ||||
| msgstr "Nouveau tag" | ||||
|  | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:114 | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:116 | ||||
| #: lib/cannery_web/templates/user_confirmation/new.html.heex:29 | ||||
| #: lib/cannery_web/templates/user_registration/new.html.heex:3 | ||||
| #: lib/cannery_web/templates/user_registration/new.html.heex:37 | ||||
| @@ -133,12 +133,12 @@ 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:56 | ||||
| #: 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:159 | ||||
| #: lib/cannery_web/live/container_live/form_component.html.heex:55 | ||||
| #: lib/cannery_web/live/invite_live/form_component.html.heex:32 | ||||
| #: lib/cannery_web/live/range_live/form_component.html.heex:44 | ||||
| #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:350 | ||||
| #: 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/range_live/form_component.html.heex:45 | ||||
| #: lib/cannery_web/live/tag_live/form_component.html.heex:37 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Save" | ||||
| @@ -149,7 +149,7 @@ msgstr "Sauvegarder" | ||||
| msgid "Send instructions to reset password" | ||||
| msgstr "Envoyer les instructions pour réinitialiser le mot de passe" | ||||
|  | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:75 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:68 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Why not add one?" | ||||
| msgstr "Pourquoi pas en ajouter un ?" | ||||
| @@ -169,7 +169,7 @@ 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:103 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:127 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:103 | ||||
| #: lib/cannery_web/live/range_live/index.html.heex:45 | ||||
| #, elixir-autogen, elixir-format | ||||
| @@ -191,7 +191,7 @@ msgstr "Sélectionner" | ||||
| msgid "Copy to clipboard" | ||||
| msgstr "Copier dans le presse-papier" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:22 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:14 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "add a container first" | ||||
| msgstr "ajouter un conteneur en premier" | ||||
| @@ -201,7 +201,7 @@ msgstr "ajouter un conteneur en premier" | ||||
| msgid "Create" | ||||
| msgstr "Créer" | ||||
|  | ||||
| #: lib/cannery_web/templates/user_settings/edit.html.heex:111 | ||||
| #: lib/cannery_web/templates/user_settings/edit.html.heex:110 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Change Language" | ||||
| msgstr "Changer la langue" | ||||
| @@ -216,13 +216,13 @@ msgstr "Changer la langue" | ||||
| msgid "View in Catalog" | ||||
| msgstr "Voir en catalogue" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:32 | ||||
| #: 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:120 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:144 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:96 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Move ammo" | ||||
| @@ -250,13 +250,13 @@ msgstr "" | ||||
| msgid "Export Data as JSON" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:84 | ||||
| #: 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:143 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:145 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Clone %{container_name}" | ||||
| msgstr "" | ||||
| @@ -266,15 +266,15 @@ msgstr "" | ||||
| msgid "Copy invite link for %{invite_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:103 | ||||
| #: 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:102 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:158 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:55 | ||||
| #: 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 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Delete %{container_name}" | ||||
| msgstr "" | ||||
| @@ -290,20 +290,20 @@ 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:130 | ||||
| #: 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:74 | ||||
| #: 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:133 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:42 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:135 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:35 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Edit %{container_name}" | ||||
| msgstr "" | ||||
| @@ -313,7 +313,7 @@ msgstr "" | ||||
| msgid "Edit %{tag_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:142 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:166 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:62 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Edit ammo group of %{ammo_group_count} bullets" | ||||
| @@ -329,44 +329,45 @@ msgstr "" | ||||
| msgid "Edit shot group of %{shot_group_count} shots" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/range_live/index.html.heex:113 | ||||
| #: 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:96 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:120 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Stage" | ||||
| msgstr "Munition préparée" | ||||
|  | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:65 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:122 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:124 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Tag %{container_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:95 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:119 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Unstage" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:64 | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:90 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "View %{ammo_type_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:154 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:178 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Clone ammo group of %{ammo_group_count} bullets" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:169 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:193 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:76 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Delete ammo group of %{ammo_group_count} bullets" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:130 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:154 | ||||
| #: lib/cannery_web/live/ammo_type_live/show.html.heex:206 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "View ammo group of %{ammo_group_count} bullets" | ||||
| msgstr "" | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -23,7 +23,7 @@ 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/containers.ex:200 | ||||
| #: lib/cannery/containers.ex:220 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Container must be empty before deleting" | ||||
| msgstr "Le conteneur doit être vide pour être supprimé" | ||||
| @@ -69,6 +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/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 | ||||
| @@ -113,32 +114,27 @@ msgstr "Non autorisé·e" | ||||
| msgid "User confirmation link is invalid or it has expired." | ||||
| msgstr "Le lien de confirmation d’utilisateur·ice est invalide ou a expiré." | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:18 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "You are not authorized to view this page" | ||||
| msgstr "Vous n’êtes pas autorisé·e à voir cette page" | ||||
|  | ||||
| #: lib/cannery_web/controllers/user_auth.ex:177 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "You are not authorized to view this page." | ||||
| msgstr "Vous n’êtes pas autorisé·e à voir cette page." | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:144 | ||||
| #: lib/cannery/accounts/user.ex:145 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "did not change" | ||||
| msgstr "est inchangé" | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:165 | ||||
| #: lib/cannery/accounts/user.ex:166 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "does not match password" | ||||
| msgstr "le mot de passe ne correspond pas" | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:202 | ||||
| #: lib/cannery/accounts/user.ex:203 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "is not valid" | ||||
| msgstr "n’est pas valide" | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:99 | ||||
| #: lib/cannery/accounts/user.ex:100 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "must have the @ sign and no spaces" | ||||
| msgstr "doit contenir le symbole @ et aucune espace" | ||||
| @@ -176,7 +172,7 @@ msgstr "Impossible d'analyser le nombre de copies" | ||||
| 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:1015 | ||||
| #: lib/cannery/ammo.ex:1123 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Invalid multiplier" | ||||
| msgstr "Multiplicateur invalide" | ||||
| @@ -191,27 +187,27 @@ msgstr "Veuillez choisir un type de munitions et un conteneur" | ||||
| msgid "Your browser does not support the canvas element." | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:72 | ||||
| #: lib/cannery/activity_log/shot_group.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:86 | ||||
| #: lib/cannery/activity_log/shot_group.ex:88 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Ammo left can be at most %{count} rounds" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:82 | ||||
| #: lib/cannery/activity_log/shot_group.ex:84 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Ammo left must be at least 0" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:119 | ||||
| #: lib/cannery/activity_log/shot_group.ex:122 | ||||
| #, 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:78 | ||||
| #: lib/cannery/activity_log/shot_group.ex:80 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "can't be blank" | ||||
| msgstr "" | ||||
|   | ||||
| @@ -32,7 +32,7 @@ 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:54 | ||||
| #: lib/cannery_web/live/ammo_type_live/show.ex:27 | ||||
| #: lib/cannery_web/live/tag_live/index.ex:65 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "%{name} deleted succesfully" | ||||
| @@ -67,14 +67,14 @@ msgstr "" | ||||
| "Êtes-vous certain·e de supprimer %{email} ? Cette action est définitive !" | ||||
|  | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:99 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:155 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:52 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:157 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:45 | ||||
| #: lib/cannery_web/live/tag_live/index.html.heex:63 | ||||
| #, elixir-autogen, elixir-format | ||||
| 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:167 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:191 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:74 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Are you sure you want to delete this ammo?" | ||||
| @@ -129,12 +129,12 @@ 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:58 | ||||
| #: 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:160 | ||||
| #: lib/cannery_web/live/container_live/form_component.html.heex:57 | ||||
| #: lib/cannery_web/live/invite_live/form_component.html.heex:34 | ||||
| #: lib/cannery_web/live/range_live/form_component.html.heex:46 | ||||
| #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:351 | ||||
| #: 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/range_live/form_component.html.heex:47 | ||||
| #: lib/cannery_web/live/tag_live/form_component.html.heex:39 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Saving..." | ||||
| @@ -178,7 +178,7 @@ 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/range_live/index.html.heex:127 | ||||
| #: 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 ?" | ||||
| @@ -204,7 +204,7 @@ msgstr "%{email} confirmé avec succès." | ||||
| msgid "Ammo moved to %{name} successfully" | ||||
| msgstr "Munition déplacée à %{name} avec succès" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:126 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:116 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Copied to clipboard" | ||||
| msgstr "Copié dans le presse-papier" | ||||
| @@ -214,8 +214,8 @@ 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:18 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:28 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:10 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:20 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "You'll need to" | ||||
| msgstr "Vous aurez besoin de" | ||||
| @@ -235,7 +235,7 @@ 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/index.ex:94 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.ex:55 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Ammo deleted succesfully" | ||||
| @@ -258,7 +258,7 @@ 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:96 | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:122 | ||||
| #: lib/cannery_web/live/ammo_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!" | ||||
| @@ -269,27 +269,27 @@ msgstr "Êtes-vous certain·e de supprimer %{name} ?" | ||||
| msgid "Register to setup Cannery" | ||||
| msgstr "S’enregistrer pour mettre en place %{name}" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:53 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:43 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{invite_name} deleted succesfully" | ||||
| msgstr "%{name} supprimé· avec succès" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:114 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:104 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{invite_name} disabled succesfully" | ||||
| msgstr "%{name} supprimé·e avec succès" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:90 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:80 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{invite_name} enabled succesfully" | ||||
| msgstr "%{name} activé·e avec succès" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:68 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:58 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{invite_name} updated succesfully" | ||||
| msgstr "%{name} mis à jour avec succès" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:135 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:125 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{user_email} deleted succesfully" | ||||
| msgstr "%{name} supprimé· avec succès" | ||||
|   | ||||
| @@ -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:41 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.ex:59 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.ex:67 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:38 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Add Ammo" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:37 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:34 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Add your first box!" | ||||
| msgstr "" | ||||
| @@ -77,7 +77,7 @@ msgstr "" | ||||
| msgid "Invite someone new!" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:122 | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:124 | ||||
| #: lib/cannery_web/templates/user_confirmation/new.html.heex:32 | ||||
| #: lib/cannery_web/templates/user_registration/new.html.heex:44 | ||||
| #: lib/cannery_web/templates/user_reset_password/edit.html.heex:45 | ||||
| @@ -108,7 +108,7 @@ msgstr "" | ||||
| msgid "New Tag" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:114 | ||||
| #: lib/cannery_web/components/core_components/topbar.html.heex:116 | ||||
| #: lib/cannery_web/templates/user_confirmation/new.html.heex:29 | ||||
| #: lib/cannery_web/templates/user_registration/new.html.heex:3 | ||||
| #: lib/cannery_web/templates/user_registration/new.html.heex:37 | ||||
| @@ -131,12 +131,12 @@ msgstr "" | ||||
| msgid "Reset password" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/components/add_shot_group_component.html.heex:56 | ||||
| #: 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:159 | ||||
| #: lib/cannery_web/live/container_live/form_component.html.heex:55 | ||||
| #: lib/cannery_web/live/invite_live/form_component.html.heex:32 | ||||
| #: lib/cannery_web/live/range_live/form_component.html.heex:44 | ||||
| #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:350 | ||||
| #: 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/range_live/form_component.html.heex:45 | ||||
| #: lib/cannery_web/live/tag_live/form_component.html.heex:37 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Save" | ||||
| @@ -147,7 +147,7 @@ msgstr "" | ||||
| msgid "Send instructions to reset password" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:75 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:68 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Why not add one?" | ||||
| msgstr "" | ||||
| @@ -167,7 +167,7 @@ msgstr "" | ||||
| msgid "Why not get some ready to shoot?" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:103 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:127 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:103 | ||||
| #: lib/cannery_web/live/range_live/index.html.heex:45 | ||||
| #, elixir-autogen, elixir-format | ||||
| @@ -189,7 +189,7 @@ msgstr "" | ||||
| msgid "Copy to clipboard" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:22 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:14 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "add a container first" | ||||
| msgstr "" | ||||
| @@ -199,7 +199,7 @@ msgstr "" | ||||
| msgid "Create" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/templates/user_settings/edit.html.heex:111 | ||||
| #: lib/cannery_web/templates/user_settings/edit.html.heex:110 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Change Language" | ||||
| msgstr "" | ||||
| @@ -214,13 +214,13 @@ msgstr "" | ||||
| msgid "View in Catalog" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:32 | ||||
| #: 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:120 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:144 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:96 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Move ammo" | ||||
| @@ -248,13 +248,13 @@ msgstr "" | ||||
| msgid "Export Data as JSON" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:84 | ||||
| #: 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:143 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:145 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Clone %{container_name}" | ||||
| msgstr "" | ||||
| @@ -264,15 +264,15 @@ msgstr "" | ||||
| msgid "Copy invite link for %{invite_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:103 | ||||
| #: 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:102 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:158 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:55 | ||||
| #: 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 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Delete %{container_name}" | ||||
| msgstr "" | ||||
| @@ -288,20 +288,20 @@ 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:130 | ||||
| #: 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:74 | ||||
| #: 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:133 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:42 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:135 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:35 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Edit %{container_name}" | ||||
| msgstr "" | ||||
| @@ -311,7 +311,7 @@ msgstr "" | ||||
| msgid "Edit %{tag_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:142 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:166 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:62 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Edit ammo group of %{ammo_group_count} bullets" | ||||
| @@ -327,44 +327,45 @@ msgstr "" | ||||
| msgid "Edit shot group of %{shot_group_count} shots" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/range_live/index.html.heex:113 | ||||
| #: 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:96 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:120 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Stage" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:65 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:122 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:124 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Tag %{container_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:95 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:119 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Unstage" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:64 | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:90 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "View %{ammo_type_name}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:154 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:178 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Clone ammo group of %{ammo_group_count} bullets" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:169 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:193 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:76 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Delete ammo group of %{ammo_group_count} bullets" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:130 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:154 | ||||
| #: lib/cannery_web/live/ammo_type_live/show.html.heex:206 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "View ammo group of %{ammo_group_count} bullets" | ||||
| msgstr "" | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -24,7 +24,7 @@ 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/containers.ex:200 | ||||
| #: lib/cannery/containers.ex:220 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Container must be empty before deleting" | ||||
| msgstr "Caithfidh an coimeádán a bheidh follamh roimh scriosadh" | ||||
| @@ -70,6 +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/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 | ||||
| @@ -114,32 +115,27 @@ msgstr "Níl cead agaibh" | ||||
| msgid "User confirmation link is invalid or it has expired." | ||||
| msgstr "Tá nasc an úsáideoir a deimhnigh neamhbailí nó as dáta." | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:18 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "You are not authorized to view this page" | ||||
| msgstr "Níl cead agaibh féachaint ar an leathanach seo" | ||||
|  | ||||
| #: lib/cannery_web/controllers/user_auth.ex:177 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "You are not authorized to view this page." | ||||
| msgstr "Níl cead agaibh féachaint ar an leathanach seo." | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:144 | ||||
| #: lib/cannery/accounts/user.ex:145 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "did not change" | ||||
| msgstr "Níor athraigh sé" | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:165 | ||||
| #: lib/cannery/accounts/user.ex:166 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "does not match password" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:202 | ||||
| #: lib/cannery/accounts/user.ex:203 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "is not valid" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/accounts/user.ex:99 | ||||
| #: lib/cannery/accounts/user.ex:100 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "must have the @ sign and no spaces" | ||||
| msgstr "" | ||||
| @@ -175,7 +171,7 @@ msgstr "" | ||||
| msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/ammo.ex:1015 | ||||
| #: lib/cannery/ammo.ex:1123 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Invalid multiplier" | ||||
| msgstr "" | ||||
| @@ -190,27 +186,27 @@ msgstr "" | ||||
| msgid "Your browser does not support the canvas element." | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:72 | ||||
| #: lib/cannery/activity_log/shot_group.ex:74 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Please select a valid user and ammo pack" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:86 | ||||
| #: lib/cannery/activity_log/shot_group.ex:88 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Ammo left can be at most %{count} rounds" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:82 | ||||
| #: lib/cannery/activity_log/shot_group.ex:84 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Ammo left must be at least 0" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:119 | ||||
| #: lib/cannery/activity_log/shot_group.ex:122 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "Count can be at most %{count} shots" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery/activity_log/shot_group.ex:78 | ||||
| #: lib/cannery/activity_log/shot_group.ex:80 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "can't be blank" | ||||
| msgstr "" | ||||
|   | ||||
| @@ -30,7 +30,7 @@ msgid "%{name} created successfully" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.ex:72 | ||||
| #: lib/cannery_web/live/ammo_type_live/show.ex:54 | ||||
| #: lib/cannery_web/live/ammo_type_live/show.ex:27 | ||||
| #: lib/cannery_web/live/tag_live/index.ex:65 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "%{name} deleted succesfully" | ||||
| @@ -62,14 +62,14 @@ msgid "Are you sure you want to delete %{email}? This action is permanent!" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:99 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:155 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:52 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:157 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:45 | ||||
| #: lib/cannery_web/live/tag_live/index.html.heex:63 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Are you sure you want to delete %{name}?" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:167 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:191 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:74 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Are you sure you want to delete this ammo?" | ||||
| @@ -120,12 +120,12 @@ msgstr "" | ||||
| msgid "Please check your email to verify your account" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/components/add_shot_group_component.html.heex:58 | ||||
| #: 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:160 | ||||
| #: lib/cannery_web/live/container_live/form_component.html.heex:57 | ||||
| #: lib/cannery_web/live/invite_live/form_component.html.heex:34 | ||||
| #: lib/cannery_web/live/range_live/form_component.html.heex:46 | ||||
| #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:351 | ||||
| #: 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/range_live/form_component.html.heex:47 | ||||
| #: lib/cannery_web/live/tag_live/form_component.html.heex:39 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Saving..." | ||||
| @@ -167,7 +167,7 @@ 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/range_live/index.html.heex:127 | ||||
| #: 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 "" | ||||
| @@ -193,7 +193,7 @@ msgstr "" | ||||
| msgid "Ammo moved to %{name} successfully" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:126 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:116 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Copied to clipboard" | ||||
| msgstr "" | ||||
| @@ -203,8 +203,8 @@ msgstr "" | ||||
| msgid "%{name} removed successfully" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:18 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:28 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:10 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:20 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "You'll need to" | ||||
| msgstr "" | ||||
| @@ -224,7 +224,7 @@ msgstr "" | ||||
| msgid "Language updated successfully." | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.ex:89 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.ex:94 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.ex:55 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Ammo deleted succesfully" | ||||
| @@ -250,7 +250,7 @@ msgstr[2] "" | ||||
| msgstr[3] "" | ||||
| msgstr[4] "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:96 | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:122 | ||||
| #: lib/cannery_web/live/ammo_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!" | ||||
| @@ -261,27 +261,27 @@ msgstr "" | ||||
| msgid "Register to setup Cannery" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:53 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:43 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{invite_name} deleted succesfully" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:114 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:104 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{invite_name} disabled succesfully" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:90 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:80 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{invite_name} enabled succesfully" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:68 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:58 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{invite_name} updated succesfully" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:135 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:125 | ||||
| #, elixir-autogen, elixir-format, fuzzy | ||||
| msgid "%{user_email} deleted succesfully" | ||||
| msgstr "" | ||||
|   | ||||
| @@ -19,7 +19,7 @@ msgid "%{name} created successfully" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.ex:72 | ||||
| #: lib/cannery_web/live/ammo_type_live/show.ex:54 | ||||
| #: lib/cannery_web/live/ammo_type_live/show.ex:27 | ||||
| #: lib/cannery_web/live/tag_live/index.ex:65 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "%{name} deleted succesfully" | ||||
| @@ -51,14 +51,14 @@ msgid "Are you sure you want to delete %{email}? This action is permanent!" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:99 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:155 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:52 | ||||
| #: lib/cannery_web/live/container_live/index.html.heex:157 | ||||
| #: lib/cannery_web/live/container_live/show.html.heex:45 | ||||
| #: lib/cannery_web/live/tag_live/index.html.heex:63 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Are you sure you want to delete %{name}?" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:167 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:191 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.html.heex:74 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Are you sure you want to delete this ammo?" | ||||
| @@ -109,12 +109,12 @@ msgstr "" | ||||
| msgid "Please check your email to verify your account" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/components/add_shot_group_component.html.heex:58 | ||||
| #: 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:160 | ||||
| #: lib/cannery_web/live/container_live/form_component.html.heex:57 | ||||
| #: lib/cannery_web/live/invite_live/form_component.html.heex:34 | ||||
| #: lib/cannery_web/live/range_live/form_component.html.heex:46 | ||||
| #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:351 | ||||
| #: 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/range_live/form_component.html.heex:47 | ||||
| #: lib/cannery_web/live/tag_live/form_component.html.heex:39 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Saving..." | ||||
| @@ -156,7 +156,7 @@ 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/range_live/index.html.heex:127 | ||||
| #: 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 "" | ||||
| @@ -182,7 +182,7 @@ msgstr "" | ||||
| msgid "Ammo moved to %{name} successfully" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:126 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:116 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Copied to clipboard" | ||||
| msgstr "" | ||||
| @@ -192,8 +192,8 @@ msgstr "" | ||||
| msgid "%{name} removed successfully" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:18 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:28 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:10 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.html.heex:20 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "You'll need to" | ||||
| msgstr "" | ||||
| @@ -213,7 +213,7 @@ msgstr "" | ||||
| msgid "Language updated successfully." | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_group_live/index.ex:89 | ||||
| #: lib/cannery_web/live/ammo_group_live/index.ex:94 | ||||
| #: lib/cannery_web/live/ammo_group_live/show.ex:55 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "Ammo deleted succesfully" | ||||
| @@ -236,7 +236,7 @@ msgid_plural "Ammo added successfully" | ||||
| msgstr[0] "" | ||||
| msgstr[1] "" | ||||
|  | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:96 | ||||
| #: lib/cannery_web/live/ammo_type_live/index.html.heex:122 | ||||
| #: lib/cannery_web/live/ammo_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!" | ||||
| @@ -247,27 +247,27 @@ msgstr "" | ||||
| msgid "Register to setup Cannery" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:53 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:43 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "%{invite_name} deleted succesfully" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:114 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:104 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "%{invite_name} disabled succesfully" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:90 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:80 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "%{invite_name} enabled succesfully" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:68 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:58 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "%{invite_name} updated succesfully" | ||||
| msgstr "" | ||||
|  | ||||
| #: lib/cannery_web/live/invite_live/index.ex:135 | ||||
| #: lib/cannery_web/live/invite_live/index.ex:125 | ||||
| #, elixir-autogen, elixir-format | ||||
| msgid "%{user_email} deleted succesfully" | ||||
| msgstr "" | ||||
|   | ||||
| @@ -2,7 +2,7 @@ defmodule Cannery.Repo.Migrations.AddLocaleSetting do | ||||
|   use Ecto.Migration | ||||
|  | ||||
|   def change do | ||||
|     alter table("users") do | ||||
|     alter table(:users) do | ||||
|       add :locale, :string | ||||
|     end | ||||
|   end | ||||
|   | ||||
| @@ -0,0 +1,98 @@ | ||||
| defmodule Cannery.Repo.Migrations.AddAmmoTypeTypesAndShotgunFields do | ||||
|   use Ecto.Migration | ||||
|  | ||||
|   def change do | ||||
|     alter table(:ammo_types) do | ||||
|       # rifle/shotgun/pistol | ||||
|       add :type, :string, default: "rifle" | ||||
|  | ||||
|       add :wadding, :string | ||||
|       # target/bird/buck/slug/special | ||||
|       add :shot_type, :string | ||||
|       add :shot_material, :string | ||||
|       add :shot_size, :string | ||||
|       add :unfired_length, :string | ||||
|       add :brass_height, :string | ||||
|       add :chamber_size, :string | ||||
|       add :load_grains, :integer | ||||
|       add :shot_charge_weight, :string | ||||
|       add :dram_equivalent, :string | ||||
|     end | ||||
|  | ||||
|     create index(:ammo_types, [:type]) | ||||
|  | ||||
|     execute(&add_fields_to_search/0, &remove_fields_from_search/0) | ||||
|   end | ||||
|  | ||||
|   defp add_fields_to_search() do | ||||
|     execute """ | ||||
|     ALTER TABLE ammo_types | ||||
|       ALTER 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', coalesce("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') | ||||
|         setwe | ||||
|       ) STORED | ||||
|     """ | ||||
|   end | ||||
|  | ||||
|   defp remove_fields_from_search() do | ||||
|     execute """ | ||||
|     ALTER TABLE ammo_types | ||||
|       ALTER 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("bullet_type", '')), 'C') || | ||||
|         setweight(to_tsvector('english', coalesce("bullet_core", '')), 'C') || | ||||
|         setweight(to_tsvector('english', coalesce("cartridge", '')), 'C') || | ||||
|         setweight(to_tsvector('english', coalesce("caliber", '')), 'C') || | ||||
|         setweight(to_tsvector('english', coalesce("case_material", '')), 'C') || | ||||
|         setweight(to_tsvector('english', coalesce("jacket_type", '')), 'C') || | ||||
|         setweight(to_tsvector('english', immutable_to_string("muzzle_velocity", '')), 'C') || | ||||
|         setweight(to_tsvector('english', coalesce("powder_type", '')), 'C') || | ||||
|         setweight(to_tsvector('english', immutable_to_string("powder_grains_per_charge", '')), 'C') || | ||||
|         setweight(to_tsvector('english', immutable_to_string("grains", '')), 'C') || | ||||
|         setweight(to_tsvector('english', coalesce("pressure", '')), 'C') || | ||||
|         setweight(to_tsvector('english', coalesce("primer_type", '')), 'C') || | ||||
|         setweight(to_tsvector('english', coalesce("firing_type", '')), 'C') || | ||||
|         setweight(to_tsvector('english', boolean_to_string("tracer", 'tracer', '')), 'C') || | ||||
|         setweight(to_tsvector('english', boolean_to_string("incendiary", 'incendiary', '')), 'C') || | ||||
|         setweight(to_tsvector('english', boolean_to_string("blank", 'blank', '')), 'C') || | ||||
|         setweight(to_tsvector('english', boolean_to_string("corrosive", 'corrosive', '')), 'C') || | ||||
|         setweight(to_tsvector('english', coalesce("manufacturer", '')), 'D') || | ||||
|         setweight(to_tsvector('english', coalesce("upc", '')), 'D') | ||||
|       ) STORED | ||||
|     """ | ||||
|   end | ||||
| end | ||||
| @@ -0,0 +1,7 @@ | ||||
| defmodule Cannery.Repo.Migrations.RenameTypeToClass do | ||||
|   use Ecto.Migration | ||||
|  | ||||
|   def change do | ||||
|     rename table(:ammo_types), :type, to: :class | ||||
|   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,12 +5,7 @@ defmodule Cannery.ActivityLogTest do | ||||
|  | ||||
|   use Cannery.DataCase | ||||
|   import Cannery.Fixtures | ||||
|  | ||||
|   alias Cannery.{ | ||||
|     ActivityLog, | ||||
|     ActivityLog.ShotGroup, | ||||
|     Ammo | ||||
|   } | ||||
|   alias Cannery.{ActivityLog, ActivityLog.ShotGroup, Ammo} | ||||
|  | ||||
|   @moduletag :activity_log_test | ||||
|  | ||||
| @@ -21,10 +16,10 @@ defmodule Cannery.ActivityLogTest do | ||||
|       ammo_type = ammo_type_fixture(current_user) | ||||
|  | ||||
|       {1, [%{id: ammo_group_id} = ammo_group]} = | ||||
|         ammo_group_fixture(%{"count" => 25}, ammo_type, container, current_user) | ||||
|         ammo_group_fixture(%{count: 25}, ammo_type, container, current_user) | ||||
|  | ||||
|       shot_group = | ||||
|         %{"count" => 5, "date" => ~N[2022-02-13 03:17:00], "notes" => "some notes"} | ||||
|         %{count: 5, date: ~N[2022-02-13 03:17:00], notes: "some notes"} | ||||
|         |> shot_group_fixture(current_user, ammo_group) | ||||
|  | ||||
|       ammo_group = ammo_group_id |> Ammo.get_ammo_group!(current_user) | ||||
| @@ -38,50 +33,6 @@ defmodule Cannery.ActivityLogTest do | ||||
|       ] | ||||
|     end | ||||
|  | ||||
|     test "list_shot_groups/1 returns all shot_groups", | ||||
|          %{shot_group: shot_group, current_user: current_user} do | ||||
|       assert ActivityLog.list_shot_groups(current_user) == [shot_group] | ||||
|     end | ||||
|  | ||||
|     test "list_shot_groups/2 returns relevant shot_groups for a user", %{ | ||||
|       ammo_type: ammo_type, | ||||
|       ammo_group: ammo_group, | ||||
|       container: container, | ||||
|       current_user: current_user | ||||
|     } do | ||||
|       shot_group_a = shot_group_fixture(%{"notes" => "amazing"}, current_user, ammo_group) | ||||
|  | ||||
|       {1, [another_ammo_group]} = | ||||
|         ammo_group_fixture(%{"notes" => "stupendous"}, ammo_type, container, current_user) | ||||
|  | ||||
|       shot_group_b = shot_group_fixture(current_user, another_ammo_group) | ||||
|  | ||||
|       another_ammo_type = ammo_type_fixture(%{"name" => "fabulous ammo"}, current_user) | ||||
|  | ||||
|       {1, [yet_another_ammo_group]} = | ||||
|         ammo_group_fixture(another_ammo_type, container, current_user) | ||||
|  | ||||
|       shot_group_c = shot_group_fixture(current_user, yet_another_ammo_group) | ||||
|  | ||||
|       random_user = user_fixture() | ||||
|       random_container = container_fixture(random_user) | ||||
|       random_ammo_type = ammo_type_fixture(random_user) | ||||
|  | ||||
|       {1, [random_ammo_group]} = | ||||
|         ammo_group_fixture(random_ammo_type, random_container, random_user) | ||||
|  | ||||
|       _shouldnt_return = shot_group_fixture(random_user, random_ammo_group) | ||||
|  | ||||
|       # notes | ||||
|       assert ActivityLog.list_shot_groups("amazing", current_user) == [shot_group_a] | ||||
|  | ||||
|       # ammo group attributes | ||||
|       assert ActivityLog.list_shot_groups("stupendous", current_user) == [shot_group_b] | ||||
|  | ||||
|       # ammo type attributes | ||||
|       assert ActivityLog.list_shot_groups("fabulous", current_user) == [shot_group_c] | ||||
|     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 | ||||
| @@ -98,7 +49,7 @@ defmodule Cannery.ActivityLogTest do | ||||
|  | ||||
|     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"} | ||||
|       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) | ||||
| @@ -113,7 +64,7 @@ defmodule Cannery.ActivityLogTest do | ||||
|            current_user: current_user, | ||||
|            ammo_group: %{id: ammo_group_id, count: org_count} = ammo_group | ||||
|          } 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) | ||||
| @@ -126,7 +77,7 @@ defmodule Cannery.ActivityLogTest do | ||||
|  | ||||
|     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"} | ||||
|       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) | ||||
| @@ -136,12 +87,12 @@ defmodule Cannery.ActivityLogTest do | ||||
|       assert ammo_group.count == 0 | ||||
|  | ||||
|       assert {:error, %Ecto.Changeset{}} = | ||||
|                ActivityLog.create_shot_group(%{"count" => 1}, current_user, ammo_group) | ||||
|                ActivityLog.create_shot_group(%{count: 1}, current_user, ammo_group) | ||||
|     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} | ||||
|       invalid_params = %{count: nil, date: nil, notes: nil} | ||||
|  | ||||
|       assert {:error, %Ecto.Changeset{}} = | ||||
|                ActivityLog.create_shot_group(invalid_params, current_user, ammo_group) | ||||
| @@ -157,9 +108,9 @@ defmodule Cannery.ActivityLogTest do | ||||
|                ActivityLog.update_shot_group( | ||||
|                  shot_group, | ||||
|                  %{ | ||||
|                    "count" => 10, | ||||
|                    "date" => ~D[2022-02-13], | ||||
|                    "notes" => "some updated notes" | ||||
|                    count: 10, | ||||
|                    date: ~D[2022-02-13], | ||||
|                    notes: "some updated notes" | ||||
|                  }, | ||||
|                  current_user | ||||
|                ) | ||||
| @@ -175,9 +126,9 @@ defmodule Cannery.ActivityLogTest do | ||||
|                ActivityLog.update_shot_group( | ||||
|                  shot_group, | ||||
|                  %{ | ||||
|                    "count" => 25, | ||||
|                    "date" => ~D[2022-02-13], | ||||
|                    "notes" => "some updated notes" | ||||
|                    count: 25, | ||||
|                    date: ~D[2022-02-13], | ||||
|                    notes: "some updated notes" | ||||
|                  }, | ||||
|                  current_user | ||||
|                ) | ||||
| @@ -193,14 +144,14 @@ defmodule Cannery.ActivityLogTest do | ||||
|       assert {:error, %Ecto.Changeset{}} = | ||||
|                ActivityLog.update_shot_group( | ||||
|                  shot_group, | ||||
|                  %{"count" => 26, "date" => nil, "notes" => nil}, | ||||
|                  %{count: 26, date: nil, notes: nil}, | ||||
|                  current_user | ||||
|                ) | ||||
|  | ||||
|       assert {:error, %Ecto.Changeset{}} = | ||||
|                ActivityLog.update_shot_group( | ||||
|                  shot_group, | ||||
|                  %{"count" => -1, "date" => nil, "notes" => nil}, | ||||
|                  %{count: -1, date: nil, notes: nil}, | ||||
|                  current_user | ||||
|                ) | ||||
|  | ||||
| @@ -228,10 +179,10 @@ defmodule Cannery.ActivityLogTest do | ||||
|       assert 0 = another_ammo_group |> ActivityLog.get_used_count(current_user) | ||||
|       assert 5 = ammo_group |> ActivityLog.get_used_count(current_user) | ||||
|  | ||||
|       shot_group_fixture(%{"count" => 15}, current_user, ammo_group) | ||||
|       shot_group_fixture(%{count: 15}, current_user, ammo_group) | ||||
|       assert 20 = ammo_group |> ActivityLog.get_used_count(current_user) | ||||
|  | ||||
|       shot_group_fixture(%{"count" => 10}, current_user, ammo_group) | ||||
|       shot_group_fixture(%{count: 10}, current_user, ammo_group) | ||||
|       assert 30 = ammo_group |> ActivityLog.get_used_count(current_user) | ||||
|  | ||||
|       {1, [another_ammo_group]} = ammo_group_fixture(ammo_type, container, current_user) | ||||
| @@ -250,17 +201,17 @@ defmodule Cannery.ActivityLogTest do | ||||
|       assert %{ammo_group_id => 5} == | ||||
|                [ammo_group, another_ammo_group] |> ActivityLog.get_used_counts(current_user) | ||||
|  | ||||
|       shot_group_fixture(%{"count" => 5}, current_user, another_ammo_group) | ||||
|       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_group_fixture(%{"count" => 15}, current_user, ammo_group) | ||||
|       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_group_fixture(%{"count" => 10}, current_user, ammo_group) | ||||
|       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 | ||||
| @@ -277,10 +228,10 @@ defmodule Cannery.ActivityLogTest do | ||||
|       assert another_ammo_group |> ActivityLog.get_last_used_date(current_user) |> is_nil() | ||||
|       assert ^date = ammo_group |> ActivityLog.get_last_used_date(current_user) | ||||
|  | ||||
|       %{date: date} = shot_group_fixture(%{"date" => ~D[2022-11-10]}, current_user, ammo_group) | ||||
|       %{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_group_fixture(%{"date" => ~D[2022-11-11]}, current_user, ammo_group) | ||||
|       %{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) | ||||
|     end | ||||
|  | ||||
| @@ -298,7 +249,7 @@ defmodule Cannery.ActivityLogTest do | ||||
|       assert %{ammo_group_id => date} == | ||||
|                [ammo_group, another_ammo_group] |> ActivityLog.get_last_used_dates(current_user) | ||||
|  | ||||
|       shot_group_fixture(%{"date" => ~D[2022-11-09]}, current_user, another_ammo_group) | ||||
|       shot_group_fixture(%{date: ~D[2022-11-09]}, current_user, another_ammo_group) | ||||
|  | ||||
|       # setting initial date | ||||
|       last_used_shot_groups = | ||||
| @@ -308,7 +259,7 @@ defmodule Cannery.ActivityLogTest do | ||||
|       assert %{^another_ammo_group_id => ~D[2022-11-09]} = last_used_shot_groups | ||||
|  | ||||
|       # setting another date | ||||
|       shot_group_fixture(%{"date" => ~D[2022-11-10]}, current_user, ammo_group) | ||||
|       shot_group_fixture(%{date: ~D[2022-11-10]}, current_user, ammo_group) | ||||
|  | ||||
|       last_used_shot_groups = | ||||
|         [ammo_group, another_ammo_group] |> ActivityLog.get_last_used_dates(current_user) | ||||
| @@ -317,7 +268,7 @@ defmodule Cannery.ActivityLogTest do | ||||
|       assert %{^another_ammo_group_id => ~D[2022-11-09]} = last_used_shot_groups | ||||
|  | ||||
|       # setting yet another date | ||||
|       shot_group_fixture(%{"date" => ~D[2022-11-11]}, current_user, ammo_group) | ||||
|       shot_group_fixture(%{date: ~D[2022-11-11]}, current_user, ammo_group) | ||||
|  | ||||
|       last_used_shot_groups = | ||||
|         [ammo_group, another_ammo_group] |> ActivityLog.get_last_used_dates(current_user) | ||||
| @@ -332,10 +283,10 @@ defmodule Cannery.ActivityLogTest do | ||||
|       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) | ||||
|  | ||||
|       shot_group_fixture(%{"count" => 5}, current_user, ammo_group) | ||||
|       shot_group_fixture(%{count: 5}, current_user, ammo_group) | ||||
|       assert 10 = ammo_type |> ActivityLog.get_used_count_for_ammo_type(current_user) | ||||
|  | ||||
|       shot_group_fixture(%{"count" => 1}, current_user, ammo_group) | ||||
|       shot_group_fixture(%{count: 1}, current_user, ammo_group) | ||||
|       assert 11 = ammo_type |> ActivityLog.get_used_count_for_ammo_type(current_user) | ||||
|     end | ||||
|  | ||||
| @@ -353,7 +304,7 @@ defmodule Cannery.ActivityLogTest do | ||||
|                |> ActivityLog.get_used_count_for_ammo_types(current_user) | ||||
|  | ||||
|       # use generated ammo group | ||||
|       shot_group_fixture(%{"count" => 5}, current_user, ammo_group) | ||||
|       shot_group_fixture(%{count: 5}, current_user, ammo_group) | ||||
|  | ||||
|       used_counts = | ||||
|         [ammo_type, another_ammo_type] |> ActivityLog.get_used_count_for_ammo_types(current_user) | ||||
| @@ -362,7 +313,7 @@ defmodule Cannery.ActivityLogTest do | ||||
|       assert %{^another_ammo_type_id => 5} = used_counts | ||||
|  | ||||
|       # use generated ammo group again | ||||
|       shot_group_fixture(%{"count" => 1}, current_user, ammo_group) | ||||
|       shot_group_fixture(%{count: 1}, current_user, ammo_group) | ||||
|  | ||||
|       used_counts = | ||||
|         [ammo_type, another_ammo_type] |> ActivityLog.get_used_count_for_ammo_types(current_user) | ||||
| @@ -371,4 +322,99 @@ defmodule Cannery.ActivityLogTest do | ||||
|       assert %{^another_ammo_type_id => 6} = used_counts | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   describe "list_shot_groups/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) | ||||
|  | ||||
|       [ | ||||
|         current_user: current_user, | ||||
|         container: container, | ||||
|         ammo_type: ammo_type, | ||||
|         ammo_group: ammo_group | ||||
|       ] | ||||
|     end | ||||
|  | ||||
|     test "list_shot_groups/3 returns relevant shot_groups for a type", | ||||
|          %{current_user: current_user, container: container} do | ||||
|       other_user = user_fixture() | ||||
|       other_container = container_fixture(other_user) | ||||
|  | ||||
|       for class <- ["rifle", "shotgun", "pistol"] do | ||||
|         other_ammo_type = ammo_type_fixture(%{class: class}, other_user) | ||||
|         {1, [other_ammo_group]} = ammo_group_fixture(other_ammo_type, other_container, other_user) | ||||
|         shot_group_fixture(other_user, other_ammo_group) | ||||
|       end | ||||
|  | ||||
|       rifle_ammo_type = ammo_type_fixture(%{class: :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) | ||||
|  | ||||
|       shotgun_ammo_type = ammo_type_fixture(%{class: :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) | ||||
|  | ||||
|       pistol_ammo_type = ammo_type_fixture(%{class: :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) | ||||
|  | ||||
|       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) | ||||
|  | ||||
|       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_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 | ||||
|     end | ||||
|  | ||||
|     test "list_shot_groups/3 returns relevant shot_groups for a search", %{ | ||||
|       ammo_type: ammo_type, | ||||
|       ammo_group: ammo_group, | ||||
|       container: container, | ||||
|       current_user: current_user | ||||
|     } do | ||||
|       shot_group_a = shot_group_fixture(%{notes: "amazing"}, current_user, ammo_group) | ||||
|  | ||||
|       {1, [another_ammo_group]} = | ||||
|         ammo_group_fixture(%{notes: "stupendous"}, ammo_type, container, current_user) | ||||
|  | ||||
|       shot_group_b = shot_group_fixture(current_user, another_ammo_group) | ||||
|  | ||||
|       another_ammo_type = ammo_type_fixture(%{name: "fabulous ammo"}, current_user) | ||||
|  | ||||
|       {1, [yet_another_ammo_group]} = | ||||
|         ammo_group_fixture(another_ammo_type, container, current_user) | ||||
|  | ||||
|       shot_group_c = shot_group_fixture(current_user, yet_another_ammo_group) | ||||
|  | ||||
|       another_user = user_fixture() | ||||
|       another_container = container_fixture(another_user) | ||||
|       another_ammo_type = ammo_type_fixture(another_user) | ||||
|  | ||||
|       {1, [another_ammo_group]} = | ||||
|         ammo_group_fixture(another_ammo_type, another_container, another_user) | ||||
|  | ||||
|       _shouldnt_return = shot_group_fixture(another_user, another_ammo_group) | ||||
|  | ||||
|       # notes | ||||
|       assert ActivityLog.list_shot_groups("amazing", :all, current_user) == [shot_group_a] | ||||
|  | ||||
|       # ammo group attributes | ||||
|       assert ActivityLog.list_shot_groups("stupendous", :all, current_user) == [shot_group_b] | ||||
|  | ||||
|       # ammo type attributes | ||||
|       assert ActivityLog.list_shot_groups("fabulous", :all, current_user) == [shot_group_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" => "some bg-color", | ||||
|     "name" => "some name", | ||||
|     "text_color" => "some text-color" | ||||
|     bg_color: "#100000", | ||||
|     name: "some name", | ||||
|     text_color: "#000000" | ||||
|   } | ||||
|   @update_tag_attrs %{ | ||||
|     "bg_color" => "some updated bg-color", | ||||
|     "name" => "some updated name", | ||||
|     "text_color" => "some updated text-color" | ||||
|     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] | ||||
| @@ -90,14 +89,26 @@ defmodule Cannery.ContainersTest do | ||||
|       assert Containers.list_containers("asajslkdflskdf", current_user) == [] | ||||
|     end | ||||
|  | ||||
|     test "get_container!/1 returns the container with given id", | ||||
|     test "get_container!/2 returns the container with given id", | ||||
|          %{current_user: current_user, container: container} do | ||||
|       assert Containers.get_container!(container.id, current_user) == container | ||||
|       assert_raise KeyError, fn -> Containers.get_container!(current_user.id, current_user) end | ||||
|     end | ||||
|  | ||||
|     test "create_container/1 with valid data creates a container", %{current_user: current_user} do | ||||
|     test "get_containers/2 returns the container with given id", | ||||
|          %{current_user: current_user, container: %{id: container_id} = container} do | ||||
|       assert %{container_id => container} == | ||||
|                Containers.get_containers([container_id], current_user) | ||||
|  | ||||
|       %{id: another_container_id} = another_container = container_fixture(current_user) | ||||
|       containers = [container_id, another_container_id] |> Containers.get_containers(current_user) | ||||
|       assert %{^container_id => ^container} = containers | ||||
|       assert %{^another_container_id => ^another_container} = containers | ||||
|     end | ||||
|  | ||||
|     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" | ||||
| @@ -106,12 +117,12 @@ defmodule Cannery.ContainersTest do | ||||
|       assert container.user_id == current_user.id | ||||
|     end | ||||
|  | ||||
|     test "create_container/1 with invalid data returns error changeset", | ||||
|     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/2 with valid data updates the container", | ||||
|     test "update_container/3 with valid data updates the container", | ||||
|          %{current_user: current_user, container: container} do | ||||
|       assert {:ok, %Container{} = container} = | ||||
|                Containers.update_container(container, current_user, @update_attrs) | ||||
| @@ -122,7 +133,7 @@ defmodule Cannery.ContainersTest do | ||||
|       assert container.type == "some updated type" | ||||
|     end | ||||
|  | ||||
|     test "update_container/2 with invalid data returns error changeset", | ||||
|     test "update_container/3 with invalid data returns error changeset", | ||||
|          %{current_user: current_user, container: container} do | ||||
|       assert {:error, %Changeset{}} = | ||||
|                Containers.update_container(container, current_user, @invalid_attrs) | ||||
| @@ -130,11 +141,11 @@ defmodule Cannery.ContainersTest do | ||||
|       assert container == Containers.get_container!(container.id, current_user) | ||||
|     end | ||||
|  | ||||
|     test "delete_container/1 deletes the container", | ||||
|     test "delete_container/2 deletes the container", | ||||
|          %{current_user: current_user, container: container} do | ||||
|       assert {:ok, %Container{}} = Containers.delete_container(container, current_user) | ||||
|  | ||||
|       assert_raise Ecto.NoResultsError, fn -> | ||||
|       assert_raise KeyError, fn -> | ||||
|         Containers.get_container!(container.id, current_user) | ||||
|       end | ||||
|     end | ||||
| @@ -151,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] | ||||
| @@ -168,36 +174,36 @@ defmodule Cannery.ContainersTest do | ||||
|       assert Containers.list_tags("hollows", current_user) == [tag_b] | ||||
|     end | ||||
|  | ||||
|     test "get_tag!/1 returns the tag with given id", %{tag: tag, current_user: current_user} do | ||||
|     test "get_tag!/2 returns the tag with given id", %{tag: tag, current_user: current_user} do | ||||
|       assert Containers.get_tag!(tag.id, current_user) == tag | ||||
|     end | ||||
|  | ||||
|     test "create_tag/1 with valid data creates a tag", %{current_user: current_user} do | ||||
|     test "create_tag/2 with valid data creates a tag", %{current_user: current_user} do | ||||
|       assert {:ok, %Tag{} = tag} = Containers.create_tag(@valid_tag_attrs, current_user) | ||||
|       assert tag.bg_color == "some bg-color" | ||||
|       assert tag.bg_color == "#100000" | ||||
|       assert tag.name == "some name" | ||||
|       assert tag.text_color == "some text-color" | ||||
|       assert tag.text_color == "#000000" | ||||
|     end | ||||
|  | ||||
|     test "create_tag/1 with invalid data returns error changeset", | ||||
|     test "create_tag/2 with invalid data returns error changeset", | ||||
|          %{current_user: current_user} do | ||||
|       assert {:error, %Changeset{}} = Containers.create_tag(@invalid_tag_attrs, current_user) | ||||
|     end | ||||
|  | ||||
|     test "update_tag/2 with valid data updates the tag", %{tag: tag, current_user: current_user} do | ||||
|     test "update_tag/3 with valid data updates the tag", %{tag: tag, current_user: current_user} do | ||||
|       assert {:ok, %Tag{} = tag} = Containers.update_tag(tag, @update_tag_attrs, current_user) | ||||
|       assert tag.bg_color == "some updated bg-color" | ||||
|       assert tag.bg_color == "#100001" | ||||
|       assert tag.name == "some updated name" | ||||
|       assert tag.text_color == "some updated text-color" | ||||
|       assert tag.text_color == "#000001" | ||||
|     end | ||||
|  | ||||
|     test "update_tag/2 with invalid data returns error changeset", | ||||
|     test "update_tag/3 with invalid data returns error changeset", | ||||
|          %{tag: tag, current_user: current_user} do | ||||
|       assert {:error, %Changeset{}} = Containers.update_tag(tag, @invalid_tag_attrs, current_user) | ||||
|       assert tag == Containers.get_tag!(tag.id, current_user) | ||||
|     end | ||||
|  | ||||
|     test "delete_tag/1 deletes the tag", %{tag: tag, current_user: current_user} do | ||||
|     test "delete_tag/2 deletes the tag", %{tag: tag, current_user: current_user} do | ||||
|       assert {:ok, %Tag{}} = Containers.delete_tag(tag, current_user) | ||||
|       assert_raise Ecto.NoResultsError, fn -> Containers.get_tag!(tag.id, current_user) 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 | ||||
|  | ||||
|   | ||||
| @@ -5,43 +5,40 @@ defmodule CanneryWeb.AmmoGroupLiveTest do | ||||
|  | ||||
|   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} | ||||
|   @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} | ||||
|   @ammo_group_create_limit 10_000 | ||||
|   @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" | ||||
|   } | ||||
|   @shot_group_invalid_attrs %{ammo_left: nil, count: nil, notes: nil} | ||||
|   @empty_attrs %{ | ||||
|     "price_paid" => 50, | ||||
|     "count" => 20 | ||||
|     price_paid: 50, | ||||
|     count: 20 | ||||
|   } | ||||
|   @shot_group_attrs %{ | ||||
|     "price_paid" => 50, | ||||
|     "count" => 20 | ||||
|     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} | ||||
|     [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} | ||||
|     [ammo_group: ammo_group, shot_group: shot_group] | ||||
|   end | ||||
|  | ||||
|   defp create_empty_ammo_group(%{ | ||||
| @@ -52,7 +49,7 @@ defmodule CanneryWeb.AmmoGroupLiveTest 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} | ||||
|     [empty_ammo_group: ammo_group, shot_group: shot_group] | ||||
|   end | ||||
|  | ||||
|   describe "Index of ammo group" do | ||||
| @@ -60,12 +57,65 @@ defmodule CanneryWeb.AmmoGroupLiveTest do | ||||
|  | ||||
|     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" | ||||
|       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(%{class: :rifle}, current_user) | ||||
|       {1, [rifle_ammo_group]} = ammo_group_fixture(rifle_type, container, current_user) | ||||
|       shotgun_type = ammo_type_fixture(%{class: :shotgun}, current_user) | ||||
|       {1, [shotgun_ammo_group]} = ammo_group_fixture(shotgun_type, container, current_user) | ||||
|       pistol_type = ammo_type_fixture(%{class: :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_class"]/) | ||||
|         |> render_change(ammo_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 | ||||
|  | ||||
|       html = | ||||
|         index_live | ||||
|         |> form(~s/form[phx-change="change_class"]/) | ||||
|         |> render_change(ammo_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 | ||||
|  | ||||
|       html = | ||||
|         index_live | ||||
|         |> form(~s/form[phx-change="change_class"]/) | ||||
|         |> render_change(ammo_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 | ||||
|  | ||||
|       html = | ||||
|         index_live | ||||
|         |> form(~s/form[phx-change="change_class"]/) | ||||
|         |> render_change(ammo_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 | ||||
|     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)) | ||||
|  | ||||
| @@ -74,10 +124,9 @@ defmodule CanneryWeb.AmmoGroupLiveTest do | ||||
|       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 | ||||
|              |> form(~s/form[phx-change="search"]/) | ||||
|              |> render_change(search: %{search_term: ammo_group.ammo_type.name}) =~ | ||||
|                ammo_group.ammo_type.name | ||||
|  | ||||
|       assert_patch( | ||||
|         index_live, | ||||
| @@ -85,14 +134,15 @@ defmodule CanneryWeb.AmmoGroupLiveTest do | ||||
|       ) | ||||
|  | ||||
|       refute index_live | ||||
|              |> form(~s/form[phx-change="search"]/, search: %{search_term: "something_else"}) | ||||
|              |> render_change() =~ ammo_group.ammo_type.name | ||||
|              |> form(~s/form[phx-change="search"]/) | ||||
|              |> render_change(search: %{search_term: "something_else"}) =~ | ||||
|                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 | ||||
|              |> form(~s/form[phx-change="search"]/) | ||||
|              |> render_change(search: %{search_term: ""}) =~ ammo_group.ammo_type.name | ||||
|  | ||||
|       assert_patch(index_live, Routes.ammo_group_index_path(conn, :index)) | ||||
|     end | ||||
| @@ -100,85 +150,65 @@ defmodule CanneryWeb.AmmoGroupLiveTest do | ||||
|     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 index_live |> element("a", "Add Ammo") |> render_click() =~ "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") | ||||
|              |> render_change(ammo_group: @invalid_attrs) =~ "can't be blank" | ||||
|  | ||||
|       {:ok, _view, html} = | ||||
|         index_live | ||||
|         |> form("#ammo_group-form", ammo_group: @create_attrs) | ||||
|         |> render_submit() | ||||
|         |> form("#ammo_group-form") | ||||
|         |> render_submit(ammo_group: @create_attrs) | ||||
|         |> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index)) | ||||
|  | ||||
|       assert html =~ dgettext("prompts", "Ammo added successfully") | ||||
|       assert html =~ "42" | ||||
|       assert html =~ "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 index_live |> element("a", "Add Ammo") |> render_click() =~ "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") | ||||
|              |> render_change(ammo_group: @invalid_attrs) =~ "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() | ||||
|         |> form("#ammo_group-form") | ||||
|         |> render_submit(ammo_group: @create_attrs |> Map.put(:multiplier, multiplier)) | ||||
|         |> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index)) | ||||
|  | ||||
|       assert html =~ dgettext("prompts", "Ammo added successfully") | ||||
|       assert Ammo.list_ammo_groups(nil, false, current_user) |> Enum.count() == multiplier + 1 | ||||
|       assert html =~ "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 index_live |> element("a", "Add Ammo") |> render_click() =~ "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") | ||||
|              |> render_change(ammo_group: @invalid_attrs) =~ "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 | ||||
|                ) | ||||
|              |> form("#ammo_group-form") | ||||
|              |> render_submit(ammo_group: @create_attrs |> Map.put(:multiplier, "0")) =~ | ||||
|                "Invalid number of copies, must be between 1 and #{@ammo_group_create_limit}. Was 0" | ||||
|  | ||||
|       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 | ||||
|                ) | ||||
|              |> form("#ammo_group-form") | ||||
|              |> render_submit( | ||||
|                ammo_group: @create_attrs |> Map.put(:multiplier, @ammo_group_create_limit + 1) | ||||
|              ) =~ | ||||
|                "Invalid number of copies, must be between 1 and #{@ammo_group_create_limit}. Was #{@ammo_group_create_limit + 1}" | ||||
|     end | ||||
|  | ||||
|     test "updates ammo_group in listing", %{conn: conn, ammo_group: ammo_group} do | ||||
| @@ -186,23 +216,22 @@ defmodule CanneryWeb.AmmoGroupLiveTest do | ||||
|  | ||||
|       assert index_live | ||||
|              |> element(~s/a[aria-label="Edit ammo group of #{ammo_group.count} bullets"]/) | ||||
|              |> render_click() =~ | ||||
|                gettext("Edit ammo") | ||||
|              |> render_click() =~ "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") | ||||
|       assert index_live | ||||
|              |> form("#ammo_group-form") | ||||
|              |> render_change(ammo_group: @invalid_attrs) =~ "can't be blank" | ||||
|  | ||||
|       {:ok, _view, html} = | ||||
|         index_live | ||||
|         |> form("#ammo_group-form", ammo_group: @update_attrs) | ||||
|         |> render_submit() | ||||
|         |> form("#ammo_group-form") | ||||
|         |> render_submit(ammo_group: @update_attrs) | ||||
|         |> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index)) | ||||
|  | ||||
|       assert html =~ dgettext("prompts", "Ammo updated successfully") | ||||
|       assert html =~ "43" | ||||
|       assert html =~ "Ammo updated successfully" | ||||
|       assert html =~ "\n43\n" | ||||
|     end | ||||
|  | ||||
|     test "clones ammo_group in listing", %{conn: conn, ammo_group: ammo_group} do | ||||
| @@ -213,24 +242,38 @@ defmodule CanneryWeb.AmmoGroupLiveTest do | ||||
|         |> 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 html =~ "Add Ammo" | ||||
|       assert html =~ "$#{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} = | ||||
|       {:ok, _index_live, 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 =~ "42" | ||||
|       assert html =~ gettext("$%{amount}", amount: display_currency(120.5)) | ||||
|       assert html =~ "Ammo added successfully" | ||||
|       assert html =~ "\n42\n" | ||||
|       assert html =~ "$#{display_currency(120.5)}" | ||||
|     end | ||||
|  | ||||
|     test "checks validity when cloning", %{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 =~ "Add Ammo" | ||||
|       assert html =~ "$#{display_currency(120.5)}" | ||||
|  | ||||
|       assert_patch(index_live, Routes.ammo_group_index_path(conn, :clone, ammo_group)) | ||||
|  | ||||
|       assert index_live | ||||
|              |> form("#ammo_group-form") | ||||
|              |> render_change(ammo_group: @invalid_attrs) =~ "can't be blank" | ||||
|     end | ||||
|  | ||||
|     test "clones ammo_group in listing with updates", %{conn: conn, ammo_group: ammo_group} do | ||||
| @@ -241,24 +284,23 @@ defmodule CanneryWeb.AmmoGroupLiveTest do | ||||
|         |> 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 html =~ "Add Ammo" | ||||
|       assert html =~ "$#{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") | ||||
|       assert index_live | ||||
|              |> form("#ammo_group-form") | ||||
|              |> render_change(ammo_group: @invalid_attrs) =~ "can't be blank" | ||||
|  | ||||
|       {:ok, _view, html} = | ||||
|         index_live | ||||
|         |> form("#ammo_group-form", ammo_group: Map.merge(@create_attrs, %{"count" => 43})) | ||||
|         |> render_submit() | ||||
|         |> form("#ammo_group-form") | ||||
|         |> render_submit(ammo_group: @create_attrs |> Map.put(:count, 43)) | ||||
|         |> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index)) | ||||
|  | ||||
|       assert html =~ dgettext("prompts", "Ammo added successfully") | ||||
|       assert html =~ "43" | ||||
|       assert html =~ gettext("$%{amount}", amount: display_currency(120.5)) | ||||
|       assert html =~ "Ammo added successfully" | ||||
|       assert html =~ "\n43\n" | ||||
|       assert html =~ "$#{display_currency(120.5)}" | ||||
|     end | ||||
|  | ||||
|     test "deletes ammo_group in listing", %{conn: conn, ammo_group: ammo_group} do | ||||
| @@ -274,22 +316,20 @@ defmodule CanneryWeb.AmmoGroupLiveTest do | ||||
|     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 index_live |> element("a", "Record shots") |> render_click() =~ "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") | ||||
|       assert index_live | ||||
|              |> form("#shot-group-form") | ||||
|              |> render_change(shot_group: @shot_group_invalid_attrs) =~ "can't be blank" | ||||
|  | ||||
|       {:ok, _view, html} = | ||||
|         index_live | ||||
|         |> form("#shot-group-form", shot_group: @shot_group_create_attrs) | ||||
|         |> render_submit() | ||||
|         |> form("#shot-group-form") | ||||
|         |> render_submit(shot_group: @shot_group_create_attrs) | ||||
|         |> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index)) | ||||
|  | ||||
|       assert html =~ dgettext("prompts", "Shots recorded successfully") | ||||
|       assert html =~ "Shots recorded successfully" | ||||
|     end | ||||
|  | ||||
|     @spec display_currency(float()) :: String.t() | ||||
| @@ -306,29 +346,20 @@ defmodule CanneryWeb.AmmoGroupLiveTest do | ||||
|     } 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)) | ||||
|       assert html =~ "Show used" | ||||
|       refute html =~ "$#{display_currency(50.00)}" | ||||
|  | ||||
|       refute html =~ | ||||
|                "\n" <> | ||||
|                  gettext("%{percentage}%", | ||||
|                    percentage: ammo_group |> Ammo.get_percentage_remaining(current_user) | ||||
|                  ) <> | ||||
|                  "\n" | ||||
|       percentage = ammo_group |> 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 =~ gettext("$%{amount}", amount: display_currency(50.00)) | ||||
|  | ||||
|       assert html =~ | ||||
|                "\n" <> | ||||
|                  gettext("%{percentage}%", | ||||
|                    percentage: ammo_group |> Ammo.get_percentage_remaining(current_user) | ||||
|                  ) <> | ||||
|                  "\n" | ||||
|       assert html =~ "$#{display_currency(50.00)}" | ||||
|       percentage = ammo_group |> Ammo.get_percentage_remaining(current_user) | ||||
|       assert html =~ "\n#{"#{percentage}%"}\n" | ||||
|     end | ||||
|   end | ||||
|  | ||||
| @@ -337,9 +368,8 @@ defmodule CanneryWeb.AmmoGroupLiveTest do | ||||
|  | ||||
|     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 =~ "Show Ammo" | ||||
|       assert html =~ ammo_group.ammo_type.name | ||||
|     end | ||||
|  | ||||
| @@ -348,44 +378,41 @@ defmodule CanneryWeb.AmmoGroupLiveTest do | ||||
|  | ||||
|       assert show_live | ||||
|              |> element(~s/a[aria-label="Edit ammo group of #{ammo_group.count} bullets"]/) | ||||
|              |> render_click() =~ | ||||
|                gettext("Edit Ammo") | ||||
|              |> render_click() =~ "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") | ||||
|       assert show_live | ||||
|              |> form("#ammo_group-form") | ||||
|              |> render_change(ammo_group: @invalid_attrs) =~ "can't be blank" | ||||
|  | ||||
|       {:ok, _view, html} = | ||||
|         show_live | ||||
|         |> form("#ammo_group-form", ammo_group: @update_attrs) | ||||
|         |> render_submit() | ||||
|         |> form("#ammo_group-form") | ||||
|         |> render_submit(ammo_group: @update_attrs) | ||||
|         |> follow_redirect(conn, Routes.ammo_group_show_path(conn, :show, ammo_group)) | ||||
|  | ||||
|       assert html =~ dgettext("prompts", "Ammo updated successfully") | ||||
|       assert html =~ "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 index_live |> element("a", "Record shots") |> render_click() =~ "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") | ||||
|       assert index_live | ||||
|              |> form("#shot-group-form") | ||||
|              |> render_change(shot_group: @shot_group_invalid_attrs) =~ "can't be blank" | ||||
|  | ||||
|       {:ok, _view, html} = | ||||
|         index_live | ||||
|         |> form("#shot-group-form", shot_group: @shot_group_create_attrs) | ||||
|         |> render_submit() | ||||
|         |> form("#shot-group-form") | ||||
|         |> render_submit(shot_group: @shot_group_create_attrs) | ||||
|         |> follow_redirect(conn, Routes.ammo_group_show_path(conn, :show, ammo_group)) | ||||
|  | ||||
|       assert html =~ dgettext("prompts", "Shots recorded successfully") | ||||
|       assert html =~ "Shots recorded successfully" | ||||
|     end | ||||
|   end | ||||
|  | ||||
| @@ -398,25 +425,24 @@ defmodule CanneryWeb.AmmoGroupLiveTest do | ||||
|  | ||||
|       assert index_live | ||||
|              |> element(~s/a[aria-label="Edit shot group of #{shot_group.count} shots"]/) | ||||
|              |> render_click() =~ | ||||
|                gettext("Edit Shot Records") | ||||
|              |> render_click() =~ "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") | ||||
|       assert index_live | ||||
|              |> form("#shot-group-form") | ||||
|              |> render_change(shot_group: @shot_group_invalid_attrs) =~ "can't be blank" | ||||
|  | ||||
|       {:ok, _view, html} = | ||||
|         index_live | ||||
|         |> form("#shot-group-form", shot_group: @shot_group_update_attrs) | ||||
|         |> render_submit() | ||||
|         |> form("#shot-group-form") | ||||
|         |> render_submit(shot_group: @shot_group_update_attrs) | ||||
|         |> follow_redirect(conn, Routes.ammo_group_show_path(conn, :show, ammo_group)) | ||||
|  | ||||
|       assert html =~ dgettext("actions", "Shot records updated successfully") | ||||
|       assert html =~ "Shot records updated successfully" | ||||
|       assert html =~ "some updated notes" | ||||
|     end | ||||
|  | ||||
|   | ||||
| @@ -5,54 +5,51 @@ defmodule CanneryWeb.AmmoTypeLiveTest do | ||||
|  | ||||
|   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 | ||||
|     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 | ||||
|     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 | ||||
|   } | ||||
|   @ammo_group_attrs %{ | ||||
|     "notes" => "some ammo group", | ||||
|     "count" => 20 | ||||
|     notes: "some ammo group", | ||||
|     count: 20 | ||||
|   } | ||||
|   @shot_group_attrs %{ | ||||
|     "notes" => "some shot group", | ||||
|     "count" => 20 | ||||
|     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)} | ||||
|     [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} | ||||
|     [ammo_group: ammo_group, container: container] | ||||
|   end | ||||
|  | ||||
|   defp create_empty_ammo_group(%{ammo_type: ammo_type, current_user: current_user}) do | ||||
| @@ -60,8 +57,7 @@ defmodule CanneryWeb.AmmoTypeLiveTest do | ||||
|     {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} | ||||
|     [ammo_group: ammo_group, container: container, shot_group: shot_group] | ||||
|   end | ||||
|  | ||||
|   describe "Index" do | ||||
| @@ -69,33 +65,81 @@ defmodule CanneryWeb.AmmoTypeLiveTest do | ||||
|  | ||||
|     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 =~ "Catalog" | ||||
|       assert html =~ ammo_type.bullet_type | ||||
|     end | ||||
|  | ||||
|     test "can sort by class", %{conn: conn, current_user: current_user} do | ||||
|       rifle_type = ammo_type_fixture(%{class: :rifle}, current_user) | ||||
|       shotgun_type = ammo_type_fixture(%{class: :shotgun}, current_user) | ||||
|       pistol_type = ammo_type_fixture(%{class: :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_class"]/) | ||||
|         |> render_change(ammo_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(ammo_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(ammo_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(ammo_type: %{class: :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"]/, | ||||
|                search: %{search_term: ammo_type.bullet_type} | ||||
|              ) | ||||
|              |> render_change() =~ ammo_type.bullet_type | ||||
|              |> 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"]/, search: %{search_term: "something_else"}) | ||||
|              |> render_change() =~ ammo_type.bullet_type | ||||
|              |> 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"]/, search: %{search_term: ""}) | ||||
|              |> render_change() =~ ammo_type.bullet_type | ||||
|              |> 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 | ||||
| @@ -103,23 +147,21 @@ defmodule CanneryWeb.AmmoTypeLiveTest do | ||||
|     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 index_live |> element("a", "New Ammo type") |> render_click() =~ "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") | ||||
|       assert index_live | ||||
|              |> form("#ammo_type-form") | ||||
|              |> render_change(ammo_type: @invalid_attrs) =~ "can't be blank" | ||||
|  | ||||
|       {:ok, _view, html} = | ||||
|         index_live | ||||
|         |> form("#ammo_type-form", ammo_type: @create_attrs) | ||||
|         |> render_submit() | ||||
|         |> 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 =~ "#{ammo_type.name} created successfully" | ||||
|       assert html =~ "some bullet_type" | ||||
|     end | ||||
|  | ||||
| @@ -128,22 +170,22 @@ defmodule CanneryWeb.AmmoTypeLiveTest 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) | ||||
|                "Edit #{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") | ||||
|       assert index_live | ||||
|              |> form("#ammo_type-form") | ||||
|              |> render_change(ammo_type: @invalid_attrs) =~ "can't be blank" | ||||
|  | ||||
|       {:ok, _view, html} = | ||||
|         index_live | ||||
|         |> form("#ammo_type-form", ammo_type: @update_attrs) | ||||
|         |> render_submit() | ||||
|         |> 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 =~ "#{ammo_type.name} updated successfully" | ||||
|       assert html =~ "some updated bullet_type" | ||||
|     end | ||||
|  | ||||
| @@ -152,23 +194,23 @@ defmodule CanneryWeb.AmmoTypeLiveTest 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 =~ "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") | ||||
|       assert index_live | ||||
|              |> form("#ammo_type-form") | ||||
|              |> render_change(ammo_type: @invalid_attrs) =~ "can't be blank" | ||||
|  | ||||
|       {:ok, _view, html} = | ||||
|         index_live | ||||
|         |> form("#ammo_type-form", ammo_type: @create_attrs) | ||||
|         |> render_submit() | ||||
|         |> 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 =~ "#{ammo_type.name} created successfully" | ||||
|       assert html =~ "some bullet_type" | ||||
|     end | ||||
|  | ||||
| @@ -177,31 +219,30 @@ defmodule CanneryWeb.AmmoTypeLiveTest 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 =~ "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") | ||||
|       assert index_live | ||||
|              |> form("#ammo_type-form") | ||||
|              |> render_change(ammo_type: @invalid_attrs) =~ "can't be blank" | ||||
|  | ||||
|       {:ok, _view, html} = | ||||
|         index_live | ||||
|         |> form("#ammo_type-form", | ||||
|           ammo_type: Map.merge(@create_attrs, %{"bullet_type" => "some updated bullet_type"}) | ||||
|         |> form("#ammo_type-form") | ||||
|         |> render_submit( | ||||
|           ammo_type: Map.merge(@create_attrs, %{bullet_type: "some updated bullet_type"}) | ||||
|         ) | ||||
|         |> render_submit() | ||||
|         |> 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 =~ "#{ammo_type.name} created successfully" | ||||
|       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 | ||||
| @@ -214,27 +255,27 @@ defmodule CanneryWeb.AmmoTypeLiveTest do | ||||
|          %{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") | ||||
|       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 =~ gettext("Used rounds") | ||||
|       assert html =~ gettext("Total ever rounds") | ||||
|       assert html =~ gettext("Used packs") | ||||
|       assert html =~ gettext("Total ever packs") | ||||
|       assert html =~ "Used rounds" | ||||
|       assert html =~ "Total ever rounds" | ||||
|       assert html =~ "Used packs" | ||||
|       assert html =~ "Total ever packs" | ||||
|  | ||||
|       assert html =~ "20" | ||||
|       assert html =~ "0" | ||||
|       assert html =~ "1" | ||||
|       assert html =~ "\n20\n" | ||||
|       assert html =~ "\n0\n" | ||||
|       assert html =~ "\n1\n" | ||||
|  | ||||
|       shot_group_fixture(%{"count" => 5}, current_user, ammo_group) | ||||
|       shot_group_fixture(%{count: 5}, current_user, ammo_group) | ||||
|  | ||||
|       {:ok, index_live, _html} = live(conn, Routes.ammo_type_index_path(conn, :index)) | ||||
|  | ||||
| @@ -243,8 +284,8 @@ defmodule CanneryWeb.AmmoTypeLiveTest do | ||||
|         |> element(~s/input[type="checkbox"][aria-labelledby="toggle_show_used-label"}]/) | ||||
|         |> render_click() | ||||
|  | ||||
|       assert html =~ "15" | ||||
|       assert html =~ "5" | ||||
|       assert html =~ "\n15\n" | ||||
|       assert html =~ "\n5\n" | ||||
|     end | ||||
|   end | ||||
|  | ||||
| @@ -266,22 +307,22 @@ defmodule CanneryWeb.AmmoTypeLiveTest 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) | ||||
|                "Edit #{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") | ||||
|       assert show_live | ||||
|              |> form("#ammo_type-form") | ||||
|              |> render_change(ammo_type: @invalid_attrs) =~ "can't be blank" | ||||
|  | ||||
|       {:ok, _view, html} = | ||||
|         show_live | ||||
|         |> form("#ammo_type-form", ammo_type: @update_attrs) | ||||
|         |> render_submit() | ||||
|         |> 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 =~ "#{ammo_type.name} updated successfully" | ||||
|       assert html =~ "some updated bullet_type" | ||||
|     end | ||||
|   end | ||||
| @@ -297,7 +338,7 @@ defmodule CanneryWeb.AmmoTypeLiveTest do | ||||
|       {:ok, _show_live, html} = live(conn, Routes.ammo_type_show_path(conn, :show, ammo_type)) | ||||
|  | ||||
|       assert html =~ ammo_type_name | ||||
|       assert html =~ "some ammo group" | ||||
|       assert html =~ "\n20\n" | ||||
|       assert html =~ container_name | ||||
|     end | ||||
|  | ||||
| @@ -310,9 +351,7 @@ defmodule CanneryWeb.AmmoTypeLiveTest do | ||||
|         |> element(~s/input[type="checkbox"][aria-labelledby="toggle_table-label"}]/) | ||||
|         |> render_click() | ||||
|  | ||||
|       assert_patch(show_live, Routes.ammo_type_show_path(conn, :table, ammo_type)) | ||||
|  | ||||
|       assert html =~ "some ammo group" | ||||
|       assert html =~ "\n20\n" | ||||
|       assert html =~ container_name | ||||
|     end | ||||
|   end | ||||
| @@ -323,16 +362,15 @@ defmodule CanneryWeb.AmmoTypeLiveTest do | ||||
|     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 =~ "some ammo group" | ||||
|       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 =~ "some ammo group" | ||||
|       assert html =~ "\n20\n" | ||||
|       assert html =~ "Empty" | ||||
|       assert html =~ container_name | ||||
|     end | ||||
| @@ -346,17 +384,15 @@ defmodule CanneryWeb.AmmoTypeLiveTest do | ||||
|         |> element(~s/input[type="checkbox"][aria-labelledby="toggle_table-label"}]/) | ||||
|         |> render_click() | ||||
|  | ||||
|       assert_patch(show_live, Routes.ammo_type_show_path(conn, :table, ammo_type)) | ||||
|  | ||||
|       assert html =~ dgettext("actions", "Show used") | ||||
|       refute html =~ "some ammo group" | ||||
|       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 =~ "some ammo group" | ||||
|       assert html =~ "\n20\n" | ||||
|       assert html =~ "Empty" | ||||
|       assert html =~ container_name | ||||
|     end | ||||
|   | ||||
| @@ -5,61 +5,46 @@ defmodule CanneryWeb.ContainerLiveTest do | ||||
|  | ||||
|   use CanneryWeb.ConnCase | ||||
|   import Phoenix.LiveViewTest | ||||
|   import CanneryWeb.Gettext | ||||
|   alias Cannery.{Containers, Repo} | ||||
|   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" | ||||
|   } | ||||
|   @invalid_attrs %{desc: nil, location: nil, name: nil, type: nil} | ||||
|   @ammo_type_attrs %{ | ||||
|     "bullet_type" => "some bullet_type", | ||||
|     "case_material" => "some case_material", | ||||
|     "desc" => "some desc", | ||||
|     "manufacturer" => "some manufacturer", | ||||
|     "name" => "some name", | ||||
|     "grains" => 120 | ||||
|     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 | ||||
|     notes: "some ammo group", | ||||
|     count: 20 | ||||
|   } | ||||
|   @shot_group_attrs %{ | ||||
|     "notes" => "some shot group", | ||||
|     "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) | ||||
|  | ||||
|     %{ammo_type: ammo_type, ammo_group: ammo_group} | ||||
|   end | ||||
|  | ||||
|   defp create_empty_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) | ||||
|     shot_group = shot_group_fixture(@shot_group_attrs, current_user, ammo_group) | ||||
|     ammo_group = ammo_group |> Repo.reload!() | ||||
|  | ||||
|     %{ammo_type: ammo_type, ammo_group: ammo_group, shot_group: shot_group} | ||||
|     [ammo_type: ammo_type, ammo_group: ammo_group] | ||||
|   end | ||||
|  | ||||
|   describe "Index" do | ||||
| @@ -68,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 | ||||
|  | ||||
| @@ -80,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 | ||||
|  | ||||
| @@ -90,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 | ||||
| @@ -113,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 | ||||
|  | ||||
| @@ -140,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 | ||||
|  | ||||
| @@ -167,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 | ||||
|  | ||||
| @@ -195,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 | ||||
| @@ -232,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 | ||||
| @@ -245,24 +224,78 @@ 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(%{class: :rifle}, current_user) | ||||
|       {1, [rifle_ammo_group]} = ammo_group_fixture(rifle_type, container, current_user) | ||||
|       shotgun_type = ammo_type_fixture(%{class: :shotgun}, current_user) | ||||
|       {1, [shotgun_ammo_group]} = ammo_group_fixture(shotgun_type, container, current_user) | ||||
|       pistol_type = ammo_type_fixture(%{class: :pistol}, current_user) | ||||
|       {1, [pistol_ammo_group]} = ammo_group_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 | ||||
|  | ||||
|       html = | ||||
|         index_live | ||||
|         |> form(~s/form[phx-change="change_class"]/) | ||||
|         |> render_change(ammo_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 | ||||
|  | ||||
|       html = | ||||
|         index_live | ||||
|         |> form(~s/form[phx-change="change_class"]/) | ||||
|         |> render_change(ammo_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 | ||||
|  | ||||
|       html = | ||||
|         index_live | ||||
|         |> form(~s/form[phx-change="change_class"]/) | ||||
|         |> render_change(ammo_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 | ||||
|  | ||||
|       html = | ||||
|         index_live | ||||
|         |> form(~s/form[phx-change="change_class"]/) | ||||
|         |> render_change(ammo_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 | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   describe "Show with ammo group" do | ||||
| @@ -273,7 +306,7 @@ defmodule CanneryWeb.ContainerLiveTest do | ||||
|       {:ok, _show_live, html} = live(conn, Routes.container_show_path(conn, :show, container)) | ||||
|  | ||||
|       assert html =~ ammo_type_name | ||||
|       assert html =~ "some ammo group" | ||||
|       assert html =~ "\n20\n" | ||||
|     end | ||||
|  | ||||
|     test "displays ammo group in table", | ||||
| @@ -286,50 +319,7 @@ defmodule CanneryWeb.ContainerLiveTest do | ||||
|         |> render_click() | ||||
|  | ||||
|       assert html =~ ammo_type_name | ||||
|       assert html =~ "some ammo group" | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   describe "Show with empty ammo group" do | ||||
|     setup [:register_and_log_in_user, :create_container, :create_empty_ammo_group] | ||||
|  | ||||
|     test "hides empty ammo groups by default", | ||||
|          %{conn: conn, ammo_type: %{name: ammo_type_name}, container: container} do | ||||
|       {:ok, show_live, html} = live(conn, Routes.container_show_path(conn, :show, container)) | ||||
|  | ||||
|       assert html =~ dgettext("actions", "Show used") | ||||
|       refute html =~ "some ammo group" | ||||
|  | ||||
|       html = | ||||
|         show_live | ||||
|         |> element(~s/input[type="checkbox"][aria-labelledby="toggle_show_used-label"}]/) | ||||
|         |> render_click() | ||||
|  | ||||
|       assert html =~ ammo_type_name | ||||
|       assert html =~ "some ammo group" | ||||
|       assert html =~ "Empty" | ||||
|     end | ||||
|  | ||||
|     test "displays empty ammo groups in table on toggle", | ||||
|          %{conn: conn, ammo_type: %{name: ammo_type_name}, container: container} do | ||||
|       {:ok, show_live, _html} = live(conn, Routes.container_show_path(conn, :show, container)) | ||||
|  | ||||
|       html = | ||||
|         show_live | ||||
|         |> element(~s/input[type="checkbox"][aria-labelledby="toggle_table-label"}]/) | ||||
|         |> render_click() | ||||
|  | ||||
|       assert html =~ dgettext("actions", "Show used") | ||||
|       refute html =~ "some ammo group" | ||||
|  | ||||
|       html = | ||||
|         show_live | ||||
|         |> element(~s/input[type="checkbox"][aria-labelledby="toggle_show_used-label"}]/) | ||||
|         |> render_click() | ||||
|  | ||||
|       assert html =~ ammo_type_name | ||||
|       assert html =~ "some ammo group" | ||||
|       assert html =~ "Empty" | ||||
|       assert html =~ "\n20\n" | ||||
|     end | ||||
|   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 | ||||
|   | ||||
| @@ -6,25 +6,28 @@ 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) | ||||
|     container = container_fixture(%{staged: true}, current_user) | ||||
|     ammo_type = ammo_type_fixture(current_user) | ||||
|  | ||||
|     {1, [ammo_group]} = | ||||
|       ammo_group_fixture(%{"staged" => true}, ammo_type, container, current_user) | ||||
|     {1, [ammo_group]} = ammo_group_fixture(%{staged: true}, ammo_type, container, current_user) | ||||
|  | ||||
|     shot_group = | ||||
|       %{"count" => 5, "date" => ~N[2022-02-13 03:17:00], "notes" => "some notes"} | ||||
|       %{count: 5, date: ~N[2022-02-13 03:17:00], notes: "some notes"} | ||||
|       |> shot_group_fixture(current_user, ammo_group) | ||||
|  | ||||
|     %{shot_group: shot_group, ammo_group: ammo_group} | ||||
|     [ | ||||
|       container: container, | ||||
|       ammo_type: ammo_type, | ||||
|       ammo_group: ammo_group, | ||||
|       shot_group: shot_group | ||||
|     ] | ||||
|   end | ||||
|  | ||||
|   describe "Index" do | ||||
| @@ -33,32 +36,94 @@ defmodule CanneryWeb.RangeLiveTest do | ||||
|     test "lists all shot_groups", %{conn: conn, shot_group: shot_group} do | ||||
|       {:ok, _index_live, html} = live(conn, Routes.range_index_path(conn, :index)) | ||||
|  | ||||
|       assert html =~ gettext("Range day") | ||||
|       assert html =~ "Range day" | ||||
|       assert html =~ shot_group.notes | ||||
|     end | ||||
|  | ||||
|     test "can sort by type", | ||||
|          %{conn: conn, container: container, current_user: current_user} do | ||||
|       rifle_ammo_type = ammo_type_fixture(%{class: :rifle}, current_user) | ||||
|       {1, [rifle_ammo_group]} = ammo_group_fixture(rifle_ammo_type, container, current_user) | ||||
|  | ||||
|       rifle_shot_group = shot_group_fixture(%{notes: "group_one"}, current_user, rifle_ammo_group) | ||||
|  | ||||
|       shotgun_ammo_type = ammo_type_fixture(%{class: :shotgun}, current_user) | ||||
|       {1, [shotgun_ammo_group]} = ammo_group_fixture(shotgun_ammo_type, container, current_user) | ||||
|  | ||||
|       shotgun_shot_group = | ||||
|         shot_group_fixture(%{notes: "group_two"}, current_user, shotgun_ammo_group) | ||||
|  | ||||
|       pistol_ammo_type = ammo_type_fixture(%{class: :pistol}, current_user) | ||||
|       {1, [pistol_ammo_group]} = ammo_group_fixture(pistol_ammo_type, container, current_user) | ||||
|  | ||||
|       pistol_shot_group = | ||||
|         shot_group_fixture(%{notes: "group_three"}, current_user, pistol_ammo_group) | ||||
|  | ||||
|       {: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 | ||||
|  | ||||
|       html = | ||||
|         index_live | ||||
|         |> form(~s/form[phx-change="change_class"]/) | ||||
|         |> render_change(ammo_type: %{class: :rifle}) | ||||
|  | ||||
|       assert html =~ rifle_shot_group.notes | ||||
|       refute html =~ shotgun_shot_group.notes | ||||
|       refute html =~ pistol_shot_group.notes | ||||
|  | ||||
|       html = | ||||
|         index_live | ||||
|         |> form(~s/form[phx-change="change_class"]/) | ||||
|         |> render_change(ammo_type: %{class: :shotgun}) | ||||
|  | ||||
|       refute html =~ rifle_shot_group.notes | ||||
|       assert html =~ shotgun_shot_group.notes | ||||
|       refute html =~ pistol_shot_group.notes | ||||
|  | ||||
|       html = | ||||
|         index_live | ||||
|         |> form(~s/form[phx-change="change_class"]/) | ||||
|         |> render_change(ammo_type: %{class: :pistol}) | ||||
|  | ||||
|       refute html =~ rifle_shot_group.notes | ||||
|       refute html =~ shotgun_shot_group.notes | ||||
|       assert html =~ pistol_shot_group.notes | ||||
|  | ||||
|       html = | ||||
|         index_live | ||||
|         |> form(~s/form[phx-change="change_class"]/) | ||||
|         |> render_change(ammo_type: %{class: :all}) | ||||
|  | ||||
|       assert html =~ rifle_shot_group.notes | ||||
|       assert html =~ shotgun_shot_group.notes | ||||
|       assert html =~ pistol_shot_group.notes | ||||
|     end | ||||
|  | ||||
|     test "can search for shot_group", %{conn: conn, shot_group: shot_group} do | ||||
|       {:ok, index_live, html} = live(conn, Routes.range_index_path(conn, :index)) | ||||
|  | ||||
|       assert html =~ shot_group.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_group.notes}) =~ shot_group.notes | ||||
|  | ||||
|       assert_patch(index_live, Routes.range_index_path(conn, :search, shot_group.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_group.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_group.notes | ||||
|  | ||||
|       assert_patch(index_live, Routes.range_index_path(conn, :index)) | ||||
|     end | ||||
| @@ -66,22 +131,20 @@ defmodule CanneryWeb.RangeLiveTest do | ||||
|     test "saves new shot_group", %{conn: conn, ammo_group: ammo_group} 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_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-group-form") | ||||
|              |> render_change(shot_group: @invalid_attrs) =~ "can't be blank" | ||||
|  | ||||
|       {:ok, _view, html} = | ||||
|         index_live | ||||
|         |> form("#shot-group-form", shot_group: @create_attrs) | ||||
|         |> render_submit() | ||||
|         |> form("#shot-group-form") | ||||
|         |> render_submit(shot_group: @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 | ||||
|  | ||||
| @@ -90,14 +153,13 @@ defmodule CanneryWeb.RangeLiveTest do | ||||
|  | ||||
|       assert index_live | ||||
|              |> element(~s/a[aria-label="Edit shot record of #{shot_group.count} shots"]/) | ||||
|              |> render_click() =~ | ||||
|                gettext("Edit Shot Records") | ||||
|              |> render_click() =~ "Edit Shot Records" | ||||
|  | ||||
|       assert_patch(index_live, Routes.range_index_path(conn, :edit, shot_group)) | ||||
|  | ||||
|       # assert index_live | ||||
|       #        |> form("#shot_group-form", shot_group: @invalid_attrs) | ||||
|       #        |> render_change() =~ dgettext("errors", "is invalid") | ||||
|       assert index_live | ||||
|              |> form("#shot-group-form") | ||||
|              |> render_change(shot_group: @invalid_attrs) =~ "can't be blank" | ||||
|  | ||||
|       {:ok, _view, html} = | ||||
|         index_live | ||||
| @@ -105,7 +167,7 @@ defmodule CanneryWeb.RangeLiveTest do | ||||
|         |> 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 | ||||
|  | ||||
|   | ||||
| @@ -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" => "some bg-color", | ||||
|     "name" => "some name", | ||||
|     "text_color" => "some text-color" | ||||
|     bg_color: "#100000", | ||||
|     name: "some name", | ||||
|     text_color: "#000000" | ||||
|   } | ||||
|   @update_attrs %{ | ||||
|     "bg_color" => "some updated bg-color", | ||||
|     "name" => "some updated name", | ||||
|     "text_color" => "some updated text-color" | ||||
|     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,47 +66,43 @@ 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 bg-color" | ||||
|       assert html =~ "some name created successfully" | ||||
|       assert html =~ "#100000" | ||||
|     end | ||||
|  | ||||
|     test "updates tag in listing", %{conn: conn, tag: tag} 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 bg-color" | ||||
|       assert html =~ "some updated name updated successfully" | ||||
|       assert html =~ "#100001" | ||||
|     end | ||||
|  | ||||
|     test "deletes tag in listing", %{conn: conn, tag: tag} do | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -26,7 +26,7 @@ defmodule CanneryWeb.ConnCase do | ||||
|       import Plug.Conn | ||||
|       import Phoenix.ConnTest | ||||
|       # credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse | ||||
|       import Cannery.Fixtures | ||||
|       import Cannery.{DataCase, Fixtures} | ||||
|       import CanneryWeb.ConnCase | ||||
|  | ||||
|       alias CanneryWeb.Router.Helpers, as: Routes | ||||
|   | ||||
| @@ -48,4 +48,15 @@ defmodule Cannery.DataCase do | ||||
|       end) | ||||
|     end) | ||||
|   end | ||||
|  | ||||
|   @doc """ | ||||
|   Generates a random string of any length, default of 12 | ||||
|   """ | ||||
|   @spec random_string(length :: non_neg_integer()) :: String.t() | ||||
|   def random_string(length \\ 12) do | ||||
|     :crypto.strong_rand_bytes(length) |> Base.url_encode64() |> binary_part(0, length) | ||||
|   end | ||||
|  | ||||
|   def unique_user_email, do: "user#{System.unique_integer()}@example.com" | ||||
|   def valid_user_password, do: "hello world!" | ||||
| end | ||||
|   | ||||
| @@ -3,6 +3,8 @@ defmodule Cannery.Fixtures do | ||||
|   This module defines test helpers for creating entities | ||||
|   """ | ||||
|  | ||||
|   import Cannery.DataCase | ||||
|  | ||||
|   alias Cannery.{ | ||||
|     Accounts, | ||||
|     Accounts.User, | ||||
| @@ -17,16 +19,13 @@ defmodule Cannery.Fixtures do | ||||
|     Repo | ||||
|   } | ||||
|  | ||||
|   def unique_user_email, do: "user#{System.unique_integer()}@example.com" | ||||
|   def valid_user_password, do: "hello world!" | ||||
|  | ||||
|   @spec user_fixture() :: User.t() | ||||
|   @spec user_fixture(attrs :: map()) :: User.t() | ||||
|   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() | ||||
| @@ -37,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() | ||||
| @@ -77,9 +76,9 @@ defmodule Cannery.Fixtures do | ||||
|   def shot_group_fixture(attrs \\ %{}, %User{} = user, %AmmoGroup{} = ammo_group) do | ||||
|     attrs | ||||
|     |> Enum.into(%{ | ||||
|       "count" => 20, | ||||
|       "date" => ~N[2022-02-13 03:17:00], | ||||
|       "notes" => "some notes" | ||||
|       count: 20, | ||||
|       date: ~N[2022-02-13 03:17:00], | ||||
|       notes: random_string() | ||||
|     }) | ||||
|     |> Cannery.ActivityLog.create_shot_group(user, ammo_group) | ||||
|     |> unwrap_ok_tuple() | ||||
| @@ -92,7 +91,7 @@ defmodule Cannery.Fixtures do | ||||
|   @spec container_fixture(attrs :: map(), User.t()) :: Container.t() | ||||
|   def container_fixture(attrs \\ %{}, %User{} = user) do | ||||
|     attrs | ||||
|     |> Enum.into(%{"name" => "My container", "type" => "Ammo can"}) | ||||
|     |> Enum.into(%{name: random_string(), type: "Ammo can"}) | ||||
|     |> Containers.create_container(user) | ||||
|     |> unwrap_ok_tuple() | ||||
|   end | ||||
| @@ -104,7 +103,7 @@ defmodule Cannery.Fixtures do | ||||
|   @spec ammo_type_fixture(attrs :: map(), User.t()) :: AmmoType.t() | ||||
|   def ammo_type_fixture(attrs \\ %{}, %User{} = user) do | ||||
|     attrs | ||||
|     |> Enum.into(%{"name" => "ammo_type"}) | ||||
|     |> Enum.into(%{name: random_string(), class: :rifle}) | ||||
|     |> Ammo.create_ammo_type(user) | ||||
|     |> unwrap_ok_tuple() | ||||
|   end | ||||
| @@ -132,10 +131,10 @@ defmodule Cannery.Fixtures do | ||||
|       ) do | ||||
|     attrs | ||||
|     |> Enum.into(%{ | ||||
|       "ammo_type_id" => ammo_type_id, | ||||
|       "container_id" => container_id, | ||||
|       "count" => 20, | ||||
|       "purchased_on" => Date.utc_today() | ||||
|       ammo_type_id: ammo_type_id, | ||||
|       container_id: container_id, | ||||
|       count: 20, | ||||
|       purchased_on: Date.utc_today() | ||||
|     }) | ||||
|     |> Ammo.create_ammo_groups(multiplier, user) | ||||
|     |> unwrap_ok_tuple() | ||||
| @@ -149,9 +148,9 @@ defmodule Cannery.Fixtures do | ||||
|   def tag_fixture(attrs \\ %{}, %User{} = user) do | ||||
|     attrs | ||||
|     |> Enum.into(%{ | ||||
|       "bg_color" => "some bg-color", | ||||
|       "name" => "some name", | ||||
|       "text_color" => "some text-color" | ||||
|       bg_color: "#100000", | ||||
|       name: random_string(), | ||||
|       text_color: "#000000" | ||||
|     }) | ||||
|     |> Containers.create_tag(user) | ||||
|     |> unwrap_ok_tuple() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user