19 Commits

Author SHA1 Message Date
0cae7c2940 rename ammo_type type to class
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-28 23:08:40 -04:00
1e645b5bb8 generate fonts with correct filename
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-28 22:03:14 -04:00
bab2b26c13 use atom keys in tests
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-28 21:57:29 -04:00
8c95536ffd add selectable ammo types
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-23 22:07:25 -04:00
d9251c7e4c improve components 2023-03-23 00:21:26 -04:00
fe4e4f4f17 add length limits to all items
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-19 23:52:25 -04:00
e5e5449e8b improve modal accessibility
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-19 15:39:39 -04:00
355752598c show link to ammo pack in ammo pack table while viewing ammo type
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-19 15:09:44 -04:00
03f8a2e8a7 remove all n+1 queries for real this time 2023-03-19 15:05:09 -04:00
071eb1b3c9 fix some values not being sorted in tables properly 2023-03-19 14:31:53 -04:00
2987e4ff37 hide more ammo group table fields when not viewing historical information 2023-03-19 14:11:01 -04:00
ca81924ebe fix ammo type table not displaying correct information 2023-03-19 14:07:23 -04:00
40e4f6fe0a remove :table path 2023-03-19 13:37:28 -04:00
213dcca973 fix duplicate entries showing up 2023-03-19 13:28:56 -04:00
b32edd581d fix accessibility issues
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-19 12:35:26 -04:00
2e372ca2ab hide historical ammo type information until show_used is toggled 2023-03-19 11:43:13 -04:00
fd0bac3bbf fix tables unable to sort on nil dates 2023-03-19 11:19:55 -04:00
f83fbc5d99 add links to readme 2023-03-19 00:41:39 -04:00
daab051026 remove unnecessary auth check on invite page 2023-03-19 00:23:59 -04:00
99 changed files with 6026 additions and 3433 deletions

View File

@ -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

View File

@ -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

View File

@ -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]' }
}
]
},

View File

@ -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

View File

@ -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

View File

@ -6,38 +6,53 @@ 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)
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)
def list_shot_groups(search, %{id: user_id}) when search |> is_binary() do
trimmed_search = String.trim(search)
Repo.all(
from sg in ShotGroup,
@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,
where:
distinct: sg.id
)
|> list_shot_groups_search(search)
|> list_shot_groups_filter_type(type)
|> Repo.all()
end
@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
defp list_shot_groups_search(query, search) when search |> is_binary() do
trimmed_search = String.trim(search)
query
|> where(
[sg: sg, ag: ag, at: at],
fragment(
"? @@ websearch_to_tsquery('english', ?)",
sg.search,
@ -52,18 +67,31 @@ defmodule Cannery.ActivityLog do
"? @@ websearch_to_tsquery('english', ?)",
at.search,
^trimmed_search
),
order_by: {
)
)
|> 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},

View File

@ -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)

View File

@ -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,
def list_ammo_types(search, %{id: user_id}, type) do
from(at in AmmoType,
as: :at,
where: at.user_id == ^user_id,
order_by: at.name,
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:
query
|> where(
[at: at],
fragment(
"? @@ websearch_to_tsquery('english', ?)",
at.search,
^trimmed_search
),
order_by: {
)
)
|> order_by(
[at: at],
{
:desc,
fragment(
"ts_rank_cd(?, websearch_to_tsquery('english', ?), 4)",
at.search,
^trimmed_search
)
},
preload: ^@ammo_type_preloads
}
)
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,
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,
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,
@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,
where: not (ag.count == 0),
preload: ^@ammo_group_preloads
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(
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{} = 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
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,7 +1106,7 @@ defmodule Cannery.Ammo do
end
end
def create_ammo_groups(
defp do_create_ammo_groups(
%{"ammo_type_id" => ammo_type_id, "container_id" => container_id} = attrs,
_multiplier,
user
@ -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

View File

@ -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
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
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

View 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

View 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

View File

@ -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 """

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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
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},
|> 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},
%{label: gettext("Notes"), key: :notes}
| columns
]
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 =
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)

View File

@ -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)
|> Enum.any?(fn ammo_type -> Map.get(ammo_type, key, default_value) != default_value end)
end)
end)
|> Kernel.++([
%{label: gettext("Rounds"), key: :round_count, type: :round_count}
])
|> Kernel.++(
if show_used do
[
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
)
|> TableComponent.maybe_compose_columns(
%{
label: gettext("Used packs"),
key: :used_pack_count,
type: :used_pack_count
},
show_used
)
|> 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
},
%{
label: gettext("Total ever rounds"),
key: :historical_round_count,
type: :historical_round_count
}
]
else
[]
end
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("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]
)
|> 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("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}
{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

View File

@ -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 = %{

View File

@ -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)

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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)

View File

@ -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 =

View File

@ -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

View File

@ -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)

View File

@ -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 =

View File

@ -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

View File

@ -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,20 +37,46 @@
<.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">
<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 ammo")
) %>
@ -77,6 +100,7 @@
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">
@ -177,6 +201,7 @@
</:actions>
</.live_component>
<% end %>
<% end %>
</div>
<%= case @live_action do %>

View File

@ -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)

View File

@ -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") %>
<%= 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") %>
<h2 class="text-center title text-lg text-primary-600 col-span-3">
<%= gettext("Dimensions") %>
</h2>
<%= 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, :caliber, gettext("Caliber"), class: "title text-lg text-primary-600") %>
<%= label(
f,
:caliber,
if(Changeset.get_field(@changeset, :class) == :shotgun,
do: gettext("Gauge"),
else: 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") %>
<%= 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, :muzzle_velocity, gettext("Muzzle velocity"),
<%= if Changeset.get_field(@changeset, :class) == :shotgun do %>
<%= label(f, :unfired_length, gettext("Unfired shell length"),
class: "title text-lg text-primary-600"
) %>
<%= number_input(f, :muzzle_velocity,
step: "1",
<%= text_input(f, :unfired_length,
class: "text-center col-span-2 input input-primary",
min: 1
maxlength: 255
) %>
<%= error_tag(f, :muzzle_velocity, "col-span-3 text-center") %>
<%= error_tag(f, :unfired_length, "col-span-3 text-center") %>
<%= 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",
<%= 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",
min: 1
maxlength: 255
) %>
<%= error_tag(f, :powder_grains_per_charge, "col-span-3 text-center") %>
<%= error_tag(f, :brass_height, "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 %>
<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"),

View File

@ -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

View File

@ -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">

View File

@ -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

View File

@ -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,6 +91,7 @@
<%= @rounds %>
</span>
<%= if @show_used do %>
<h3 class="title text-lg">
<%= gettext("Used rounds:") %>
</h3>
@ -89,11 +107,8 @@
<span class="text-primary-600">
<%= @historical_round_count %>
</span>
</div>
<% 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,6 +117,7 @@
<%= @packs_count %>
</span>
<%= if @show_used do %>
<h3 class="title text-lg">
<%= gettext("Used packs:") %>
</h3>
@ -117,11 +133,8 @@
<span class="text-primary-600">
<%= @historical_packs_count %>
</span>
</div>
<% 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 %>

View File

@ -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") %>

View File

@ -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,7 +41,6 @@
</span>
</.toggle_button>
</div>
<% end %>
<%= if @containers |> Enum.empty?() do %>
<h2 class="title text-xl text-primary-600">
@ -96,7 +96,9 @@
phx-click="delete"
phx-value-id={container.id}
data-confirm={
dgettext("prompts", "Are you sure you want to delete %{name}?", name: container.name)
dgettext("prompts", "Are you sure you want to delete %{name}?",
name: container.name
)
}
aria-label={
dgettext("actions", "Delete %{container_name}", container_name: container.name)
@ -152,7 +154,9 @@
phx-click="delete"
phx-value-id={container.id}
data-confirm={
dgettext("prompts", "Are you sure you want to delete %{name}?", name: container.name)
dgettext("prompts", "Are you sure you want to delete %{name}?",
name: container.name
)
}
aria-label={
dgettext("actions", "Delete %{container_name}", container_name: container.name)
@ -164,6 +168,7 @@
</div>
<% end %>
<% end %>
<% end %>
</div>
<%= case @live_action do %>

View File

@ -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,

View File

@ -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 :if={@show_used} class="rounded-lg title text-lg">
<%= gettext("Total packs:") %>
<%= Enum.count(@ammo_groups) %>
<%= @ammo_groups_count %>
</span>
<span class="rounded-lg title text-lg">
<%= gettext("Rounds:") %>
<%= @round_count %>
</span>
<% end %>
<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">

View File

@ -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") %>

View File

@ -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

View File

@ -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"

View File

@ -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)

View File

@ -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")
) %>

View File

@ -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") %>

View File

@ -18,20 +18,20 @@
<.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">
<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 tags")
) %>
@ -69,6 +69,7 @@
</.tag_card>
</div>
<% end %>
<% end %>
</div>
<.modal :if={@live_action in [:new, :edit]} return_to={Routes.tag_index_path(Endpoint, :index)}>

View File

@ -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

View File

@ -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?()}

View File

@ -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(),

View File

@ -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 ""

View File

@ -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

View File

@ -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 ""

View File

@ -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

View File

@ -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

View File

@ -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 ""

View File

@ -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 ""

View File

@ -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 ""

View File

@ -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

View File

@ -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 ""

View File

@ -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"

View File

@ -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

View File

@ -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 dutilisateur·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 "nest 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 ""

View File

@ -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 "Senregistrer 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"

View File

@ -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

View File

@ -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 ""

View File

@ -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 ""

View File

@ -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 ""

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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."
)
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."
)
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

View File

@ -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

View File

@ -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."
)
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

View File

@ -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&#39;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&#39;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&#39;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&#39;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&#39;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&#39;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&#39;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&#39;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&#39;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&#39;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

View File

@ -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&#39;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&#39;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&#39;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&#39;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&#39;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

View File

@ -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&#39;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&#39;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&#39;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&#39;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&#39;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

View File

@ -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

View File

@ -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&#39;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&#39;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

View File

@ -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&#39;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&#39;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

View File

@ -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&#39;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&#39;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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()