7 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
85 changed files with 5374 additions and 3113 deletions

View File

@ -1,3 +1,14 @@
# 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 # v0.8.6
- Fix duplicate entries showing up - Fix duplicate entries showing up
- Show ammo packs under a type in a table by default - Show ammo packs under a type in a table by default

View File

@ -45,7 +45,7 @@ module.exports = (env, options) => {
{ {
test: /\.(woff(2)?|ttf|eot|svg|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/, test: /\.(woff(2)?|ttf|eot|svg|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
type: 'asset/resource', 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__{} %__MODULE__{}
|> change(token: token, created_by_id: user_id) |> change(token: token, created_by_id: user_id)
|> cast(attrs, [:name, :uses_left, :disabled_at]) |> 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_number(:uses_left, greater_than_or_equal_to: 0)
|> validate_required([:name, :token, :created_by_id])
end end
@doc false @doc false
@ -57,7 +58,8 @@ defmodule Cannery.Accounts.Invite do
def update_changeset(invite, attrs) do def update_changeset(invite, attrs) do
invite invite
|> cast(attrs, [:name, :uses_left, :disabled_at]) |> 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_number(:uses_left, greater_than_or_equal_to: 0)
|> validate_required([:name])
end end
end end

View File

@ -79,6 +79,7 @@ defmodule Cannery.Accounts.User do
%User{} %User{}
|> cast(attrs, [:email, :password, :locale]) |> cast(attrs, [:email, :password, :locale])
|> put_change(:invite_id, if(invite, do: invite.id)) |> put_change(:invite_id, if(invite, do: invite.id))
|> validate_length(:locale, max: 255)
|> validate_email() |> validate_email()
|> validate_password(opts) |> validate_password(opts)
end end
@ -209,6 +210,7 @@ defmodule Cannery.Accounts.User do
def locale_changeset(user_or_changeset, locale) do def locale_changeset(user_or_changeset, locale) do
user_or_changeset user_or_changeset
|> cast(%{"locale" => locale}, [:locale]) |> cast(%{"locale" => locale}, [:locale])
|> validate_length(:locale, max: 255)
|> validate_required(:locale) |> validate_required(:locale)
end end
end end

View File

@ -6,65 +6,92 @@ defmodule Cannery.ActivityLog do
import Ecto.Query, warn: false import Ecto.Query, warn: false
alias Cannery.Ammo.{AmmoGroup, AmmoType} alias Cannery.Ammo.{AmmoGroup, AmmoType}
alias Cannery.{Accounts.User, ActivityLog.ShotGroup, Repo} alias Cannery.{Accounts.User, ActivityLog.ShotGroup, Repo}
alias Ecto.Multi alias Ecto.{Multi, Queryable}
@doc """ @doc """
Returns the list of shot_groups. Returns the list of shot_groups.
## Examples ## Examples
iex> list_shot_groups(%User{id: 123}) iex> list_shot_groups(:all, %User{id: 123})
[%ShotGroup{}, ...] [%ShotGroup{}, ...]
iex> list_shot_groups("cool", %User{id: 123}) iex> list_shot_groups("cool", :all, %User{id: 123})
[%ShotGroup{notes: "My cool shot group"}, ...] [%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(AmmoType.class() | :all, User.t()) :: [ShotGroup.t()]
@spec list_shot_groups(search :: nil | String.t(), User.t()) :: [ShotGroup.t()] @spec list_shot_groups(search :: nil | String.t(), AmmoType.class() | :all, User.t()) ::
def list_shot_groups(search \\ nil, user) [ShotGroup.t()]
def list_shot_groups(search \\ nil, type, %{id: user_id}) do
from(sg in ShotGroup,
as: :sg,
left_join: ag in AmmoGroup,
as: :ag,
on: sg.ammo_group_id == ag.id,
left_join: at in AmmoType,
as: :at,
on: ag.ammo_type_id == at.id,
where: sg.user_id == ^user_id,
distinct: sg.id
)
|> list_shot_groups_search(search)
|> list_shot_groups_filter_type(type)
|> Repo.all()
end
def list_shot_groups(search, %{id: user_id}) when search |> is_nil() or search == "", @spec list_shot_groups_search(Queryable.t(), search :: String.t() | nil) ::
do: Repo.all(from sg in ShotGroup, where: sg.user_id == ^user_id) Queryable.t()
defp list_shot_groups_search(query, search) when search in ["", nil], do: query
def list_shot_groups(search, %{id: user_id}) when search |> is_binary() do defp list_shot_groups_search(query, search) when search |> is_binary() do
trimmed_search = String.trim(search) trimmed_search = String.trim(search)
Repo.all( query
from sg in ShotGroup, |> where(
left_join: ag in AmmoGroup, [sg: sg, ag: ag, at: at],
on: sg.ammo_group_id == ag.id, fragment(
left_join: at in AmmoType, "? @@ websearch_to_tsquery('english', ?)",
on: ag.ammo_type_id == at.id, sg.search,
where: sg.user_id == ^user_id, ^trimmed_search
where: ) or
fragment( fragment(
"? @@ websearch_to_tsquery('english', ?)", "? @@ websearch_to_tsquery('english', ?)",
sg.search, ag.search,
^trimmed_search ^trimmed_search
) or ) or
fragment( fragment(
"? @@ websearch_to_tsquery('english', ?)", "? @@ websearch_to_tsquery('english', ?)",
ag.search, at.search,
^trimmed_search ^trimmed_search
) or )
fragment(
"? @@ websearch_to_tsquery('english', ?)",
at.search,
^trimmed_search
),
order_by: {
:desc,
fragment(
"ts_rank_cd(?, websearch_to_tsquery('english', ?), 4)",
sg.search,
^trimmed_search
)
},
distinct: sg.id
) )
|> order_by([sg: sg], {
:desc,
fragment(
"ts_rank_cd(?, websearch_to_tsquery('english', ?), 4)",
sg.search,
^trimmed_search
)
})
end 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()] @spec list_shot_groups_for_ammo_group(AmmoGroup.t(), User.t()) :: [ShotGroup.t()]
def list_shot_groups_for_ammo_group( def list_shot_groups_for_ammo_group(
%AmmoGroup{id: ammo_group_id, user_id: user_id}, %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(user_id: user_id)
|> change(ammo_group_id: ammo_group_id) |> change(ammo_group_id: ammo_group_id)
|> cast(attrs, [:count, :notes, :date]) |> cast(attrs, [:count, :notes, :date])
|> validate_length(:notes, max: 255)
|> validate_create_shot_group_count(ammo_group) |> validate_create_shot_group_count(ammo_group)
|> validate_required([:date, :ammo_group_id, :user_id]) |> validate_required([:date, :ammo_group_id, :user_id])
end end
@ -68,6 +69,7 @@ defmodule Cannery.ActivityLog.ShotGroup do
def create_changeset(shot_group, _invalid_user, _invalid_ammo_group, attrs) do def create_changeset(shot_group, _invalid_user, _invalid_ammo_group, attrs) do
shot_group shot_group
|> cast(attrs, [:count, :notes, :date]) |> cast(attrs, [:count, :notes, :date])
|> validate_length(:notes, max: 255)
|> validate_required([:ammo_group_id, :user_id]) |> validate_required([:ammo_group_id, :user_id])
|> add_error(:invalid, dgettext("errors", "Please select a valid user and ammo pack")) |> add_error(:invalid, dgettext("errors", "Please select a valid user and ammo pack"))
end end
@ -99,6 +101,7 @@ defmodule Cannery.ActivityLog.ShotGroup do
def update_changeset(%__MODULE__{} = shot_group, user, attrs) do def update_changeset(%__MODULE__{} = shot_group, user, attrs) do
shot_group shot_group
|> cast(attrs, [:count, :notes, :date]) |> cast(attrs, [:count, :notes, :date])
|> validate_length(:notes, max: 255)
|> validate_number(:count, greater_than: 0) |> validate_number(:count, greater_than: 0)
|> validate_required([:count, :date]) |> validate_required([:count, :date])
|> validate_update_shot_group_count(shot_group, user) |> 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.Containers.{Container, ContainerTag, Tag}
alias Cannery.{ActivityLog, ActivityLog.ShotGroup} alias Cannery.{ActivityLog, ActivityLog.ShotGroup}
alias Cannery.Ammo.{AmmoGroup, AmmoType} alias Cannery.Ammo.{AmmoGroup, AmmoType}
alias Ecto.Changeset alias Ecto.{Changeset, Queryable}
@ammo_group_create_limit 10_000 @ammo_group_create_limit 10_000
@ammo_group_preloads [:ammo_type] @ammo_group_preloads [:ammo_type]
@ -20,50 +20,70 @@ defmodule Cannery.Ammo do
## Examples ## Examples
iex> list_ammo_types(%User{id: 123}) iex> list_ammo_types(%User{id: 123}, :all)
[%AmmoType{}, ...] [%AmmoType{}, ...]
iex> list_ammo_types("cool", %User{id: 123}) iex> list_ammo_types("cool", %User{id: 123}, :shotgun)
[%AmmoType{name: "My cool ammo type"}, ...] [%AmmoType{name: "My cool ammo type", class: :shotgun}, ...]
""" """
@spec list_ammo_types(User.t()) :: [AmmoType.t()] @spec list_ammo_types(User.t(), AmmoType.class() | :all) :: [AmmoType.t()]
@spec list_ammo_types(search :: nil | String.t(), User.t()) :: [AmmoType.t()] @spec list_ammo_types(search :: nil | String.t(), User.t(), AmmoType.class() | :all) ::
def list_ammo_types(search \\ nil, user) [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 def list_ammo_types(search, %{id: user_id}, type) do
Repo.all( from(at in AmmoType,
from at in AmmoType, as: :at,
where: at.user_id == ^user_id, where: at.user_id == ^user_id,
order_by: at.name, preload: ^@ammo_type_preloads
preload: ^@ammo_type_preloads
) )
|> list_ammo_types_filter_type(type)
|> list_ammo_types_filter_search(search)
|> Repo.all()
end 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) trimmed_search = String.trim(search)
Repo.all( query
from at in AmmoType, |> where(
where: at.user_id == ^user_id, [at: at],
where: fragment(
fragment( "? @@ websearch_to_tsquery('english', ?)",
"? @@ websearch_to_tsquery('english', ?)", at.search,
at.search, ^trimmed_search
^trimmed_search )
), )
order_by: { |> order_by(
:desc, [at: at],
fragment( {
"ts_rank_cd(?, websearch_to_tsquery('english', ?), 4)", :desc,
at.search, fragment(
^trimmed_search "ts_rank_cd(?, websearch_to_tsquery('english', ?), 4)",
) at.search,
}, ^trimmed_search
preload: ^@ammo_type_preloads )
}
) )
end 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 """ @doc """
Returns a count of ammo_types. Returns a count of ammo_types.
@ -80,7 +100,7 @@ defmodule Cannery.Ammo do
where: at.user_id == ^user_id, where: at.user_id == ^user_id,
select: count(at.id), select: count(at.id),
distinct: true distinct: true
) ) || 0
end end
@doc """ @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()) :: [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()] [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( def list_ammo_groups_for_type(
%AmmoType{id: ammo_type_id, user_id: user_id}, %AmmoType{id: ammo_type_id, user_id: user_id},
%User{id: user_id}, %User{id: user_id},
true = _include_empty show_used
) do ) do
Repo.all( from(ag in AmmoGroup,
from ag in AmmoGroup, as: :ag,
where: ag.ammo_type_id == ^ammo_type_id, where: ag.ammo_type_id == ^ammo_type_id,
where: ag.user_id == ^user_id, where: ag.user_id == ^user_id,
preload: ^@ammo_group_preloads preload: ^@ammo_group_preloads
) )
|> list_ammo_groups_for_type_show_used(show_used)
|> Repo.all()
end end
def list_ammo_groups_for_type( @spec list_ammo_groups_for_type_show_used(Queryable.t(), show_used :: boolean()) ::
%AmmoType{id: ammo_type_id, user_id: user_id}, Queryable.t()
%User{id: user_id}, def list_ammo_groups_for_type_show_used(query, false),
false = _include_empty do: query |> where([ag: ag], ag.count > 0)
) do
Repo.all( def list_ammo_groups_for_type_show_used(query, _true), do: query
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
@doc """ @doc """
Returns the list of ammo_groups for a user and container. 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( iex> list_ammo_groups_for_container(
...> %Container{id: 123, user_id: 456}, ...> %Container{id: 123, user_id: 456},
...> :rifle,
...> %User{id: 456} ...> %User{id: 456}
...> ) ...> )
[%AmmoGroup{}, ...] [%AmmoGroup{}, ...]
iex> list_ammo_groups_for_container( iex> list_ammo_groups_for_container(
...> %Container{id: 123, user_id: 456}, ...> %Container{id: 123, user_id: 456},
...> %User{id: 456}, ...> :all,
...> true ...> %User{id: 456}
...> ) ...> )
[%AmmoGroup{}, %AmmoGroup{}, ...] [%AmmoGroup{}, %AmmoGroup{}, ...]
""" """
@spec list_ammo_groups_for_container(Container.t(), User.t()) :: [AmmoGroup.t()] @spec list_ammo_groups_for_container(
@spec list_ammo_groups_for_container(Container.t(), User.t(), include_empty :: boolean()) :: Container.t(),
[AmmoGroup.t()] AmmoType.t() | :all,
def list_ammo_groups_for_container(container, user, include_empty \\ false) User.t()
) :: [AmmoGroup.t()]
def list_ammo_groups_for_container( def list_ammo_groups_for_container(
%Container{id: container_id, user_id: user_id}, %Container{id: container_id, user_id: user_id},
%User{id: user_id}, type,
true = _include_empty %User{id: user_id}
) do ) do
Repo.all( from(ag in AmmoGroup,
from ag in AmmoGroup, as: :ag,
where: ag.container_id == ^container_id, join: at in assoc(ag, :ammo_type),
where: ag.user_id == ^user_id, as: :at,
preload: ^@ammo_group_preloads 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 end
def list_ammo_groups_for_container( @spec list_ammo_groups_for_container_filter_type(Queryable.t(), AmmoType.class() | :all) ::
%Container{id: container_id, user_id: user_id}, Queryable.t()
%User{id: user_id}, defp list_ammo_groups_for_container_filter_type(query, :rifle),
false = _include_empty do: query |> where([at: at], at.class == :rifle)
) do
Repo.all( defp list_ammo_groups_for_container_filter_type(query, :pistol),
from ag in AmmoGroup, do: query |> where([at: at], at.class == :pistol)
where: ag.container_id == ^container_id,
where: ag.user_id == ^user_id, defp list_ammo_groups_for_container_filter_type(query, :shotgun),
where: not (ag.count == 0), do: query |> where([at: at], at.class == :shotgun)
preload: ^@ammo_group_preloads
defp list_ammo_groups_for_container_filter_type(query, _all), do: query
@doc """
Returns a count of ammo_groups.
## Examples
iex> get_ammo_groups_count!(%User{id: 123})
3
iex> get_ammo_groups_count!(%User{id: 123}, true)
4
"""
@spec get_ammo_groups_count!(User.t()) :: integer()
@spec get_ammo_groups_count!(User.t(), show_used :: boolean()) :: integer()
def get_ammo_groups_count!(%User{id: user_id}, show_used \\ false) do
from(ag in AmmoGroup,
as: :ag,
where: ag.user_id == ^user_id,
select: count(ag.id),
distinct: true
) )
|> get_ammo_groups_count_show_used(show_used)
|> Repo.one() || 0
end 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 """ @doc """
Returns the count of ammo_groups for an ammo type. 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()) :: 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() non_neg_integer()
def get_ammo_groups_count_for_type( def get_ammo_groups_count_for_type(
%AmmoType{id: ammo_type_id} = ammo_type, %AmmoType{id: ammo_type_id} = ammo_type,
user, user,
include_empty \\ false show_used \\ false
) do ) do
[ammo_type] [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) |> Map.get(ammo_type_id, 0)
end end
@ -510,28 +561,31 @@ defmodule Cannery.Ammo do
""" """
@spec get_ammo_groups_count_for_types([AmmoType.t()], User.t()) :: @spec get_ammo_groups_count_for_types([AmmoType.t()], User.t()) ::
%{optional(AmmoType.id()) => non_neg_integer()} %{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()} %{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_type_ids =
ammo_types ammo_types
|> Enum.map(fn %AmmoType{id: ammo_type_id, user_id: ^user_id} -> ammo_type_id end) |> Enum.map(fn %AmmoType{id: ammo_type_id, user_id: ^user_id} -> ammo_type_id end)
from(ag in AmmoGroup, from(ag in AmmoGroup,
as: :ag,
where: ag.user_id == ^user_id, where: ag.user_id == ^user_id,
where: ag.ammo_type_id in ^ammo_type_ids, where: ag.ammo_type_id in ^ammo_type_ids,
group_by: ag.ammo_type_id, group_by: ag.ammo_type_id,
select: {ag.ammo_type_id, count(ag.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() |> Repo.all()
|> Map.new() |> Map.new()
end 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 defp get_ammo_groups_count_for_types_maybe_show_used(query, _false) do
query |> where([ag], not (ag.count == 0)) query |> where([ag: ag], not (ag.count == 0))
end end
@doc """ @doc """
@ -628,7 +682,7 @@ defmodule Cannery.Ammo do
Repo.all( Repo.all(
from ag in AmmoGroup, from ag in AmmoGroup,
where: ag.container_id in ^container_ids, where: ag.container_id in ^container_ids,
where: ag.count != 0, where: ag.count > 0,
group_by: ag.container_id, group_by: ag.container_id,
select: {ag.container_id, count(ag.id)} select: {ag.container_id, count(ag.id)}
) )
@ -690,17 +744,20 @@ defmodule Cannery.Ammo do
iex> list_ammo_groups(%User{id: 123}) iex> list_ammo_groups(%User{id: 123})
[%AmmoGroup{}, ...] [%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"}, ...] [%AmmoGroup{notes: "My cool ammo group"}, ...]
""" """
@spec list_ammo_groups(User.t()) :: [AmmoGroup.t()] @spec list_ammo_groups(search :: String.t() | nil, AmmoType.class() | :all, User.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()) ::
[AmmoGroup.t()] [AmmoGroup.t()]
def list_ammo_groups(search \\ nil, include_empty \\ false, %{id: user_id}) do @spec list_ammo_groups(
from( search :: nil | String.t(),
ag in AmmoGroup, 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, as: :ag,
join: at in assoc(ag, :ammo_type), join: at in assoc(ag, :ammo_type),
as: :at, as: :at,
@ -718,17 +775,32 @@ defmodule Cannery.Ammo do
distinct: ag.id, distinct: ag.id,
preload: ^@ammo_group_preloads 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) |> list_ammo_groups_search(search)
|> Repo.all() |> Repo.all()
end 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 defp list_ammo_groups_filter_on_type(query, :pistol),
query |> where([ag], not (ag.count == 0)) 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 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, nil), do: query
defp list_ammo_groups_search(query, ""), do: query defp list_ammo_groups_search(query, ""), do: query
@ -984,13 +1056,21 @@ defmodule Cannery.Ammo do
@spec create_ammo_groups(attrs :: map(), multiplier :: non_neg_integer(), User.t()) :: @spec create_ammo_groups(attrs :: map(), multiplier :: non_neg_integer(), User.t()) ::
{:ok, {count :: non_neg_integer(), [AmmoGroup.t()] | nil}} {:ok, {count :: non_neg_integer(), [AmmoGroup.t()] | nil}}
| {:error, AmmoGroup.changeset()} | {:error, AmmoGroup.changeset()}
def create_ammo_groups( def create_ammo_groups(attrs, multiplier, %User{} = user) do
%{"ammo_type_id" => ammo_type_id, "container_id" => container_id} = attrs, attrs
multiplier, |> Map.new(fn {k, v} -> {to_string(k), v} end)
%User{} = user |> do_create_ammo_groups(multiplier, user)
) end
when multiplier >= 1 and multiplier <= @ammo_group_create_limit and
not (ammo_type_id |> is_nil()) and not (container_id |> is_nil()) do defp do_create_ammo_groups(
%{"ammo_type_id" => ammo_type_id, "container_id" => container_id} = attrs,
multiplier,
user
)
when multiplier >= 1 and
multiplier <= @ammo_group_create_limit and
ammo_type_id |> is_binary() and
container_id |> is_binary() do
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
changesets = changesets =
@ -1026,12 +1106,12 @@ defmodule Cannery.Ammo do
end end
end end
def create_ammo_groups( defp do_create_ammo_groups(
%{"ammo_type_id" => ammo_type_id, "container_id" => container_id} = attrs, %{"ammo_type_id" => ammo_type_id, "container_id" => container_id} = attrs,
_multiplier, _multiplier,
user user
) )
when is_binary(ammo_type_id) and is_binary(container_id) do when is_binary(ammo_type_id) and is_binary(container_id) do
changeset = changeset =
%AmmoGroup{} %AmmoGroup{}
|> AmmoGroup.create_changeset( |> AmmoGroup.create_changeset(
@ -1045,7 +1125,7 @@ defmodule Cannery.Ammo do
{:error, changeset} {:error, changeset}
end 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)} {:error, %AmmoGroup{} |> AmmoGroup.create_changeset(nil, nil, user, invalid_attrs)}
end end

View File

@ -42,30 +42,47 @@ defmodule Cannery.Ammo.AmmoType do
field :name, :string field :name, :string
field :desc, :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_type, :string
field :bullet_core, :string field :bullet_core, :string
field :cartridge, :string # also gauge for shotguns
field :caliber, :string field :caliber, :string
field :case_material, :string field :case_material, :string
field :jacket_type, :string
field :muzzle_velocity, :integer
field :powder_type, :string field :powder_type, :string
field :powder_grains_per_charge, :integer
field :grains, :integer field :grains, :integer
field :pressure, :string field :pressure, :string
field :primer_type, :string field :primer_type, :string
field :firing_type, :string field :firing_type, :string
field :manufacturer, :string
field :upc, :string
field :tracer, :boolean, default: false field :tracer, :boolean, default: false
field :incendiary, :boolean, default: false field :incendiary, :boolean, default: false
field :blank, :boolean, default: false field :blank, :boolean, default: false
field :corrosive, :boolean, default: false field :corrosive, :boolean, default: false
field :manufacturer, :string # rifle/pistol fields
field :upc, :string 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 field :user_id, :binary_id
has_many :ammo_groups, AmmoGroup has_many :ammo_groups, AmmoGroup
timestamps() timestamps()
@ -75,6 +92,7 @@ defmodule Cannery.Ammo.AmmoType do
id: id(), id: id(),
name: String.t(), name: String.t(),
desc: String.t() | nil, desc: String.t() | nil,
class: class(),
bullet_type: String.t() | nil, bullet_type: String.t() | nil,
bullet_core: String.t() | nil, bullet_core: String.t() | nil,
cartridge: String.t() | nil, cartridge: String.t() | nil,
@ -88,6 +106,16 @@ defmodule Cannery.Ammo.AmmoType do
pressure: String.t() | nil, pressure: String.t() | nil,
primer_type: String.t() | nil, primer_type: String.t() | nil,
firing_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(), tracer: boolean(),
incendiary: boolean(), incendiary: boolean(),
blank: boolean(), blank: boolean(),
@ -102,12 +130,14 @@ defmodule Cannery.Ammo.AmmoType do
@type new_ammo_type :: %__MODULE__{} @type new_ammo_type :: %__MODULE__{}
@type id :: UUID.t() @type id :: UUID.t()
@type changeset :: Changeset.t(t() | new_ammo_type()) @type changeset :: Changeset.t(t() | new_ammo_type())
@type class :: :rifle | :shotgun | :pistol | nil
@spec changeset_fields() :: [atom()] @spec changeset_fields() :: [atom()]
defp changeset_fields, defp changeset_fields,
do: [ do: [
:name, :name,
:desc, :desc,
:class,
:bullet_type, :bullet_type,
:bullet_core, :bullet_core,
:cartridge, :cartridge,
@ -121,6 +151,16 @@ defmodule Cannery.Ammo.AmmoType do
:pressure, :pressure,
:primer_type, :primer_type,
:firing_type, :firing_type,
:wadding,
:shot_type,
:shot_material,
:shot_size,
:unfired_length,
:brass_height,
:chamber_size,
:load_grains,
:shot_charge_weight,
:dram_equivalent,
:tracer, :tracer,
:incendiary, :incendiary,
:blank, :blank,
@ -129,20 +169,55 @@ defmodule Cannery.Ammo.AmmoType do
:upc :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 @doc false
@spec create_changeset(new_ammo_type(), User.t(), attrs :: map()) :: changeset() @spec create_changeset(new_ammo_type(), User.t(), attrs :: map()) :: changeset()
def create_changeset(ammo_type, %User{id: user_id}, attrs) do def create_changeset(ammo_type, %User{id: user_id}, attrs) do
ammo_type changeset =
|> change(user_id: user_id) ammo_type
|> cast(attrs, changeset_fields()) |> 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]) |> validate_required([:name, :user_id])
end end
@doc false @doc false
@spec update_changeset(t() | new_ammo_type(), attrs :: map()) :: changeset() @spec update_changeset(t() | new_ammo_type(), attrs :: map()) :: changeset()
def update_changeset(ammo_type, attrs) do def update_changeset(ammo_type, attrs) do
ammo_type changeset =
|> cast(attrs, changeset_fields()) ammo_type
|> cast(attrs, changeset_fields())
string_fields()
|> Enum.reduce(changeset, fn field, acc -> acc |> validate_length(field, max: 255) end)
|> validate_required(:name) |> validate_required(:name)
end end
end end

View File

@ -53,6 +53,8 @@ defmodule Cannery.Containers.Container do
container container
|> change(user_id: user_id) |> change(user_id: user_id)
|> cast(attrs, [:name, :desc, :type, :location]) |> cast(attrs, [:name, :desc, :type, :location])
|> validate_length(:name, max: 255)
|> validate_length(:type, max: 255)
|> validate_required([:name, :type, :user_id]) |> validate_required([:name, :type, :user_id])
end end
@ -61,6 +63,8 @@ defmodule Cannery.Containers.Container do
def update_changeset(container, attrs) do def update_changeset(container, attrs) do
container container
|> cast(attrs, [:name, :desc, :type, :location]) |> cast(attrs, [:name, :desc, :type, :location])
|> validate_length(:name, max: 255)
|> validate_length(:type, max: 255)
|> validate_required([:name, :type]) |> validate_required([:name, :type])
end end
end end

View File

@ -47,6 +47,9 @@ defmodule Cannery.Containers.Tag do
tag tag
|> change(user_id: user_id) |> change(user_id: user_id)
|> cast(attrs, [:name, :bg_color, :text_color]) |> 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]) |> validate_required([:name, :bg_color, :text_color, :user_id])
end end
@ -55,6 +58,9 @@ defmodule Cannery.Containers.Tag do
def update_changeset(tag, attrs) do def update_changeset(tag, attrs) do
tag tag
|> cast(attrs, [:name, :bg_color, :text_color]) |> 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]) |> validate_required([:name, :bg_color, :text_color])
end end
end end

View File

@ -39,6 +39,7 @@
<%= textarea(f, :notes, <%= textarea(f, :notes,
id: "add-shot-group-form-notes", id: "add-shot-group-form-notes",
class: "input input-primary col-span-2", class: "input input-primary col-span-2",
maxlength: 255,
placeholder: gettext("Really great weather"), placeholder: gettext("Really great weather"),
phx_hook: "MaintainAttrs", phx_hook: "MaintainAttrs",
phx_update: "ignore" phx_update: "ignore"

View File

@ -5,6 +5,7 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do
use CanneryWeb, :live_component use CanneryWeb, :live_component
alias Cannery.{Accounts.User, Ammo.AmmoGroup, ComparableDate} alias Cannery.{Accounts.User, Ammo.AmmoGroup, ComparableDate}
alias Cannery.{ActivityLog, Ammo, Containers} alias Cannery.{ActivityLog, Ammo, Containers}
alias CanneryWeb.Components.TableComponent
alias Ecto.UUID alias Ecto.UUID
alias Phoenix.LiveView.{Rendered, Socket} alias Phoenix.LiveView.{Rendered, Socket}
@ -54,59 +55,47 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do
} = socket } = socket
) do ) do
columns = columns =
if actions == [] do []
[] |> TableComponent.maybe_compose_columns(
else %{label: gettext("Actions"), key: :actions, sortable: false},
[%{label: gettext("Actions"), key: :actions, sortable: false}] actions != []
end )
|> TableComponent.maybe_compose_columns(%{
columns = [ label: gettext("Last used on"),
%{label: gettext("Purchased on"), key: :purchased_on, type: ComparableDate}, key: :used_up_on,
%{label: gettext("Last used on"), key: :used_up_on, type: ComparableDate} | columns type: ComparableDate
] })
|> TableComponent.maybe_compose_columns(%{
columns = label: gettext("Purchased on"),
if container == [] do key: :purchased_on,
columns type: ComparableDate
else })
[%{label: gettext("Container"), key: :container} | columns] |> TableComponent.maybe_compose_columns(
end %{label: gettext("Container"), key: :container},
container != []
columns = )
if range == [] do |> TableComponent.maybe_compose_columns(
columns %{label: gettext("Range"), key: :range},
else range != []
[%{label: gettext("Range"), key: :range} | columns] )
end |> TableComponent.maybe_compose_columns(%{label: gettext("CPR"), key: :cpr})
|> TableComponent.maybe_compose_columns(%{label: gettext("Price paid"), key: :price_paid})
columns = [ |> TableComponent.maybe_compose_columns(
%{label: gettext("Price paid"), key: :price_paid}, %{label: gettext("% left"), key: :remaining},
%{label: gettext("CPR"), key: :cpr} show_used
| columns )
] |> TableComponent.maybe_compose_columns(
%{label: gettext("Original Count"), key: :original_count},
columns = show_used
if show_used do )
[ |> TableComponent.maybe_compose_columns(%{
%{label: gettext("Original Count"), key: :original_count}, label: if(show_used, do: gettext("Current Count"), else: gettext("Count")),
%{label: gettext("% left"), key: :remaining} key: :count
| columns })
] |> TableComponent.maybe_compose_columns(
else %{label: gettext("Ammo type"), key: :ammo_type},
columns ammo_type != []
end )
columns = [
%{label: if(show_used, do: gettext("Current Count"), else: gettext("Count")), key: :count}
| columns
]
columns =
if ammo_type == [] do
columns
else
[%{label: gettext("Ammo type"), key: :ammo_type} | columns]
end
containers = containers =
ammo_groups ammo_groups
@ -140,12 +129,7 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do
def render(assigns) do def render(assigns) do
~H""" ~H"""
<div id={@id} class="w-full"> <div id={@id} class="w-full">
<.live_component <.live_component module={TableComponent} id={"table-#{@id}"} columns={@columns} rows={@rows} />
module={CanneryWeb.Components.TableComponent}
id={"table-#{@id}"}
columns={@columns}
rows={@rows}
/>
</div> </div>
""" """
end end

View File

@ -4,6 +4,7 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do
""" """
use CanneryWeb, :live_component use CanneryWeb, :live_component
alias Cannery.{Accounts.User, ActivityLog, Ammo, Ammo.AmmoType} alias Cannery.{Accounts.User, ActivityLog, Ammo, Ammo.AmmoType}
alias CanneryWeb.Components.TableComponent
alias Ecto.UUID alias Ecto.UUID
alias Phoenix.LiveView.{Rendered, Socket} alias Phoenix.LiveView.{Rendered, Socket}
@ -12,6 +13,7 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do
%{ %{
required(:id) => UUID.t(), required(:id) => UUID.t(),
required(:current_user) => User.t(), required(:current_user) => User.t(),
optional(:class) => AmmoType.class() | nil,
optional(:show_used) => boolean(), optional(:show_used) => boolean(),
optional(:ammo_types) => [AmmoType.t()], optional(:ammo_types) => [AmmoType.t()],
optional(:actions) => Rendered.t(), optional(:actions) => Rendered.t(),
@ -24,6 +26,7 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do
socket socket
|> assign(assigns) |> assign(assigns)
|> assign_new(:show_used, fn -> false end) |> assign_new(:show_used, fn -> false end)
|> assign_new(:class, fn -> :all end)
|> assign_new(:actions, fn -> [] end) |> assign_new(:actions, fn -> [] end)
|> display_ammo_types() |> display_ammo_types()
@ -36,90 +39,118 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do
ammo_types: ammo_types, ammo_types: ammo_types,
current_user: current_user, current_user: current_user,
show_used: show_used, show_used: show_used,
class: class,
actions: actions actions: actions
} }
} = socket } = socket
) do ) 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("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("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 type"), key: :powder_type, type: :string},
%{ %{
label: gettext("Powder grains per charge"), label: gettext("Powder grains per charge"),
key: :powder_grains_per_charge, key: :powder_grains_per_charge,
type: :string type: :string
}, },
%{label: gettext("Grains"), key: :grains, type: :string},
%{label: gettext("Pressure"), key: :pressure, 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("Primer type"), key: :primer_type, type: :string},
%{label: gettext("Firing type"), key: :firing_type, type: :string}, %{label: gettext("Firing type"), key: :firing_type, type: :string},
%{label: gettext("Tracer"), key: :tracer, type: :boolean}, %{label: gettext("Tracer"), key: :tracer, type: :atom},
%{label: gettext("Incendiary"), key: :incendiary, type: :boolean}, %{label: gettext("Incendiary"), key: :incendiary, type: :atom},
%{label: gettext("Blank"), key: :blank, type: :boolean}, %{label: gettext("Blank"), key: :blank, type: :atom},
%{label: gettext("Corrosive"), key: :corrosive, type: :boolean}, %{label: gettext("Corrosive"), key: :corrosive, type: :atom},
%{label: gettext("Manufacturer"), key: :manufacturer, type: :string}, %{label: gettext("Manufacturer"), key: :manufacturer, type: :string}
%{label: gettext("UPC"), key: "upc", type: :string}
] ]
|> Enum.filter(fn %{key: key, type: type} -> |> Enum.filter(fn %{key: key, type: type} ->
# remove columns if all values match defaults # 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 ammo_types
|> Enum.any?(fn ammo_type -> |> Enum.any?(fn ammo_type -> Map.get(ammo_type, key, default_value) != default_value end)
not (ammo_type |> Map.get(key) == default_value)
end)
end) end)
|> Kernel.++([
%{label: gettext("Rounds"), key: :round_count, type: :round_count} columns =
]) [%{label: gettext("Actions"), key: "actions", type: :actions, sortable: false}]
|> Kernel.++( |> TableComponent.maybe_compose_columns(%{
if show_used do label: gettext("Average CPR"),
[ key: :avg_price_paid,
%{ type: :avg_price_paid
label: gettext("Used rounds"), })
key: :used_round_count, |> TableComponent.maybe_compose_columns(
type: :used_round_count %{
}, label: gettext("Total ever packs"),
%{ key: :historical_pack_count,
label: gettext("Total ever rounds"), type: :historical_pack_count
key: :historical_round_count, },
type: :historical_round_count show_used
}
]
else
[]
end
) )
|> Kernel.++([%{label: gettext("Packs"), key: :ammo_count, type: :ammo_count}]) |> TableComponent.maybe_compose_columns(
|> Kernel.++( %{
if show_used do label: gettext("Used packs"),
[ key: :used_pack_count,
%{ type: :used_pack_count
label: gettext("Used packs"), },
key: :used_pack_count, show_used
type: :used_pack_count
},
%{
label: gettext("Total ever packs"),
key: :historical_pack_count,
type: :historical_pack_count
}
]
else
[]
end
) )
|> Kernel.++([ |> TableComponent.maybe_compose_columns(%{
%{label: gettext("Average CPR"), key: :avg_price_paid, type: :avg_price_paid}, label: gettext("Packs"),
%{label: gettext("Actions"), key: "actions", type: :actions, sortable: false} key: :ammo_count,
]) type: :ammo_count
})
|> TableComponent.maybe_compose_columns(
%{
label: gettext("Total ever rounds"),
key: :historical_round_count,
type: :historical_round_count
},
show_used
)
|> TableComponent.maybe_compose_columns(
%{
label: gettext("Used rounds"),
key: :used_round_count,
type: :used_round_count
},
show_used
)
|> TableComponent.maybe_compose_columns(%{
label: gettext("Rounds"),
key: :round_count,
type: :round_count
})
|> TableComponent.maybe_compose_columns(filtered_columns)
|> TableComponent.maybe_compose_columns(
%{label: gettext("Class"), key: :type, type: :atom},
class in [:all, nil]
)
|> TableComponent.maybe_compose_columns(%{label: gettext("Name"), key: :name, type: :name})
round_counts = ammo_types |> Ammo.get_round_count_for_ammo_types(current_user) round_counts = ammo_types |> Ammo.get_round_count_for_ammo_types(current_user)
packs_count = ammo_types |> Ammo.get_ammo_groups_count_for_types(current_user) packs_count = ammo_types |> Ammo.get_ammo_groups_count_for_types(current_user)
@ -162,12 +193,7 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do
def render(assigns) do def render(assigns) do
~H""" ~H"""
<div id={@id} class="w-full"> <div id={@id} class="w-full">
<.live_component <.live_component module={TableComponent} id={"table-#{@id}"} columns={@columns} rows={@rows} />
module={CanneryWeb.Components.TableComponent}
id={"table-#{@id}"}
columns={@columns}
rows={@rows}
/>
</div> </div>
""" """
end end
@ -179,7 +205,7 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do
end) end)
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() do: ammo_type |> Map.get(key) |> humanize()
defp get_ammo_type_value(:round_count, _key, %{id: ammo_type_id}, %{round_counts: round_counts}), defp get_ammo_type_value(:round_count, _key, %{id: ammo_type_id}, %{round_counts: round_counts}),

View File

@ -6,6 +6,7 @@
p-8 flex flex-col justify-center items-center cursor-auto" p-8 flex flex-col justify-center items-center cursor-auto"
style="background-color: rgba(0,0,0,0.4);" style="background-color: rgba(0,0,0,0.4);"
phx-remove={hide_modal()} phx-remove={hide_modal()}
aria-label={gettext("Close modal")}
> >
<span class="hidden"></span> <span class="hidden"></span>
</.link> </.link>
@ -31,6 +32,7 @@
text-gray-500 hover:text-gray-800 text-gray-500 hover:text-gray-800
transition-all duration-500 ease-in-out" transition-all duration-500 ease-in-out"
phx-remove={hide_modal()} phx-remove={hide_modal()}
aria-label={gettext("Close modal")}
> >
<i class="fa-fw fa-lg fas fa-times"></i> <i class="fa-fw fa-lg fas fa-times"></i>
</.link> </.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 <input
id={@id || @action} id={@id || @action}
type="checkbox" type="checkbox"
@ -23,7 +23,7 @@
</div> </div>
<span <span
id={"#{@id || @action}-label"} 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) %> <%= render_slot(@inner_block) %>
</span> </span>

View File

@ -135,4 +135,25 @@ defmodule CanneryWeb.Components.TableComponent do
sort_mode sort_mode
) )
end 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 end

View File

@ -3,7 +3,7 @@ defmodule CanneryWeb.ExportController do
alias Cannery.{ActivityLog, Ammo, Containers} alias Cannery.{ActivityLog, Ammo, Containers}
def export(%{assigns: %{current_user: current_user}} = conn, %{"mode" => "json"}) do 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) 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) 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) ammo_group_counts = ammo_types |> Ammo.get_ammo_groups_count_for_types(current_user)
@ -28,7 +28,7 @@ defmodule CanneryWeb.ExportController do
}) })
end) 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) used_counts = ammo_groups |> ActivityLog.get_used_counts(current_user)
original_counts = ammo_groups |> Ammo.get_original_counts(current_user) original_counts = ammo_groups |> Ammo.get_original_counts(current_user)
cprs = ammo_groups |> Ammo.get_cprs(current_user) cprs = ammo_groups |> Ammo.get_cprs(current_user)
@ -48,7 +48,7 @@ defmodule CanneryWeb.ExportController do
}) })
end) end)
shot_groups = ActivityLog.list_shot_groups(current_user) shot_groups = ActivityLog.list_shot_groups(:all, current_user)
containers = containers =
Containers.list_containers(current_user) Containers.list_containers(current_user)

View File

@ -26,7 +26,7 @@ defmodule CanneryWeb.AmmoGroupLive.FormComponent do
socket = socket =
socket socket
|> assign(:ammo_group_create_limit, @ammo_group_create_limit) |> 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) |> assign_new(:containers, fn -> Containers.list_containers(current_user) end)
params = params =

View File

@ -8,11 +8,16 @@ defmodule CanneryWeb.AmmoGroupLive.Index do
@impl true @impl true
def mount(%{"search" => search}, _session, socket) do 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 end
def mount(_params, _session, socket) do 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 end
@impl true @impl true
@ -119,10 +124,36 @@ defmodule CanneryWeb.AmmoGroupLive.Index do
{:noreply, socket} {:noreply, socket}
end 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( 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 ) 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) ammo_types_count = Ammo.get_ammo_types_count!(current_user)
containers_count = Containers.get_containers_count!(current_user) containers_count = Containers.get_containers_count!(current_user)
@ -130,7 +161,8 @@ defmodule CanneryWeb.AmmoGroupLive.Index do
|> assign( |> assign(
ammo_groups: ammo_groups, ammo_groups: ammo_groups,
ammo_types_count: ammo_types_count, ammo_types_count: ammo_types_count,
containers_count: containers_count containers_count: containers_count,
ammo_groups_count: ammo_groups_count
) )
end end
end end

View File

@ -3,14 +3,6 @@
<%= gettext("Ammo") %> <%= gettext("Ammo") %>
</h1> </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 %> <%= cond do %>
<% @containers_count == 0 -> %> <% @containers_count == 0 -> %>
<div class="flex justify-center items-center"> <div class="flex justify-center items-center">
@ -32,7 +24,12 @@
<%= dgettext("actions", "add an ammo type first") %> <%= dgettext("actions", "add an ammo type first") %>
</.link> </.link>
</div> </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"> <.link patch={Routes.ammo_group_index_path(Endpoint, :new)} class="btn btn-primary">
<%= dgettext("actions", "Add your first box!") %> <%= dgettext("actions", "Add your first box!") %>
</.link> </.link>
@ -40,144 +37,170 @@
<.link patch={Routes.ammo_group_index_path(Endpoint, :new)} class="btn btn-primary"> <.link patch={Routes.ammo_group_index_path(Endpoint, :new)} class="btn btn-primary">
<%= dgettext("actions", "Add Ammo") %> <%= dgettext("actions", "Add Ammo") %>
</.link> </.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 <.form
:let={f} :let={f}
for={%{}} for={%{}}
as={:search} as={:ammo_type}
phx-change="search" phx-change="change_class"
phx-submit="search" phx-submit="change_class"
class="grow self-stretch flex flex-col items-stretch" class="flex items-center"
> >
<%= text_input(f, :search_term, <%= label(f, :class, gettext("Class"),
class: "input input-primary", class: "title text-primary-600 text-lg text-center"
value: @search, ) %>
role: "search",
phx_debounce: 300,
placeholder: gettext("Search ammo")
) %>
</.form>
<.toggle_button action="toggle_show_used" value={@show_used}> <%= select(
<span class="title text-lg text-primary-600"> f,
<%= gettext("Show used") %> :class,
</span> [
</.toggle_button> {gettext("All"), :all},
</div> {gettext("Rifle"), :rifle},
{gettext("Shotgun"), :shotgun},
{gettext("Pistol"), :pistol}
],
class: "mx-2 my-1 min-w-md input input-primary",
value: @class
) %>
</.form>
<%= if @ammo_groups |> Enum.empty?() do %> <.form
<h2 class="title text-xl text-primary-600"> :let={f}
<%= gettext("No Ammo") %> for={%{}}
<%= display_emoji("😔") %> as={:search}
</h2> phx-change="search"
<% else %> phx-submit="search"
<.live_component class="grow flex items-center"
module={CanneryWeb.Components.AmmoGroupTableComponent} >
id="ammo-group-index-table" <%= text_input(f, :search_term,
ammo_groups={@ammo_groups} class: "grow input input-primary",
current_user={@current_user} value: @search,
show_used={@show_used} role: "search",
> phx_debounce: 300,
<:ammo_type :let={%{name: ammo_type_name} = ammo_type}> placeholder: gettext("Search ammo")
<.link navigate={Routes.ammo_type_show_path(Endpoint, :show, ammo_type)} class="link"> ) %>
<%= ammo_type_name %> </.form>
</.link>
</:ammo_type>
<:range :let={ammo_group}>
<div class="min-w-20 py-2 px-4 h-full flex flew-wrap justify-center items-center">
<button
type="button"
class="mx-2 my-1 text-sm btn btn-primary"
phx-click="toggle_staged"
phx-value-ammo_group_id={ammo_group.id}
>
<%= if ammo_group.staged,
do: dgettext("actions", "Unstage"),
else: dgettext("actions", "Stage") %>
</button>
<.link <.toggle_button action="toggle_show_used" value={@show_used}>
patch={Routes.ammo_group_index_path(Endpoint, :add_shot_group, ammo_group)} <span class="title text-lg text-primary-600">
class="mx-2 my-1 text-sm btn btn-primary" <%= gettext("Show used") %>
> </span>
<%= dgettext("actions", "Record shots") %> </.toggle_button>
</.link> </div>
</div>
</:range>
<:container :let={{ammo_group, %{name: container_name} = container}}>
<div class="min-w-20 py-2 px-4 h-full flex flew-wrap justify-center items-center">
<.link
navigate={Routes.container_show_path(Endpoint, :show, container)}
class="mx-2 my-1 link"
>
<%= container_name %>
</.link>
<.link <%= if @ammo_groups |> Enum.empty?() do %>
patch={Routes.ammo_group_index_path(Endpoint, :move, ammo_group)} <h2 class="title text-xl text-primary-600">
class="mx-2 my-1 text-sm btn btn-primary" <%= gettext("No Ammo") %>
> <%= display_emoji("😔") %>
<%= dgettext("actions", "Move ammo") %> </h2>
</.link> <% else %>
</div> <.live_component
</:container> module={CanneryWeb.Components.AmmoGroupTableComponent}
<:actions :let={%{count: ammo_group_count} = ammo_group}> id="ammo-group-index-table"
<div class="py-2 px-4 h-full space-x-4 flex justify-center items-center"> ammo_groups={@ammo_groups}
<.link current_user={@current_user}
navigate={Routes.ammo_group_show_path(Endpoint, :show, ammo_group)} show_used={@show_used}
class="text-primary-600 link" >
aria-label={ <:ammo_type :let={%{name: ammo_type_name} = ammo_type}>
dgettext("actions", "View ammo group of %{ammo_group_count} bullets", <.link navigate={Routes.ammo_type_show_path(Endpoint, :show, ammo_type)} class="link">
ammo_group_count: ammo_group_count <%= ammo_type_name %>
) </.link>
} </:ammo_type>
> <:range :let={ammo_group}>
<i class="fa-fw fa-lg fas fa-eye"></i> <div class="min-w-20 py-2 px-4 h-full flex flew-wrap justify-center items-center">
</.link> <button
type="button"
class="mx-2 my-1 text-sm btn btn-primary"
phx-click="toggle_staged"
phx-value-ammo_group_id={ammo_group.id}
>
<%= if ammo_group.staged,
do: dgettext("actions", "Unstage"),
else: dgettext("actions", "Stage") %>
</button>
<.link <.link
patch={Routes.ammo_group_index_path(Endpoint, :edit, ammo_group)} patch={Routes.ammo_group_index_path(Endpoint, :add_shot_group, ammo_group)}
class="text-primary-600 link" class="mx-2 my-1 text-sm btn btn-primary"
aria-label={ >
dgettext("actions", "Edit ammo group of %{ammo_group_count} bullets", <%= dgettext("actions", "Record shots") %>
ammo_group_count: ammo_group_count </.link>
) </div>
} </:range>
> <:container :let={{ammo_group, %{name: container_name} = container}}>
<i class="fa-fw fa-lg fas fa-edit"></i> <div class="min-w-20 py-2 px-4 h-full flex flew-wrap justify-center items-center">
</.link> <.link
navigate={Routes.container_show_path(Endpoint, :show, container)}
class="mx-2 my-1 link"
>
<%= container_name %>
</.link>
<.link <.link
patch={Routes.ammo_group_index_path(Endpoint, :clone, ammo_group)} patch={Routes.ammo_group_index_path(Endpoint, :move, ammo_group)}
class="text-primary-600 link" class="mx-2 my-1 text-sm btn btn-primary"
aria-label={ >
dgettext("actions", "Clone ammo group of %{ammo_group_count} bullets", <%= dgettext("actions", "Move ammo") %>
ammo_group_count: ammo_group_count </.link>
) </div>
} </:container>
> <:actions :let={%{count: ammo_group_count} = ammo_group}>
<i class="fa-fw fa-lg fas fa-copy"></i> <div class="py-2 px-4 h-full space-x-4 flex justify-center items-center">
</.link> <.link
navigate={Routes.ammo_group_show_path(Endpoint, :show, ammo_group)}
class="text-primary-600 link"
aria-label={
dgettext("actions", "View ammo group of %{ammo_group_count} bullets",
ammo_group_count: ammo_group_count
)
}
>
<i class="fa-fw fa-lg fas fa-eye"></i>
</.link>
<.link <.link
href="#" patch={Routes.ammo_group_index_path(Endpoint, :edit, ammo_group)}
class="text-primary-600 link" class="text-primary-600 link"
phx-click="delete" aria-label={
phx-value-id={ammo_group.id} dgettext("actions", "Edit ammo group of %{ammo_group_count} bullets",
data-confirm={dgettext("prompts", "Are you sure you want to delete this ammo?")} ammo_group_count: ammo_group_count
aria-label={ )
dgettext("actions", "Delete ammo group of %{ammo_group_count} bullets", }
ammo_group_count: ammo_group_count >
) <i class="fa-fw fa-lg fas fa-edit"></i>
} </.link>
>
<i class="fa-fw fa-lg fas fa-trash"></i> <.link
</.link> patch={Routes.ammo_group_index_path(Endpoint, :clone, ammo_group)}
</div> class="text-primary-600 link"
</:actions> aria-label={
</.live_component> dgettext("actions", "Clone ammo group of %{ammo_group_count} bullets",
ammo_group_count: ammo_group_count
)
}
>
<i class="fa-fw fa-lg fas fa-copy"></i>
</.link>
<.link
href="#"
class="text-primary-600 link"
phx-click="delete"
phx-value-id={ammo_group.id}
data-confirm={dgettext("prompts", "Are you sure you want to delete this ammo?")}
aria-label={
dgettext("actions", "Delete ammo group of %{ammo_group_count} bullets",
ammo_group_count: ammo_group_count
)
}
>
<i class="fa-fw fa-lg fas fa-trash"></i>
</.link>
</div>
</:actions>
</.live_component>
<% end %>
<% end %> <% end %>
</div> </div>

View File

@ -15,11 +15,24 @@
:if={@changeset.action && not @changeset.valid?()} :if={@changeset.action && not @changeset.valid?()}
class="invalid-feedback col-span-3 text-center" class="invalid-feedback col-span-3 text-center"
> >
<%= changeset_errors(@changeset) %> <%= dgettext("errors", "Oops, something went wrong! Please check the errors below.") %>
</div> </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") %> <%= 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") %> <%= error_tag(f, :name, "col-span-3 text-center") %>
<%= label(f, :desc, gettext("Description"), class: "title text-lg text-primary-600") %> <%= 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") %> <%= error_tag(f, :desc, "col-span-3 text-center") %>
<.link <h2 class="text-center title text-lg text-primary-600 col-span-3">
href="https://shootersreference.com/reloadingdata/bullet_abbreviations/" <%= gettext("Dimensions") %>
class="col-span-3 text-center link title text-md text-primary-600" </h2>
>
<%= 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") %> <%= if Changeset.get_field(@changeset, :class) in [:rifle, :pistol] do %>
<%= text_input(f, :bullet_core, <%= label(f, :cartridge, gettext("Cartridge"), class: "title text-lg text-primary-600") %>
class: "text-center col-span-2 input input-primary", <%= text_input(f, :cartridge,
placeholder: gettext("Steel") class: "text-center col-span-2 input input-primary",
) %> maxlength: 255,
<%= error_tag(f, :bullet_core, "col-span-3 text-center") %> placeholder: gettext("5.56x46mm NATO")
) %>
<%= error_tag(f, :cartridge, "col-span-3 text-center") %>
<% else %>
<%= hidden_input(f, :cartridge, value: nil) %>
<% end %>
<%= label(f, :cartridge, gettext("Cartridge"), class: "title text-lg text-primary-600") %> <%= label(
<%= text_input(f, :cartridge, f,
class: "text-center col-span-2 input input-primary", :caliber,
placeholder: gettext("5.56x46mm NATO") if(Changeset.get_field(@changeset, :class) == :shotgun,
do: gettext("Gauge"),
else: gettext("Caliber")
),
class: "title text-lg text-primary-600"
) %> ) %>
<%= error_tag(f, :cartridge, "col-span-3 text-center") %>
<%= label(f, :caliber, gettext("Caliber"), class: "title text-lg text-primary-600") %>
<%= text_input(f, :caliber, <%= text_input(f, :caliber,
class: "text-center col-span-2 input input-primary", class: "text-center col-span-2 input input-primary",
maxlength: 255,
placeholder: gettext(".223") placeholder: gettext(".223")
) %> ) %>
<%= error_tag(f, :caliber, "col-span-3 text-center") %> <%= error_tag(f, :caliber, "col-span-3 text-center") %>
<%= label(f, :case_material, gettext("Case material"), class: "title text-lg text-primary-600") %> <%= if Changeset.get_field(@changeset, :class) == :shotgun do %>
<%= text_input(f, :case_material, <%= label(f, :unfired_length, gettext("Unfired shell length"),
class: "text-center col-span-2 input input-primary", class: "title text-lg text-primary-600"
placeholder: gettext("Brass") ) %>
) %> <%= text_input(f, :unfired_length,
<%= error_tag(f, :case_material, "col-span-3 text-center") %> class: "text-center col-span-2 input input-primary",
maxlength: 255
) %>
<%= error_tag(f, :unfired_length, "col-span-3 text-center") %>
<%= label(f, :jacket_type, gettext("Jacket type"), class: "title text-lg text-primary-600") %> <%= label(f, :brass_height, gettext("Brass height"), class: "title text-lg text-primary-600") %>
<%= text_input(f, :jacket_type, <%= text_input(f, :brass_height,
class: "text-center col-span-2 input input-primary", class: "text-center col-span-2 input input-primary",
placeholder: gettext("Bimetal") maxlength: 255
) %> ) %>
<%= error_tag(f, :case_material, "col-span-3 text-center") %> <%= error_tag(f, :brass_height, "col-span-3 text-center") %>
<%= label(f, :muzzle_velocity, gettext("Muzzle velocity"), <%= label(f, :chamber_size, gettext("Chamber size"), class: "title text-lg text-primary-600") %>
class: "title text-lg text-primary-600" <%= text_input(f, :chamber_size,
) %> class: "text-center col-span-2 input input-primary",
<%= number_input(f, :muzzle_velocity, maxlength: 255
step: "1", ) %>
class: "text-center col-span-2 input input-primary", <%= error_tag(f, :chamber_size, "col-span-3 text-center") %>
min: 1 <% else %>
) %> <%= hidden_input(f, :unfired_length, value: nil) %>
<%= error_tag(f, :muzzle_velocity, "col-span-3 text-center") %> <%= hidden_input(f, :brass_height, value: nil) %>
<%= hidden_input(f, :chamber_size, value: nil) %>
<% end %>
<%= label(f, :powder_type, gettext("Powder type"), class: "title text-lg text-primary-600") %> <h2 class="text-center title text-lg text-primary-600 col-span-3">
<%= text_input(f, :powder_type, class: "text-center col-span-2 input input-primary") %> <%= gettext("Projectile") %>
<%= error_tag(f, :powder_type, "col-span-3 text-center") %> </h2>
<%= 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") %>
<%= label(f, :grains, gettext("Grains"), class: "title text-lg text-primary-600") %> <%= label(f, :grains, gettext("Grains"), class: "title text-lg text-primary-600") %>
<%= number_input(f, :grains, <%= number_input(f, :grains,
@ -111,16 +117,186 @@
) %> ) %>
<%= error_tag(f, :grains, "col-span-3 text-center") %> <%= 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") %> <%= label(f, :pressure, gettext("Pressure"), class: "title text-lg text-primary-600") %>
<%= text_input(f, :pressure, <%= text_input(f, :pressure,
class: "text-center col-span-2 input input-primary", class: "text-center col-span-2 input input-primary",
maxlength: 255,
placeholder: gettext("+P") placeholder: gettext("+P")
) %> ) %>
<%= error_tag(f, :pressure, "col-span-3 text-center") %> <%= 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") %> <%= label(f, :primer_type, gettext("Primer type"), class: "title text-lg text-primary-600") %>
<%= text_input(f, :primer_type, <%= text_input(f, :primer_type,
class: "text-center col-span-2 input input-primary", class: "text-center col-span-2 input input-primary",
maxlength: 255,
placeholder: gettext("Boxer") placeholder: gettext("Boxer")
) %> ) %>
<%= error_tag(f, :primer_type, "col-span-3 text-center") %> <%= 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") %> <%= label(f, :firing_type, gettext("Firing type"), class: "title text-lg text-primary-600") %>
<%= text_input(f, :firing_type, <%= text_input(f, :firing_type,
class: "text-center col-span-2 input input-primary", class: "text-center col-span-2 input input-primary",
maxlength: 255,
placeholder: gettext("Centerfire") placeholder: gettext("Centerfire")
) %> ) %>
<%= error_tag(f, :firing_type, "col-span-3 text-center") %> <%= 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") %> <%= label(f, :tracer, gettext("Tracer"), class: "title text-lg text-primary-600") %>
<%= checkbox(f, :tracer, class: "text-center col-span-2 checkbox") %> <%= checkbox(f, :tracer, class: "text-center col-span-2 checkbox") %>
<%= error_tag(f, :tracer, "col-span-3 text-center") %> <%= error_tag(f, :tracer, "col-span-3 text-center") %>
@ -148,12 +329,22 @@
<%= checkbox(f, :corrosive, class: "text-center col-span-2 checkbox") %> <%= checkbox(f, :corrosive, class: "text-center col-span-2 checkbox") %>
<%= error_tag(f, :corrosive, "col-span-3 text-center") %> <%= 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") %> <%= 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") %> <%= error_tag(f, :manufacturer, "col-span-3 text-center") %>
<%= label(f, :upc, gettext("UPC"), class: "title text-lg text-primary-600") %> <%= 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") %> <%= error_tag(f, :upc, "col-span-3 text-center") %>
<%= submit(dgettext("actions", "Save"), <%= submit(dgettext("actions", "Save"),

View File

@ -8,11 +8,11 @@ defmodule CanneryWeb.AmmoTypeLive.Index do
@impl true @impl true
def mount(%{"search" => search}, _session, socket) do 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 end
def mount(_params, _session, socket) do 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 end
@impl true @impl true
@ -86,7 +86,29 @@ defmodule CanneryWeb.AmmoTypeLive.Index do
{:noreply, socket |> push_patch(to: search_path)} {:noreply, socket |> push_patch(to: search_path)}
end end
defp list_ammo_types(%{assigns: %{search: search, current_user: current_user}} = socket) do def handle_event("change_class", %{"ammo_type" => %{"class" => "rifle"}}, socket) do
socket |> assign(ammo_types: Ammo.list_ammo_types(search, current_user)) {: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
end end

View File

@ -3,7 +3,7 @@
<%= gettext("Catalog") %> <%= gettext("Catalog") %>
</h1> </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"> <h2 class="title text-xl text-primary-600">
<%= gettext("No Ammo types") %> <%= gettext("No Ammo types") %>
<%= display_emoji("😔") %> <%= display_emoji("😔") %>
@ -17,17 +17,41 @@
<%= dgettext("actions", "New Ammo type") %> <%= dgettext("actions", "New Ammo type") %>
</.link> </.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 <.form
:let={f} :let={f}
for={%{}} for={%{}}
as={:search} as={:search}
phx-change="search" phx-change="search"
phx-submit="search" phx-submit="search"
class="grow self-stretch flex flex-col items-stretch" class="grow flex items-center"
> >
<%= text_input(f, :search_term, <%= text_input(f, :search_term,
class: "input input-primary", class: "grow input input-primary",
value: @search, value: @search,
role: "search", role: "search",
phx_debounce: 300, phx_debounce: 300,
@ -55,6 +79,7 @@
ammo_types={@ammo_types} ammo_types={@ammo_types}
current_user={@current_user} current_user={@current_user}
show_used={@show_used} show_used={@show_used}
class={@class}
> >
<:actions :let={ammo_type}> <:actions :let={ammo_type}>
<div class="px-4 py-2 space-x-4 flex justify-center items-center"> <div class="px-4 py-2 space-x-4 flex justify-center items-center">

View File

@ -7,28 +7,6 @@ defmodule CanneryWeb.AmmoTypeLive.Show do
alias Cannery.{ActivityLog, Ammo, Ammo.AmmoType, Containers} alias Cannery.{ActivityLog, Ammo, Ammo.AmmoType, Containers}
alias CanneryWeb.Endpoint 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 @impl true
def mount(_params, _session, socket), def mount(_params, _session, socket),
do: {:ok, socket |> assign(show_used: false, view_table: true)} do: {:ok, socket |> assign(show_used: false, view_table: true)}
@ -65,8 +43,8 @@ defmodule CanneryWeb.AmmoTypeLive.Show do
socket, socket,
%AmmoType{name: ammo_type_name} = ammo_type %AmmoType{name: ammo_type_name} = ammo_type
) do ) do
fields_to_display = custom_fields? =
@fields_list fields_to_display(ammo_type)
|> Enum.any?(fn %{key: field, type: type} -> |> Enum.any?(fn %{key: field, type: type} ->
default_value = default_value =
case type do case type do
@ -125,8 +103,8 @@ defmodule CanneryWeb.AmmoTypeLive.Show do
packs_count: ammo_type |> Ammo.get_ammo_groups_count_for_type(current_user), packs_count: ammo_type |> Ammo.get_ammo_groups_count_for_type(current_user),
used_packs_count: used_packs_count, used_packs_count: used_packs_count,
historical_packs_count: historical_packs_count, historical_packs_count: historical_packs_count,
fields_list: @fields_list, fields_to_display: fields_to_display(ammo_type),
fields_to_display: fields_to_display custom_fields?: custom_fields?
) )
end end
@ -138,6 +116,48 @@ defmodule CanneryWeb.AmmoTypeLive.Show do
socket |> display_ammo_type(ammo_type) socket |> display_ammo_type(ammo_type)
end 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() @spec display_currency(float()) :: String.t()
defp display_currency(float), do: :erlang.float_to_binary(float, decimals: 2) defp display_currency(float), do: :erlang.float_to_binary(float, decimals: 2)
end end

View File

@ -42,9 +42,26 @@
<hr class="hr" /> <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"> <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 %> <%= if @ammo_type |> Map.get(key) do %>
<h3 class="title text-lg"> <h3 class="title text-lg">
<%= label %> <%= label %>

View File

@ -21,7 +21,8 @@
<%= label(f, :name, gettext("Name"), class: "title text-lg text-primary-600") %> <%= label(f, :name, gettext("Name"), class: "title text-lg text-primary-600") %>
<%= text_input(f, :name, <%= text_input(f, :name,
class: "input input-primary col-span-2", 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") %> <%= 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") %> <%= label(f, :type, gettext("Type"), class: "title text-lg text-primary-600") %>
<%= text_input(f, :type, <%= text_input(f, :type,
class: "input input-primary col-span-2", 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") %> <%= error_tag(f, :type, "col-span-3 text-center") %>

View File

@ -17,17 +17,17 @@
<%= dgettext("actions", "New Container") %> <%= dgettext("actions", "New Container") %>
</.link> </.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 <.form
:let={f} :let={f}
for={%{}} for={%{}}
as={:search} as={:search}
phx-change="search" phx-change="search"
phx-submit="search" phx-submit="search"
class="grow self-stretch flex flex-col items-stretch" class="grow flex items-center"
> >
<%= text_input(f, :search_term, <%= text_input(f, :search_term,
class: "input input-primary", class: "grow input input-primary",
value: @search, value: @search,
role: "search", role: "search",
phx_debounce: 300, phx_debounce: 300,
@ -41,80 +41,22 @@
</span> </span>
</.toggle_button> </.toggle_button>
</div> </div>
<% end %>
<%= if @containers |> Enum.empty?() do %> <%= if @containers |> Enum.empty?() do %>
<h2 class="title text-xl text-primary-600"> <h2 class="title text-xl text-primary-600">
<%= gettext("No containers") %> <%= gettext("No containers") %>
<%= display_emoji("😔") %> <%= display_emoji("😔") %>
</h2> </h2>
<% else %>
<%= if @view_table do %>
<.live_component
module={CanneryWeb.Components.ContainerTableComponent}
id="containers_index_table"
action={@live_action}
containers={@containers}
current_user={@current_user}
>
<:tag_actions :let={container}>
<div class="mx-4 my-2">
<.link
patch={Routes.container_index_path(Endpoint, :edit_tags, container)}
class="text-primary-600 link"
aria-label={
dgettext("actions", "Tag %{container_name}", container_name: container.name)
}
>
<i class="fa-fw fa-lg fas fa-tags"></i>
</.link>
</div>
</:tag_actions>
<:actions :let={container}>
<.link
patch={Routes.container_index_path(Endpoint, :edit, container)}
class="text-primary-600 link"
aria-label={
dgettext("actions", "Edit %{container_name}", container_name: container.name)
}
>
<i class="fa-fw fa-lg fas fa-edit"></i>
</.link>
<.link
patch={Routes.container_index_path(Endpoint, :clone, container)}
class="text-primary-600 link"
aria-label={
dgettext("actions", "Clone %{container_name}", container_name: container.name)
}
>
<i class="fa-fw fa-lg fas fa-copy"></i>
</.link>
<.link
href="#"
class="text-primary-600 link"
phx-click="delete"
phx-value-id={container.id}
data-confirm={
dgettext("prompts", "Are you sure you want to delete %{name}?", name: container.name)
}
aria-label={
dgettext("actions", "Delete %{container_name}", container_name: container.name)
}
>
<i class="fa-fw fa-lg fas fa-trash"></i>
</.link>
</:actions>
</.live_component>
<% else %> <% else %>
<div class="w-full flex flex-row flex-wrap justify-center items-stretch"> <%= if @view_table do %>
<.container_card <.live_component
:for={container <- @containers} module={CanneryWeb.Components.ContainerTableComponent}
container={container} id="containers_index_table"
action={@live_action}
containers={@containers}
current_user={@current_user} current_user={@current_user}
> >
<:tag_actions> <:tag_actions :let={container}>
<div class="mx-4 my-2"> <div class="mx-4 my-2">
<.link <.link
patch={Routes.container_index_path(Endpoint, :edit_tags, container)} patch={Routes.container_index_path(Endpoint, :edit_tags, container)}
@ -127,42 +69,104 @@
</.link> </.link>
</div> </div>
</:tag_actions> </:tag_actions>
<.link <:actions :let={container}>
patch={Routes.container_index_path(Endpoint, :edit, container)} <.link
class="text-primary-600 link" patch={Routes.container_index_path(Endpoint, :edit, container)}
aria-label={ class="text-primary-600 link"
dgettext("actions", "Edit %{container_name}", container_name: container.name) aria-label={
} dgettext("actions", "Edit %{container_name}", container_name: container.name)
> }
<i class="fa-fw fa-lg fas fa-edit"></i> >
</.link> <i class="fa-fw fa-lg fas fa-edit"></i>
</.link>
<.link <.link
patch={Routes.container_index_path(Endpoint, :clone, container)} patch={Routes.container_index_path(Endpoint, :clone, container)}
class="text-primary-600 link" class="text-primary-600 link"
aria-label={ aria-label={
dgettext("actions", "Clone %{container_name}", container_name: container.name) dgettext("actions", "Clone %{container_name}", container_name: container.name)
} }
> >
<i class="fa-fw fa-lg fas fa-copy"></i> <i class="fa-fw fa-lg fas fa-copy"></i>
</.link> </.link>
<.link <.link
href="#" href="#"
class="text-primary-600 link" class="text-primary-600 link"
phx-click="delete" phx-click="delete"
phx-value-id={container.id} phx-value-id={container.id}
data-confirm={ 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) }
} aria-label={
dgettext("actions", "Delete %{container_name}", container_name: container.name)
}
>
<i class="fa-fw fa-lg fas fa-trash"></i>
</.link>
</:actions>
</.live_component>
<% else %>
<div class="w-full flex flex-row flex-wrap justify-center items-stretch">
<.container_card
:for={container <- @containers}
container={container}
current_user={@current_user}
> >
<i class="fa-fw fa-lg fas fa-trash"></i> <:tag_actions>
</.link> <div class="mx-4 my-2">
</.container_card> <.link
</div> patch={Routes.container_index_path(Endpoint, :edit_tags, container)}
class="text-primary-600 link"
aria-label={
dgettext("actions", "Tag %{container_name}", container_name: container.name)
}
>
<i class="fa-fw fa-lg fas fa-tags"></i>
</.link>
</div>
</:tag_actions>
<.link
patch={Routes.container_index_path(Endpoint, :edit, container)}
class="text-primary-600 link"
aria-label={
dgettext("actions", "Edit %{container_name}", container_name: container.name)
}
>
<i class="fa-fw fa-lg fas fa-edit"></i>
</.link>
<.link
patch={Routes.container_index_path(Endpoint, :clone, container)}
class="text-primary-600 link"
aria-label={
dgettext("actions", "Clone %{container_name}", container_name: container.name)
}
>
<i class="fa-fw fa-lg fas fa-copy"></i>
</.link>
<.link
href="#"
class="text-primary-600 link"
phx-click="delete"
phx-value-id={container.id}
data-confirm={
dgettext("prompts", "Are you sure you want to delete %{name}?",
name: container.name
)
}
aria-label={
dgettext("actions", "Delete %{container_name}", container_name: container.name)
}
>
<i class="fa-fw fa-lg fas fa-trash"></i>
</.link>
</.container_card>
</div>
<% end %>
<% end %> <% end %>
<% end %> <% end %>
</div> </div>

View File

@ -11,13 +11,13 @@ defmodule CanneryWeb.ContainerLive.Show do
@impl true @impl true
def mount(_params, _session, socket), 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 @impl true
def handle_params(%{"id" => id}, _session, %{assigns: %{current_user: current_user}} = socket) do def handle_params(%{"id" => id}, _session, %{assigns: %{current_user: current_user}} = socket) do
socket = socket =
socket socket
|> assign(view_table: true) |> assign(:view_table, true)
|> render_container(id, current_user) |> render_container(id, current_user)
{:noreply, socket} {:noreply, socket}
@ -82,22 +82,34 @@ defmodule CanneryWeb.ContainerLive.Show do
{:noreply, socket} {:noreply, socket}
end 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 def handle_event("toggle_table", _params, %{assigns: %{view_table: view_table}} = socket) do
{:noreply, socket |> assign(:view_table, !view_table) |> render_container()} {:noreply, socket |> assign(:view_table, !view_table) |> render_container()}
end 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() @spec render_container(Socket.t(), Container.id(), User.t()) :: Socket.t()
defp render_container( defp render_container(
%{assigns: %{live_action: live_action, show_used: show_used}} = socket, %{assigns: %{class: class, live_action: live_action}} = socket,
id, id,
current_user current_user
) do ) do
%{name: container_name} = container = Containers.get_container!(id, current_user) %{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) original_counts = ammo_groups |> Ammo.get_original_counts(current_user)
cprs = ammo_groups |> Ammo.get_cprs(current_user) cprs = ammo_groups |> Ammo.get_cprs(current_user)
last_used_dates = ammo_groups |> ActivityLog.get_last_used_dates(current_user) last_used_dates = ammo_groups |> ActivityLog.get_last_used_dates(current_user)
@ -113,6 +125,7 @@ defmodule CanneryWeb.ContainerLive.Show do
|> assign( |> assign(
container: container, container: container,
round_count: Ammo.get_round_count_for_container!(container, current_user), 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, ammo_groups: ammo_groups,
original_counts: original_counts, original_counts: original_counts,
cprs: cprs, cprs: cprs,

View File

@ -18,22 +18,15 @@
<%= @container.location %> <%= @container.location %>
</span> </span>
<%= unless @ammo_groups |> Enum.empty?() do %> <span class="rounded-lg title text-lg">
<span class="rounded-lg title text-lg"> <%= gettext("Packs:") %>
<%= gettext("Packs:") %> <%= @ammo_groups_count %>
<%= @ammo_groups |> Enum.reject(fn %{count: count} -> count in [0, nil] end) |> Enum.count() %> </span>
</span>
<span :if={@show_used} class="rounded-lg title text-lg"> <span class="rounded-lg title text-lg">
<%= gettext("Total packs:") %> <%= gettext("Rounds:") %>
<%= Enum.count(@ammo_groups) %> <%= @round_count %>
</span> </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"> <div class="flex space-x-4 justify-center items-center text-primary-600">
<.link <.link
@ -93,11 +86,29 @@
<hr class="mb-4 hr" /> <hr class="mb-4 hr" />
<div class="flex justify-center items-center space-x-4"> <div class="flex justify-center items-center space-x-4">
<.toggle_button action="toggle_show_used" value={@show_used}> <.form
<span class="title text-lg text-primary-600"> :let={f}
<%= gettext("Show used") %> for={%{}}
</span> as={:ammo_type}
</.toggle_button> 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}> <.toggle_button action="toggle_table" value={@view_table}>
<span class="title text-lg text-primary-600"> <span class="title text-lg text-primary-600">
@ -118,7 +129,7 @@
id="ammo-type-show-table" id="ammo-type-show-table"
ammo_groups={@ammo_groups} ammo_groups={@ammo_groups}
current_user={@current_user} current_user={@current_user}
show_used={@show_used} show_used={false}
> >
<:ammo_type :let={%{name: ammo_type_name} = ammo_type}> <:ammo_type :let={%{name: ammo_type_name} = ammo_type}>
<.link navigate={Routes.ammo_type_show_path(Endpoint, :show, ammo_type)} class="link"> <.link navigate={Routes.ammo_type_show_path(Endpoint, :show, ammo_type)} class="link">

View File

@ -18,7 +18,10 @@
<%= changeset_errors(@changeset) %> <%= changeset_errors(@changeset) %>
</div> </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") %> <%= text_input(f, :name, class: "input input-primary col-span-2") %>
<%= error_tag(f, :name, "col-span-3") %> <%= error_tag(f, :name, "col-span-3") %>

View File

@ -31,6 +31,7 @@
<%= textarea(f, :notes, <%= textarea(f, :notes,
id: "shot-group-form-notes", id: "shot-group-form-notes",
class: "input input-primary col-span-2", class: "input input-primary col-span-2",
maxlength: 255,
placeholder: gettext("Really great weather"), placeholder: gettext("Really great weather"),
phx_hook: "MaintainAttrs", phx_hook: "MaintainAttrs",
phx_update: "ignore" phx_update: "ignore"

View File

@ -10,11 +10,11 @@ defmodule CanneryWeb.RangeLive.Index do
@impl true @impl true
def mount(%{"search" => search}, _session, socket) do 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 end
def mount(_params, _session, socket) do 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 end
@impl true @impl true
@ -102,9 +102,27 @@ defmodule CanneryWeb.RangeLive.Index do
{:noreply, socket |> push_patch(to: Routes.range_index_path(Endpoint, :search, search_term))} {:noreply, socket |> push_patch(to: Routes.range_index_path(Endpoint, :search, search_term))}
end 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() @spec display_shot_groups(Socket.t()) :: Socket.t()
defp display_shot_groups(%{assigns: %{search: search, current_user: current_user}} = socket) do defp display_shot_groups(
shot_groups = ActivityLog.list_shot_groups(search, current_user) %{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) ammo_groups = Ammo.list_staged_ammo_groups(current_user)
chart_data = shot_groups |> get_chart_data_for_shot_group() chart_data = shot_groups |> get_chart_data_for_shot_group()
original_counts = ammo_groups |> Ammo.get_original_counts(current_user) original_counts = ammo_groups |> Ammo.get_original_counts(current_user)

View File

@ -74,17 +74,41 @@
<%= dgettext("errors", "Your browser does not support the canvas element.") %> <%= dgettext("errors", "Your browser does not support the canvas element.") %>
</canvas> </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 <.form
:let={f} :let={f}
for={%{}} for={%{}}
as={:search} as={:search}
phx-change="search" phx-change="search"
phx-submit="search" phx-submit="search"
class="grow self-stretch flex flex-col items-stretch" class="grow flex items-center"
> >
<%= text_input(f, :search_term, <%= text_input(f, :search_term,
class: "input input-primary", class: "grow input input-primary",
value: @search, value: @search,
role: "search", role: "search",
phx_debounce: 300, phx_debounce: 300,

View File

@ -19,7 +19,7 @@
</div> </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") %>
<%= 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") %> <%= error_tag(f, :name, "col-span-3") %>
<%= label(f, :bg_color, gettext("Background color"), class: "title text-lg text-primary-600") %> <%= label(f, :bg_color, gettext("Background color"), class: "title text-lg text-primary-600") %>

View File

@ -18,57 +18,57 @@
<.link patch={Routes.tag_index_path(Endpoint, :new)} class="btn btn-primary"> <.link patch={Routes.tag_index_path(Endpoint, :new)} class="btn btn-primary">
<%= dgettext("actions", "New Tag") %> <%= dgettext("actions", "New Tag") %>
</.link> </.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 <.form
:let={f} :let={f}
for={%{}} for={%{}}
as={:search} as={:search}
phx-change="search" phx-change="search"
phx-submit="search" phx-submit="search"
class="grow self-stretch flex flex-col items-stretch" class="grow flex items-center"
> >
<%= text_input(f, :search_term, <%= text_input(f, :search_term,
class: "input input-primary", class: "grow input input-primary",
value: @search, value: @search,
role: "search", role: "search",
phx_debounce: 300, phx_debounce: 300,
placeholder: gettext("Search tags") placeholder: gettext("Search tags")
) %> ) %>
</.form> </.form>
</div>
<%= if @tags |> Enum.empty?() do %>
<h2 class="title text-xl text-primary-600">
<%= gettext("No tags") %>
<%= display_emoji("😔") %>
</h2>
<% else %>
<div class="flex flex-row flex-wrap justify-center items-stretch">
<.tag_card :for={tag <- @tags} tag={tag}>
<.link
patch={Routes.tag_index_path(Endpoint, :edit, tag)}
class="text-primary-600 link"
aria-label={dgettext("actions", "Edit %{tag_name}", tag_name: tag.name)}
>
<i class="fa-fw fa-lg fas fa-edit"></i>
</.link>
<.link
href="#"
class="text-primary-600 link"
phx-click="delete"
phx-value-id={tag.id}
data-confirm={
dgettext("prompts", "Are you sure you want to delete %{name}?", name: tag.name)
}
aria-label={dgettext("actions", "Delete %{tag_name}", tag_name: tag.name)}
>
<i class="fa-fw fa-lg fas fa-trash"></i>
</.link>
</.tag_card>
</div> </div>
<%= if @tags |> Enum.empty?() do %>
<h2 class="title text-xl text-primary-600">
<%= gettext("No tags") %>
<%= display_emoji("😔") %>
</h2>
<% else %>
<div class="flex flex-row flex-wrap justify-center items-stretch">
<.tag_card :for={tag <- @tags} tag={tag}>
<.link
patch={Routes.tag_index_path(Endpoint, :edit, tag)}
class="text-primary-600 link"
aria-label={dgettext("actions", "Edit %{tag_name}", tag_name: tag.name)}
>
<i class="fa-fw fa-lg fas fa-edit"></i>
</.link>
<.link
href="#"
class="text-primary-600 link"
phx-click="delete"
phx-value-id={tag.id}
data-confirm={
dgettext("prompts", "Are you sure you want to delete %{name}?", name: tag.name)
}
aria-label={dgettext("actions", "Delete %{tag_name}", tag_name: tag.name)}
>
<i class="fa-fw fa-lg fas fa-trash"></i>
</.link>
</.tag_card>
</div>
<% end %>
<% end %> <% end %>
</div> </div>

View File

@ -4,7 +4,7 @@ defmodule Cannery.MixProject do
def project do def project do
[ [
app: :cannery, app: :cannery,
version: "0.8.6", version: "0.9.0",
elixir: "1.14.1", elixir: "1.14.1",
elixirc_paths: elixirc_paths(Mix.env()), elixirc_paths: elixirc_paths(Mix.env()),
compilers: Mix.compilers(), compilers: Mix.compilers(),

View File

@ -10,14 +10,14 @@
msgid "" msgid ""
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.ex:54 #: lib/cannery_web/live/ammo_group_live/index.ex:59
#: lib/cannery_web/live/ammo_group_live/index.ex:62 #: lib/cannery_web/live/ammo_group_live/index.ex:67
#: lib/cannery_web/live/ammo_group_live/index.html.heex:41 #: lib/cannery_web/live/ammo_group_live/index.html.heex:38
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Add Ammo" msgid "Add Ammo"
msgstr "" 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 #, elixir-autogen, elixir-format
msgid "Add your first box!" msgid "Add your first box!"
msgstr "" msgstr ""
@ -120,12 +120,12 @@ msgstr ""
msgid "Reset password" msgid "Reset password"
msgstr "" 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_group_live/form_component.html.heex:84
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:159 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:350
#: lib/cannery_web/live/container_live/form_component.html.heex:55 #: lib/cannery_web/live/container_live/form_component.html.heex:57
#: lib/cannery_web/live/invite_live/form_component.html.heex:32 #: lib/cannery_web/live/invite_live/form_component.html.heex:35
#: lib/cannery_web/live/range_live/form_component.html.heex:44 #: lib/cannery_web/live/range_live/form_component.html.heex:45
#: lib/cannery_web/live/tag_live/form_component.html.heex:37 #: lib/cannery_web/live/tag_live/form_component.html.heex:37
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Save" msgid "Save"
@ -136,7 +136,7 @@ msgstr ""
msgid "Send instructions to reset password" msgid "Send instructions to reset password"
msgstr "" 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 #, elixir-autogen, elixir-format
msgid "Why not add one?" msgid "Why not add one?"
msgstr "" msgstr ""
@ -156,7 +156,7 @@ msgstr ""
msgid "Why not get some ready to shoot?" msgid "Why not get some ready to shoot?"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:105 #: 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/ammo_group_live/show.html.heex:103
#: lib/cannery_web/live/range_live/index.html.heex:45 #: lib/cannery_web/live/range_live/index.html.heex:45
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -178,7 +178,7 @@ msgstr ""
msgid "Copy to clipboard" msgid "Copy to clipboard"
msgstr "" 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 #, elixir-autogen, elixir-format
msgid "add a container first" msgid "add a container first"
msgstr "" msgstr ""
@ -203,13 +203,13 @@ msgstr ""
msgid "View in Catalog" msgid "View in Catalog"
msgstr "" 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 #, elixir-autogen, elixir-format
msgid "add an ammo type first" msgid "add an ammo type first"
msgstr "" msgstr ""
#: lib/cannery_web/components/move_ammo_group_component.ex:80 #: lib/cannery_web/components/move_ammo_group_component.ex:80
#: lib/cannery_web/live/ammo_group_live/index.html.heex:122 #: lib/cannery_web/live/ammo_group_live/index.html.heex:144
#: lib/cannery_web/live/ammo_group_live/show.html.heex:96 #: lib/cannery_web/live/ammo_group_live/show.html.heex:96
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Move ammo" msgid "Move ammo"
@ -237,13 +237,13 @@ msgstr ""
msgid "Export Data as JSON" msgid "Export Data as JSON"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:85 #: lib/cannery_web/live/ammo_type_live/index.html.heex:110
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Clone %{ammo_type_name}" msgid "Clone %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:88 #: lib/cannery_web/live/container_live/index.html.heex:87
#: lib/cannery_web/live/container_live/index.html.heex:144 #: lib/cannery_web/live/container_live/index.html.heex:145
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Clone %{container_name}" msgid "Clone %{container_name}"
msgstr "" msgstr ""
@ -253,20 +253,20 @@ msgstr ""
msgid "Copy invite link for %{invite_name}" msgid "Copy invite link for %{invite_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:104 #: lib/cannery_web/live/ammo_type_live/index.html.heex:129
#: lib/cannery_web/live/ammo_type_live/show.html.heex:36 #: lib/cannery_web/live/ammo_type_live/show.html.heex:36
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete %{ammo_type_name}" msgid "Delete %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:103 #: lib/cannery_web/live/container_live/index.html.heex:104
#: lib/cannery_web/live/container_live/index.html.heex:159 #: lib/cannery_web/live/container_live/index.html.heex:162
#: lib/cannery_web/live/container_live/show.html.heex:55 #: lib/cannery_web/live/container_live/show.html.heex:48
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete %{container_name}" msgid "Delete %{container_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/tag_live/index.html.heex:66 #: lib/cannery_web/live/tag_live/index.html.heex:65
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete %{tag_name}" msgid "Delete %{tag_name}"
msgstr "" msgstr ""
@ -277,30 +277,30 @@ msgid "Delete invite for %{invite_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/show.ex:161 #: lib/cannery_web/live/ammo_group_live/show.ex:161
#: lib/cannery_web/live/range_live/index.html.heex:131 #: lib/cannery_web/live/range_live/index.html.heex:155
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete shot record of %{shot_group_count} shots" msgid "Delete shot record of %{shot_group_count} shots"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:75 #: lib/cannery_web/live/ammo_type_live/index.html.heex:100
#: lib/cannery_web/live/ammo_type_live/show.html.heex:19 #: lib/cannery_web/live/ammo_type_live/show.html.heex:19
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit %{ammo_type_name}" msgid "Edit %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:78 #: lib/cannery_web/live/container_live/index.html.heex:77
#: lib/cannery_web/live/container_live/index.html.heex:134 #: lib/cannery_web/live/container_live/index.html.heex:135
#: lib/cannery_web/live/container_live/show.html.heex:42 #: lib/cannery_web/live/container_live/show.html.heex:35
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit %{container_name}" msgid "Edit %{container_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/tag_live/index.html.heex:53 #: lib/cannery_web/live/tag_live/index.html.heex:52
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit %{tag_name}" msgid "Edit %{tag_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:144 #: lib/cannery_web/live/ammo_group_live/index.html.heex:166
#: lib/cannery_web/live/ammo_group_live/show.html.heex:62 #: lib/cannery_web/live/ammo_group_live/show.html.heex:62
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit ammo group of %{ammo_group_count} bullets" msgid "Edit ammo group of %{ammo_group_count} bullets"
@ -316,45 +316,45 @@ msgstr ""
msgid "Edit shot group of %{shot_group_count} shots" msgid "Edit shot group of %{shot_group_count} shots"
msgstr "" msgstr ""
#: lib/cannery_web/live/range_live/index.html.heex:114 #: lib/cannery_web/live/range_live/index.html.heex:138
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit shot record of %{shot_group_count} shots" msgid "Edit shot record of %{shot_group_count} shots"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:98 #: lib/cannery_web/live/ammo_group_live/index.html.heex:120
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Stage" msgid "Stage"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:66 #: lib/cannery_web/live/container_live/index.html.heex:65
#: lib/cannery_web/live/container_live/index.html.heex:123 #: lib/cannery_web/live/container_live/index.html.heex:124
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Tag %{container_name}" msgid "Tag %{container_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:97 #: lib/cannery_web/live/ammo_group_live/index.html.heex:119
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Unstage" msgid "Unstage"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:65 #: lib/cannery_web/live/ammo_type_live/index.html.heex:90
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "View %{ammo_type_name}" msgid "View %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:156 #: lib/cannery_web/live/ammo_group_live/index.html.heex:178
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Clone ammo group of %{ammo_group_count} bullets" msgid "Clone ammo group of %{ammo_group_count} bullets"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:171 #: lib/cannery_web/live/ammo_group_live/index.html.heex:193
#: lib/cannery_web/live/ammo_group_live/show.html.heex:76 #: lib/cannery_web/live/ammo_group_live/show.html.heex:76
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete ammo group of %{ammo_group_count} bullets" msgid "Delete ammo group of %{ammo_group_count} bullets"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:132 #: lib/cannery_web/live/ammo_group_live/index.html.heex:154
#: lib/cannery_web/live/ammo_type_live/show.html.heex:189 #: lib/cannery_web/live/ammo_type_live/show.html.heex:206
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "View ammo group of %{ammo_group_count} bullets" msgid "View ammo group of %{ammo_group_count} bullets"
msgstr "" msgstr ""

View File

@ -23,14 +23,14 @@ msgstr ""
## Run "mix gettext.extract" to bring this file up to ## Run "mix gettext.extract" to bring this file up to
## date. Leave "msgstr"s empty as changing them here has no ## date. Leave "msgstr"s empty as changing them here has no
## effect: edit them in PO (.po) files instead. ## 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:59
#: lib/cannery_web/live/ammo_group_live/index.ex:62 #: lib/cannery_web/live/ammo_group_live/index.ex:67
#: lib/cannery_web/live/ammo_group_live/index.html.heex:41 #: lib/cannery_web/live/ammo_group_live/index.html.heex:38
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Add Ammo" msgid "Add Ammo"
msgstr "Munition hinzufügen" 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 #, elixir-autogen, elixir-format
msgid "Add your first box!" msgid "Add your first box!"
msgstr "Fügen Sie ihre erste Box hinzu!" msgstr "Fügen Sie ihre erste Box hinzu!"
@ -133,12 +133,12 @@ msgstr "Bestätigungsmail erneut senden"
msgid "Reset password" msgid "Reset password"
msgstr "Passwort zurücksetzen" 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_group_live/form_component.html.heex:84
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:159 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:350
#: lib/cannery_web/live/container_live/form_component.html.heex:55 #: lib/cannery_web/live/container_live/form_component.html.heex:57
#: lib/cannery_web/live/invite_live/form_component.html.heex:32 #: lib/cannery_web/live/invite_live/form_component.html.heex:35
#: lib/cannery_web/live/range_live/form_component.html.heex:44 #: lib/cannery_web/live/range_live/form_component.html.heex:45
#: lib/cannery_web/live/tag_live/form_component.html.heex:37 #: lib/cannery_web/live/tag_live/form_component.html.heex:37
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Save" msgid "Save"
@ -149,7 +149,7 @@ msgstr "Speichern"
msgid "Send instructions to reset password" msgid "Send instructions to reset password"
msgstr "Anleitung zum Passwort zurücksetzen zusenden" 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 #, elixir-autogen, elixir-format
msgid "Why not add one?" msgid "Why not add one?"
msgstr "Warum fügen Sie keine hinzu?" msgstr "Warum fügen Sie keine hinzu?"
@ -169,7 +169,7 @@ msgstr "Munition markieren"
msgid "Why not get some ready to shoot?" msgid "Why not get some ready to shoot?"
msgstr "Warum nicht einige für den Schießstand auswählen?" msgstr "Warum nicht einige für den Schießstand auswählen?"
#: lib/cannery_web/live/ammo_group_live/index.html.heex:105 #: 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/ammo_group_live/show.html.heex:103
#: lib/cannery_web/live/range_live/index.html.heex:45 #: lib/cannery_web/live/range_live/index.html.heex:45
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -191,7 +191,7 @@ msgstr "Markieren"
msgid "Copy to clipboard" msgid "Copy to clipboard"
msgstr "In die Zwischenablage kopieren" 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 #, elixir-autogen, elixir-format
msgid "add a container first" msgid "add a container first"
msgstr "Zuerst einen Behälter hinzufügen" msgstr "Zuerst einen Behälter hinzufügen"
@ -216,13 +216,13 @@ msgstr "Sprache wechseln"
msgid "View in Catalog" msgid "View in Catalog"
msgstr "" 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 #, elixir-autogen, elixir-format
msgid "add an ammo type first" msgid "add an ammo type first"
msgstr "" msgstr ""
#: lib/cannery_web/components/move_ammo_group_component.ex:80 #: lib/cannery_web/components/move_ammo_group_component.ex:80
#: lib/cannery_web/live/ammo_group_live/index.html.heex:122 #: lib/cannery_web/live/ammo_group_live/index.html.heex:144
#: lib/cannery_web/live/ammo_group_live/show.html.heex:96 #: lib/cannery_web/live/ammo_group_live/show.html.heex:96
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Move ammo" msgid "Move ammo"
@ -250,13 +250,13 @@ msgstr ""
msgid "Export Data as JSON" msgid "Export Data as JSON"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:85 #: lib/cannery_web/live/ammo_type_live/index.html.heex:110
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Clone %{ammo_type_name}" msgid "Clone %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:88 #: lib/cannery_web/live/container_live/index.html.heex:87
#: lib/cannery_web/live/container_live/index.html.heex:144 #: lib/cannery_web/live/container_live/index.html.heex:145
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Clone %{container_name}" msgid "Clone %{container_name}"
msgstr "" msgstr ""
@ -266,20 +266,20 @@ msgstr ""
msgid "Copy invite link for %{invite_name}" msgid "Copy invite link for %{invite_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:104 #: lib/cannery_web/live/ammo_type_live/index.html.heex:129
#: lib/cannery_web/live/ammo_type_live/show.html.heex:36 #: lib/cannery_web/live/ammo_type_live/show.html.heex:36
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete %{ammo_type_name}" msgid "Delete %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:103 #: lib/cannery_web/live/container_live/index.html.heex:104
#: lib/cannery_web/live/container_live/index.html.heex:159 #: lib/cannery_web/live/container_live/index.html.heex:162
#: lib/cannery_web/live/container_live/show.html.heex:55 #: lib/cannery_web/live/container_live/show.html.heex:48
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete %{container_name}" msgid "Delete %{container_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/tag_live/index.html.heex:66 #: lib/cannery_web/live/tag_live/index.html.heex:65
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete %{tag_name}" msgid "Delete %{tag_name}"
msgstr "" msgstr ""
@ -290,30 +290,30 @@ msgid "Delete invite for %{invite_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/show.ex:161 #: lib/cannery_web/live/ammo_group_live/show.ex:161
#: lib/cannery_web/live/range_live/index.html.heex:131 #: lib/cannery_web/live/range_live/index.html.heex:155
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete shot record of %{shot_group_count} shots" msgid "Delete shot record of %{shot_group_count} shots"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:75 #: lib/cannery_web/live/ammo_type_live/index.html.heex:100
#: lib/cannery_web/live/ammo_type_live/show.html.heex:19 #: lib/cannery_web/live/ammo_type_live/show.html.heex:19
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit %{ammo_type_name}" msgid "Edit %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:78 #: lib/cannery_web/live/container_live/index.html.heex:77
#: lib/cannery_web/live/container_live/index.html.heex:134 #: lib/cannery_web/live/container_live/index.html.heex:135
#: lib/cannery_web/live/container_live/show.html.heex:42 #: lib/cannery_web/live/container_live/show.html.heex:35
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit %{container_name}" msgid "Edit %{container_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/tag_live/index.html.heex:53 #: lib/cannery_web/live/tag_live/index.html.heex:52
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit %{tag_name}" msgid "Edit %{tag_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:144 #: lib/cannery_web/live/ammo_group_live/index.html.heex:166
#: lib/cannery_web/live/ammo_group_live/show.html.heex:62 #: lib/cannery_web/live/ammo_group_live/show.html.heex:62
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit ammo group of %{ammo_group_count} bullets" msgid "Edit ammo group of %{ammo_group_count} bullets"
@ -329,45 +329,45 @@ msgstr ""
msgid "Edit shot group of %{shot_group_count} shots" msgid "Edit shot group of %{shot_group_count} shots"
msgstr "" msgstr ""
#: lib/cannery_web/live/range_live/index.html.heex:114 #: lib/cannery_web/live/range_live/index.html.heex:138
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit shot record of %{shot_group_count} shots" msgid "Edit shot record of %{shot_group_count} shots"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:98 #: lib/cannery_web/live/ammo_group_live/index.html.heex:120
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Stage" msgid "Stage"
msgstr "Munition markieren" msgstr "Munition markieren"
#: lib/cannery_web/live/container_live/index.html.heex:66 #: lib/cannery_web/live/container_live/index.html.heex:65
#: lib/cannery_web/live/container_live/index.html.heex:123 #: lib/cannery_web/live/container_live/index.html.heex:124
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Tag %{container_name}" msgid "Tag %{container_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:97 #: lib/cannery_web/live/ammo_group_live/index.html.heex:119
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Unstage" msgid "Unstage"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:65 #: lib/cannery_web/live/ammo_type_live/index.html.heex:90
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "View %{ammo_type_name}" msgid "View %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:156 #: lib/cannery_web/live/ammo_group_live/index.html.heex:178
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Clone ammo group of %{ammo_group_count} bullets" msgid "Clone ammo group of %{ammo_group_count} bullets"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:171 #: lib/cannery_web/live/ammo_group_live/index.html.heex:193
#: lib/cannery_web/live/ammo_group_live/show.html.heex:76 #: lib/cannery_web/live/ammo_group_live/show.html.heex:76
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Delete ammo group of %{ammo_group_count} bullets" msgid "Delete ammo group of %{ammo_group_count} bullets"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:132 #: lib/cannery_web/live/ammo_group_live/index.html.heex:154
#: lib/cannery_web/live/ammo_type_live/show.html.heex:189 #: lib/cannery_web/live/ammo_type_live/show.html.heex:206
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "View ammo group of %{ammo_group_count} bullets" msgid "View ammo group of %{ammo_group_count} bullets"
msgstr "" msgstr ""

File diff suppressed because it is too large Load Diff

View File

@ -69,6 +69,7 @@ msgstr "Ungültige Mailadresse oder Passwort"
msgid "Not found" msgid "Not found"
msgstr "Nicht gefunden" 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_registration/new.html.heex:13
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:13 #: lib/cannery_web/templates/user_reset_password/edit.html.heex:13
#: lib/cannery_web/templates/user_settings/edit.html.heex:22 #: lib/cannery_web/templates/user_settings/edit.html.heex:22
@ -117,22 +118,22 @@ msgstr "Nutzerkonto Bestätigungslink ist ungültig oder abgelaufen."
msgid "You are not authorized to view this page." msgid "You are not authorized to view this page."
msgstr "Sie sind nicht berechtigt, diese Seite aufzurufen." msgstr "Sie sind nicht berechtigt, diese Seite aufzurufen."
#: lib/cannery/accounts/user.ex:144 #: lib/cannery/accounts/user.ex:145
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "did not change" msgid "did not change"
msgstr "hat sich nicht geändert" msgstr "hat sich nicht geändert"
#: lib/cannery/accounts/user.ex:165 #: lib/cannery/accounts/user.ex:166
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "does not match password" msgid "does not match password"
msgstr "Passwort stimmt nicht überein" msgstr "Passwort stimmt nicht überein"
#: lib/cannery/accounts/user.ex:202 #: lib/cannery/accounts/user.ex:203
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "is not valid" msgid "is not valid"
msgstr "ist nicht gültig" msgstr "ist nicht gültig"
#: lib/cannery/accounts/user.ex:99 #: lib/cannery/accounts/user.ex:100
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "must have the @ sign and no spaces" msgid "must have the @ sign and no spaces"
msgstr "Muss ein @ Zeichen und keine Leerzeichen haben" msgstr "Muss ein @ Zeichen und keine Leerzeichen haben"
@ -172,7 +173,7 @@ msgstr ""
"Ungültige Nummer an Kopien. Muss zwischen 1 and %{max} liegen. War " "Ungültige Nummer an Kopien. Muss zwischen 1 and %{max} liegen. War "
"%{multiplier}" "%{multiplier}"
#: lib/cannery/ammo.ex:1043 #: lib/cannery/ammo.ex:1123
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Invalid multiplier" msgid "Invalid multiplier"
msgstr "" msgstr ""
@ -187,27 +188,27 @@ msgstr ""
msgid "Your browser does not support the canvas element." msgid "Your browser does not support the canvas element."
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:72 #: lib/cannery/activity_log/shot_group.ex:74
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Please select a valid user and ammo pack" msgid "Please select a valid user and ammo pack"
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:86 #: lib/cannery/activity_log/shot_group.ex:88
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Ammo left can be at most %{count} rounds" msgid "Ammo left can be at most %{count} rounds"
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:82 #: lib/cannery/activity_log/shot_group.ex:84
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Ammo left must be at least 0" msgid "Ammo left must be at least 0"
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:119 #: lib/cannery/activity_log/shot_group.ex:122
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Count can be at most %{count} shots" msgid "Count can be at most %{count} shots"
msgstr "Anzahl muss weniger als %{count} betragen" 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 #, elixir-autogen, elixir-format
msgid "can't be blank" msgid "can't be blank"
msgstr "" msgstr ""

View File

@ -32,7 +32,7 @@ msgid "%{name} created successfully"
msgstr "%{name} erfolgreich erstellt" msgstr "%{name} erfolgreich erstellt"
#: lib/cannery_web/live/ammo_type_live/index.ex:72 #: lib/cannery_web/live/ammo_type_live/index.ex:72
#: lib/cannery_web/live/ammo_type_live/show.ex:49 #: lib/cannery_web/live/ammo_type_live/show.ex:27
#: lib/cannery_web/live/tag_live/index.ex:65 #: lib/cannery_web/live/tag_live/index.ex:65
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{name} deleted succesfully" msgid "%{name} deleted succesfully"
@ -65,15 +65,15 @@ msgstr ""
"Sind Sie sicher, dass sie %{email} löschen möchten? Dies kann nicht " "Sind Sie sicher, dass sie %{email} löschen möchten? Dies kann nicht "
"zurückgenommen werden!" "zurückgenommen werden!"
#: lib/cannery_web/live/container_live/index.html.heex:100 #: lib/cannery_web/live/container_live/index.html.heex:99
#: lib/cannery_web/live/container_live/index.html.heex:156 #: lib/cannery_web/live/container_live/index.html.heex:157
#: lib/cannery_web/live/container_live/show.html.heex:52 #: lib/cannery_web/live/container_live/show.html.heex:45
#: lib/cannery_web/live/tag_live/index.html.heex:64 #: lib/cannery_web/live/tag_live/index.html.heex:63
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure you want to delete %{name}?" msgid "Are you sure you want to delete %{name}?"
msgstr "Sind Sie sicher, dass sie %{name} löschen möchten?" msgstr "Sind Sie sicher, dass sie %{name} löschen möchten?"
#: lib/cannery_web/live/ammo_group_live/index.html.heex:169 #: lib/cannery_web/live/ammo_group_live/index.html.heex:191
#: lib/cannery_web/live/ammo_group_live/show.html.heex:74 #: lib/cannery_web/live/ammo_group_live/show.html.heex:74
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure you want to delete this ammo?" 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" msgid "Please check your email to verify your account"
msgstr "Bitte überprüfen Sie ihre Mailbox und bestätigen Sie das Nutzerkonto" 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_group_live/form_component.html.heex:85
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:160 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:351
#: lib/cannery_web/live/container_live/form_component.html.heex:57 #: lib/cannery_web/live/container_live/form_component.html.heex:59
#: lib/cannery_web/live/invite_live/form_component.html.heex:34 #: lib/cannery_web/live/invite_live/form_component.html.heex:37
#: lib/cannery_web/live/range_live/form_component.html.heex:46 #: lib/cannery_web/live/range_live/form_component.html.heex:47
#: lib/cannery_web/live/tag_live/form_component.html.heex:39 #: lib/cannery_web/live/tag_live/form_component.html.heex:39
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Saving..." 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?" 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/ammo_group_live/show.ex:159
#: lib/cannery_web/live/range_live/index.html.heex:128 #: lib/cannery_web/live/range_live/index.html.heex:152
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure you want to delete this shot record?" msgid "Are you sure you want to delete this shot record?"
msgstr "Sind sie sicher, dass sie die Schießkladde löschen möchten?" msgstr "Sind sie sicher, dass sie die Schießkladde löschen möchten?"
@ -213,8 +213,8 @@ msgstr "Der Zwischenablage hinzugefügt"
msgid "%{name} removed successfully" msgid "%{name} removed successfully"
msgstr "%{name} erfolgreich entfernt" 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:10
#: lib/cannery_web/live/ammo_group_live/index.html.heex:28 #: lib/cannery_web/live/ammo_group_live/index.html.heex:20
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "You'll need to" msgid "You'll need to"
msgstr "Sie müssen" msgstr "Sie müssen"
@ -234,7 +234,7 @@ msgstr "Möchten Sie die Sprache wechseln?"
msgid "Language updated successfully." msgid "Language updated successfully."
msgstr "Spracheinstellung gespeichert." 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 #: lib/cannery_web/live/ammo_group_live/show.ex:55
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Ammo deleted succesfully" msgid "Ammo deleted succesfully"
@ -257,7 +257,7 @@ msgid_plural "Ammo added successfully"
msgstr[0] "Munitionsgruppe erfolgreich aktualisiert" msgstr[0] "Munitionsgruppe erfolgreich aktualisiert"
msgstr[1] "Munitionsgruppe erfolgreich aktualisiert" msgstr[1] "Munitionsgruppe erfolgreich aktualisiert"
#: lib/cannery_web/live/ammo_type_live/index.html.heex:97 #: lib/cannery_web/live/ammo_type_live/index.html.heex:122
#: lib/cannery_web/live/ammo_type_live/show.html.heex:29 #: lib/cannery_web/live/ammo_type_live/show.html.heex:29
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Are you sure you want to delete %{name}? This will delete all %{name} type ammo as well!" msgid "Are you sure you want to delete %{name}? This will delete all %{name} type ammo as well!"

File diff suppressed because it is too large Load Diff

View File

@ -10,14 +10,14 @@ msgid ""
msgstr "" msgstr ""
"Language: en\n" "Language: en\n"
#: lib/cannery_web/live/ammo_group_live/index.ex:54 #: lib/cannery_web/live/ammo_group_live/index.ex:59
#: lib/cannery_web/live/ammo_group_live/index.ex:62 #: lib/cannery_web/live/ammo_group_live/index.ex:67
#: lib/cannery_web/live/ammo_group_live/index.html.heex:41 #: lib/cannery_web/live/ammo_group_live/index.html.heex:38
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Add Ammo" msgid "Add Ammo"
msgstr "" 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 #, elixir-autogen, elixir-format
msgid "Add your first box!" msgid "Add your first box!"
msgstr "" msgstr ""
@ -120,12 +120,12 @@ msgstr ""
msgid "Reset password" msgid "Reset password"
msgstr "" 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_group_live/form_component.html.heex:84
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:159 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:350
#: lib/cannery_web/live/container_live/form_component.html.heex:55 #: lib/cannery_web/live/container_live/form_component.html.heex:57
#: lib/cannery_web/live/invite_live/form_component.html.heex:32 #: lib/cannery_web/live/invite_live/form_component.html.heex:35
#: lib/cannery_web/live/range_live/form_component.html.heex:44 #: lib/cannery_web/live/range_live/form_component.html.heex:45
#: lib/cannery_web/live/tag_live/form_component.html.heex:37 #: lib/cannery_web/live/tag_live/form_component.html.heex:37
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Save" msgid "Save"
@ -136,7 +136,7 @@ msgstr ""
msgid "Send instructions to reset password" msgid "Send instructions to reset password"
msgstr "" 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 #, elixir-autogen, elixir-format
msgid "Why not add one?" msgid "Why not add one?"
msgstr "" msgstr ""
@ -156,7 +156,7 @@ msgstr ""
msgid "Why not get some ready to shoot?" msgid "Why not get some ready to shoot?"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:105 #: 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/ammo_group_live/show.html.heex:103
#: lib/cannery_web/live/range_live/index.html.heex:45 #: lib/cannery_web/live/range_live/index.html.heex:45
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -178,7 +178,7 @@ msgstr ""
msgid "Copy to clipboard" msgid "Copy to clipboard"
msgstr "" 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 #, elixir-autogen, elixir-format
msgid "add a container first" msgid "add a container first"
msgstr "" msgstr ""
@ -203,13 +203,13 @@ msgstr ""
msgid "View in Catalog" msgid "View in Catalog"
msgstr "" 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 #, elixir-autogen, elixir-format
msgid "add an ammo type first" msgid "add an ammo type first"
msgstr "" msgstr ""
#: lib/cannery_web/components/move_ammo_group_component.ex:80 #: lib/cannery_web/components/move_ammo_group_component.ex:80
#: lib/cannery_web/live/ammo_group_live/index.html.heex:122 #: lib/cannery_web/live/ammo_group_live/index.html.heex:144
#: lib/cannery_web/live/ammo_group_live/show.html.heex:96 #: lib/cannery_web/live/ammo_group_live/show.html.heex:96
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Move ammo" msgid "Move ammo"
@ -237,13 +237,13 @@ msgstr ""
msgid "Export Data as JSON" msgid "Export Data as JSON"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:85 #: lib/cannery_web/live/ammo_type_live/index.html.heex:110
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Clone %{ammo_type_name}" msgid "Clone %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:88 #: lib/cannery_web/live/container_live/index.html.heex:87
#: lib/cannery_web/live/container_live/index.html.heex:144 #: lib/cannery_web/live/container_live/index.html.heex:145
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Clone %{container_name}" msgid "Clone %{container_name}"
msgstr "" msgstr ""
@ -253,20 +253,20 @@ msgstr ""
msgid "Copy invite link for %{invite_name}" msgid "Copy invite link for %{invite_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:104 #: lib/cannery_web/live/ammo_type_live/index.html.heex:129
#: lib/cannery_web/live/ammo_type_live/show.html.heex:36 #: lib/cannery_web/live/ammo_type_live/show.html.heex:36
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete %{ammo_type_name}" msgid "Delete %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:103 #: lib/cannery_web/live/container_live/index.html.heex:104
#: lib/cannery_web/live/container_live/index.html.heex:159 #: lib/cannery_web/live/container_live/index.html.heex:162
#: lib/cannery_web/live/container_live/show.html.heex:55 #: lib/cannery_web/live/container_live/show.html.heex:48
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete %{container_name}" msgid "Delete %{container_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/tag_live/index.html.heex:66 #: lib/cannery_web/live/tag_live/index.html.heex:65
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete %{tag_name}" msgid "Delete %{tag_name}"
msgstr "" msgstr ""
@ -277,30 +277,30 @@ msgid "Delete invite for %{invite_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/show.ex:161 #: lib/cannery_web/live/ammo_group_live/show.ex:161
#: lib/cannery_web/live/range_live/index.html.heex:131 #: lib/cannery_web/live/range_live/index.html.heex:155
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete shot record of %{shot_group_count} shots" msgid "Delete shot record of %{shot_group_count} shots"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:75 #: lib/cannery_web/live/ammo_type_live/index.html.heex:100
#: lib/cannery_web/live/ammo_type_live/show.html.heex:19 #: lib/cannery_web/live/ammo_type_live/show.html.heex:19
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit %{ammo_type_name}" msgid "Edit %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:78 #: lib/cannery_web/live/container_live/index.html.heex:77
#: lib/cannery_web/live/container_live/index.html.heex:134 #: lib/cannery_web/live/container_live/index.html.heex:135
#: lib/cannery_web/live/container_live/show.html.heex:42 #: lib/cannery_web/live/container_live/show.html.heex:35
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit %{container_name}" msgid "Edit %{container_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/tag_live/index.html.heex:53 #: lib/cannery_web/live/tag_live/index.html.heex:52
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit %{tag_name}" msgid "Edit %{tag_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:144 #: lib/cannery_web/live/ammo_group_live/index.html.heex:166
#: lib/cannery_web/live/ammo_group_live/show.html.heex:62 #: lib/cannery_web/live/ammo_group_live/show.html.heex:62
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit ammo group of %{ammo_group_count} bullets" msgid "Edit ammo group of %{ammo_group_count} bullets"
@ -316,45 +316,45 @@ msgstr ""
msgid "Edit shot group of %{shot_group_count} shots" msgid "Edit shot group of %{shot_group_count} shots"
msgstr "" msgstr ""
#: lib/cannery_web/live/range_live/index.html.heex:114 #: lib/cannery_web/live/range_live/index.html.heex:138
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit shot record of %{shot_group_count} shots" msgid "Edit shot record of %{shot_group_count} shots"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:98 #: lib/cannery_web/live/ammo_group_live/index.html.heex:120
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Stage" msgid "Stage"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:66 #: lib/cannery_web/live/container_live/index.html.heex:65
#: lib/cannery_web/live/container_live/index.html.heex:123 #: lib/cannery_web/live/container_live/index.html.heex:124
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Tag %{container_name}" msgid "Tag %{container_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:97 #: lib/cannery_web/live/ammo_group_live/index.html.heex:119
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Unstage" msgid "Unstage"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:65 #: lib/cannery_web/live/ammo_type_live/index.html.heex:90
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "View %{ammo_type_name}" msgid "View %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:156 #: lib/cannery_web/live/ammo_group_live/index.html.heex:178
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Clone ammo group of %{ammo_group_count} bullets" msgid "Clone ammo group of %{ammo_group_count} bullets"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:171 #: lib/cannery_web/live/ammo_group_live/index.html.heex:193
#: lib/cannery_web/live/ammo_group_live/show.html.heex:76 #: lib/cannery_web/live/ammo_group_live/show.html.heex:76
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Delete ammo group of %{ammo_group_count} bullets" msgid "Delete ammo group of %{ammo_group_count} bullets"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:132 #: lib/cannery_web/live/ammo_group_live/index.html.heex:154
#: lib/cannery_web/live/ammo_type_live/show.html.heex:189 #: lib/cannery_web/live/ammo_type_live/show.html.heex:206
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "View ammo group of %{ammo_group_count} bullets" msgid "View ammo group of %{ammo_group_count} bullets"
msgstr "" msgstr ""

File diff suppressed because it is too large Load Diff

View File

@ -56,6 +56,7 @@ msgstr ""
msgid "Not found" msgid "Not found"
msgstr "" 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_registration/new.html.heex:13
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:13 #: lib/cannery_web/templates/user_reset_password/edit.html.heex:13
#: lib/cannery_web/templates/user_settings/edit.html.heex:22 #: lib/cannery_web/templates/user_settings/edit.html.heex:22
@ -103,23 +104,23 @@ msgstr ""
msgid "You are not authorized to view this page." msgid "You are not authorized to view this page."
msgstr "" msgstr ""
#: lib/cannery/accounts/user.ex:144 #: lib/cannery/accounts/user.ex:145
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "did not change" msgid "did not change"
msgstr "" msgstr ""
#: lib/cannery/accounts/user.ex:165 #: lib/cannery/accounts/user.ex:166
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "does not match password" msgid "does not match password"
msgstr "" msgstr ""
## From Ecto.Changeset.put_change/3 ## From Ecto.Changeset.put_change/3
#: lib/cannery/accounts/user.ex:202 #: lib/cannery/accounts/user.ex:203
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "is not valid" msgid "is not valid"
msgstr "" msgstr ""
#: lib/cannery/accounts/user.ex:99 #: lib/cannery/accounts/user.ex:100
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "must have the @ sign and no spaces" msgid "must have the @ sign and no spaces"
msgstr "" msgstr ""
@ -155,7 +156,7 @@ msgstr ""
msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}" msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}"
msgstr "" msgstr ""
#: lib/cannery/ammo.ex:1043 #: lib/cannery/ammo.ex:1123
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Invalid multiplier" msgid "Invalid multiplier"
msgstr "" msgstr ""
@ -170,27 +171,27 @@ msgstr ""
msgid "Your browser does not support the canvas element." msgid "Your browser does not support the canvas element."
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:72 #: lib/cannery/activity_log/shot_group.ex:74
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Please select a valid user and ammo pack" msgid "Please select a valid user and ammo pack"
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:86 #: lib/cannery/activity_log/shot_group.ex:88
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Ammo left can be at most %{count} rounds" msgid "Ammo left can be at most %{count} rounds"
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:82 #: lib/cannery/activity_log/shot_group.ex:84
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Ammo left must be at least 0" msgid "Ammo left must be at least 0"
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:119 #: lib/cannery/activity_log/shot_group.ex:122
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Count can be at most %{count} shots" msgid "Count can be at most %{count} shots"
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:78 #: lib/cannery/activity_log/shot_group.ex:80
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "can't be blank" msgid "can't be blank"
msgstr "" msgstr ""

View File

@ -19,7 +19,7 @@ msgid "%{name} created successfully"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.ex:72 #: lib/cannery_web/live/ammo_type_live/index.ex:72
#: lib/cannery_web/live/ammo_type_live/show.ex:49 #: lib/cannery_web/live/ammo_type_live/show.ex:27
#: lib/cannery_web/live/tag_live/index.ex:65 #: lib/cannery_web/live/tag_live/index.ex:65
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{name} deleted succesfully" msgid "%{name} deleted succesfully"
@ -50,15 +50,15 @@ msgstr ""
msgid "Are you sure you want to delete %{email}? This action is permanent!" msgid "Are you sure you want to delete %{email}? This action is permanent!"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:100 #: lib/cannery_web/live/container_live/index.html.heex:99
#: lib/cannery_web/live/container_live/index.html.heex:156 #: lib/cannery_web/live/container_live/index.html.heex:157
#: lib/cannery_web/live/container_live/show.html.heex:52 #: lib/cannery_web/live/container_live/show.html.heex:45
#: lib/cannery_web/live/tag_live/index.html.heex:64 #: lib/cannery_web/live/tag_live/index.html.heex:63
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure you want to delete %{name}?" msgid "Are you sure you want to delete %{name}?"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:169 #: lib/cannery_web/live/ammo_group_live/index.html.heex:191
#: lib/cannery_web/live/ammo_group_live/show.html.heex:74 #: lib/cannery_web/live/ammo_group_live/show.html.heex:74
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure you want to delete this ammo?" msgid "Are you sure you want to delete this ammo?"
@ -109,12 +109,12 @@ msgstr ""
msgid "Please check your email to verify your account" msgid "Please check your email to verify your account"
msgstr "" 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_group_live/form_component.html.heex:85
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:160 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:351
#: lib/cannery_web/live/container_live/form_component.html.heex:57 #: lib/cannery_web/live/container_live/form_component.html.heex:59
#: lib/cannery_web/live/invite_live/form_component.html.heex:34 #: lib/cannery_web/live/invite_live/form_component.html.heex:37
#: lib/cannery_web/live/range_live/form_component.html.heex:46 #: lib/cannery_web/live/range_live/form_component.html.heex:47
#: lib/cannery_web/live/tag_live/form_component.html.heex:39 #: lib/cannery_web/live/tag_live/form_component.html.heex:39
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Saving..." msgid "Saving..."
@ -156,7 +156,7 @@ msgid "Are you sure you want to unstage this ammo?"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/show.ex:159 #: lib/cannery_web/live/ammo_group_live/show.ex:159
#: lib/cannery_web/live/range_live/index.html.heex:128 #: lib/cannery_web/live/range_live/index.html.heex:152
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure you want to delete this shot record?" msgid "Are you sure you want to delete this shot record?"
msgstr "" msgstr ""
@ -192,8 +192,8 @@ msgstr ""
msgid "%{name} removed successfully" msgid "%{name} removed successfully"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:18 #: lib/cannery_web/live/ammo_group_live/index.html.heex:10
#: lib/cannery_web/live/ammo_group_live/index.html.heex:28 #: lib/cannery_web/live/ammo_group_live/index.html.heex:20
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "You'll need to" msgid "You'll need to"
msgstr "" msgstr ""
@ -213,7 +213,7 @@ msgstr ""
msgid "Language updated successfully." msgid "Language updated successfully."
msgstr "" 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 #: lib/cannery_web/live/ammo_group_live/show.ex:55
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Ammo deleted succesfully" msgid "Ammo deleted succesfully"
@ -236,7 +236,7 @@ msgid_plural "Ammo added successfully"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:97 #: lib/cannery_web/live/ammo_type_live/index.html.heex:122
#: lib/cannery_web/live/ammo_type_live/show.html.heex:29 #: lib/cannery_web/live/ammo_type_live/show.html.heex:29
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Are you sure you want to delete %{name}? This will delete all %{name} type ammo as well!" msgid "Are you sure you want to delete %{name}? This will delete all %{name} type ammo as well!"

View File

@ -56,6 +56,7 @@ msgstr ""
msgid "Not found" msgid "Not found"
msgstr "" 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_registration/new.html.heex:13
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:13 #: lib/cannery_web/templates/user_reset_password/edit.html.heex:13
#: lib/cannery_web/templates/user_settings/edit.html.heex:22 #: lib/cannery_web/templates/user_settings/edit.html.heex:22
@ -103,22 +104,22 @@ msgstr ""
msgid "You are not authorized to view this page." msgid "You are not authorized to view this page."
msgstr "" msgstr ""
#: lib/cannery/accounts/user.ex:144 #: lib/cannery/accounts/user.ex:145
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "did not change" msgid "did not change"
msgstr "" msgstr ""
#: lib/cannery/accounts/user.ex:165 #: lib/cannery/accounts/user.ex:166
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "does not match password" msgid "does not match password"
msgstr "" msgstr ""
#: lib/cannery/accounts/user.ex:202 #: lib/cannery/accounts/user.ex:203
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "is not valid" msgid "is not valid"
msgstr "" msgstr ""
#: lib/cannery/accounts/user.ex:99 #: lib/cannery/accounts/user.ex:100
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "must have the @ sign and no spaces" msgid "must have the @ sign and no spaces"
msgstr "" msgstr ""
@ -154,7 +155,7 @@ msgstr ""
msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}" msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}"
msgstr "" msgstr ""
#: lib/cannery/ammo.ex:1043 #: lib/cannery/ammo.ex:1123
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Invalid multiplier" msgid "Invalid multiplier"
msgstr "" msgstr ""
@ -169,27 +170,27 @@ msgstr ""
msgid "Your browser does not support the canvas element." msgid "Your browser does not support the canvas element."
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:72 #: lib/cannery/activity_log/shot_group.ex:74
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Please select a valid user and ammo pack" msgid "Please select a valid user and ammo pack"
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:86 #: lib/cannery/activity_log/shot_group.ex:88
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Ammo left can be at most %{count} rounds" msgid "Ammo left can be at most %{count} rounds"
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:82 #: lib/cannery/activity_log/shot_group.ex:84
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Ammo left must be at least 0" msgid "Ammo left must be at least 0"
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:119 #: lib/cannery/activity_log/shot_group.ex:122
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Count can be at most %{count} shots" msgid "Count can be at most %{count} shots"
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:78 #: lib/cannery/activity_log/shot_group.ex:80
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "can't be blank" msgid "can't be blank"
msgstr "" msgstr ""

View File

@ -23,14 +23,14 @@ msgstr ""
## Run "mix gettext.extract" to bring this file up to ## Run "mix gettext.extract" to bring this file up to
## date. Leave "msgstr"s empty as changing them here has no ## date. Leave "msgstr"s empty as changing them here has no
## effect: edit them in PO (.po) files instead. ## 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:59
#: lib/cannery_web/live/ammo_group_live/index.ex:62 #: lib/cannery_web/live/ammo_group_live/index.ex:67
#: lib/cannery_web/live/ammo_group_live/index.html.heex:41 #: lib/cannery_web/live/ammo_group_live/index.html.heex:38
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Add Ammo" msgid "Add Ammo"
msgstr "Añadir Munición" 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 #, elixir-autogen, elixir-format
msgid "Add your first box!" msgid "Add your first box!"
msgstr "¡Añade tu primera caja!" msgstr "¡Añade tu primera caja!"
@ -133,12 +133,12 @@ msgstr "Reenviar instrucciones de confirmación"
msgid "Reset password" msgid "Reset password"
msgstr "Resetear contraseña" 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_group_live/form_component.html.heex:84
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:159 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:350
#: lib/cannery_web/live/container_live/form_component.html.heex:55 #: lib/cannery_web/live/container_live/form_component.html.heex:57
#: lib/cannery_web/live/invite_live/form_component.html.heex:32 #: lib/cannery_web/live/invite_live/form_component.html.heex:35
#: lib/cannery_web/live/range_live/form_component.html.heex:44 #: lib/cannery_web/live/range_live/form_component.html.heex:45
#: lib/cannery_web/live/tag_live/form_component.html.heex:37 #: lib/cannery_web/live/tag_live/form_component.html.heex:37
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Save" msgid "Save"
@ -149,7 +149,7 @@ msgstr "Guardar"
msgid "Send instructions to reset password" msgid "Send instructions to reset password"
msgstr "Enviar instrucciones para reestablecer contraseña" 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 #, elixir-autogen, elixir-format
msgid "Why not add one?" msgid "Why not add one?"
msgstr "¿Por qué no añadir una?" msgstr "¿Por qué no añadir una?"
@ -169,7 +169,7 @@ msgstr "Preparar munición"
msgid "Why not get some ready to shoot?" msgid "Why not get some ready to shoot?"
msgstr "¿Por qué no preparar parte para disparar?" msgstr "¿Por qué no preparar parte para disparar?"
#: lib/cannery_web/live/ammo_group_live/index.html.heex:105 #: 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/ammo_group_live/show.html.heex:103
#: lib/cannery_web/live/range_live/index.html.heex:45 #: lib/cannery_web/live/range_live/index.html.heex:45
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -191,7 +191,7 @@ msgstr "Seleccionar"
msgid "Copy to clipboard" msgid "Copy to clipboard"
msgstr "Copiar al portapapeles" 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 #, elixir-autogen, elixir-format
msgid "add a container first" msgid "add a container first"
msgstr "añade primero un contenedor" msgstr "añade primero un contenedor"
@ -216,13 +216,13 @@ msgstr "Cambiar lenguaje"
msgid "View in Catalog" msgid "View in Catalog"
msgstr "Ver en Catalogo" 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 #, elixir-autogen, elixir-format
msgid "add an ammo type first" msgid "add an ammo type first"
msgstr "añade primero un tipo de munición" msgstr "añade primero un tipo de munición"
#: lib/cannery_web/components/move_ammo_group_component.ex:80 #: lib/cannery_web/components/move_ammo_group_component.ex:80
#: lib/cannery_web/live/ammo_group_live/index.html.heex:122 #: lib/cannery_web/live/ammo_group_live/index.html.heex:144
#: lib/cannery_web/live/ammo_group_live/show.html.heex:96 #: lib/cannery_web/live/ammo_group_live/show.html.heex:96
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Move ammo" msgid "Move ammo"
@ -250,13 +250,13 @@ msgstr "Desmontar del campo de tiro"
msgid "Export Data as JSON" msgid "Export Data as JSON"
msgstr "Exportar datos como JSON" msgstr "Exportar datos como JSON"
#: lib/cannery_web/live/ammo_type_live/index.html.heex:85 #: lib/cannery_web/live/ammo_type_live/index.html.heex:110
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Clone %{ammo_type_name}" msgid "Clone %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:88 #: lib/cannery_web/live/container_live/index.html.heex:87
#: lib/cannery_web/live/container_live/index.html.heex:144 #: lib/cannery_web/live/container_live/index.html.heex:145
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Clone %{container_name}" msgid "Clone %{container_name}"
msgstr "" msgstr ""
@ -266,20 +266,20 @@ msgstr ""
msgid "Copy invite link for %{invite_name}" msgid "Copy invite link for %{invite_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:104 #: lib/cannery_web/live/ammo_type_live/index.html.heex:129
#: lib/cannery_web/live/ammo_type_live/show.html.heex:36 #: lib/cannery_web/live/ammo_type_live/show.html.heex:36
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete %{ammo_type_name}" msgid "Delete %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:103 #: lib/cannery_web/live/container_live/index.html.heex:104
#: lib/cannery_web/live/container_live/index.html.heex:159 #: lib/cannery_web/live/container_live/index.html.heex:162
#: lib/cannery_web/live/container_live/show.html.heex:55 #: lib/cannery_web/live/container_live/show.html.heex:48
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete %{container_name}" msgid "Delete %{container_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/tag_live/index.html.heex:66 #: lib/cannery_web/live/tag_live/index.html.heex:65
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete %{tag_name}" msgid "Delete %{tag_name}"
msgstr "" msgstr ""
@ -290,30 +290,30 @@ msgid "Delete invite for %{invite_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/show.ex:161 #: lib/cannery_web/live/ammo_group_live/show.ex:161
#: lib/cannery_web/live/range_live/index.html.heex:131 #: lib/cannery_web/live/range_live/index.html.heex:155
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete shot record of %{shot_group_count} shots" msgid "Delete shot record of %{shot_group_count} shots"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:75 #: lib/cannery_web/live/ammo_type_live/index.html.heex:100
#: lib/cannery_web/live/ammo_type_live/show.html.heex:19 #: lib/cannery_web/live/ammo_type_live/show.html.heex:19
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit %{ammo_type_name}" msgid "Edit %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:78 #: lib/cannery_web/live/container_live/index.html.heex:77
#: lib/cannery_web/live/container_live/index.html.heex:134 #: lib/cannery_web/live/container_live/index.html.heex:135
#: lib/cannery_web/live/container_live/show.html.heex:42 #: lib/cannery_web/live/container_live/show.html.heex:35
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit %{container_name}" msgid "Edit %{container_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/tag_live/index.html.heex:53 #: lib/cannery_web/live/tag_live/index.html.heex:52
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit %{tag_name}" msgid "Edit %{tag_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:144 #: lib/cannery_web/live/ammo_group_live/index.html.heex:166
#: lib/cannery_web/live/ammo_group_live/show.html.heex:62 #: lib/cannery_web/live/ammo_group_live/show.html.heex:62
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit ammo group of %{ammo_group_count} bullets" msgid "Edit ammo group of %{ammo_group_count} bullets"
@ -329,45 +329,45 @@ msgstr ""
msgid "Edit shot group of %{shot_group_count} shots" msgid "Edit shot group of %{shot_group_count} shots"
msgstr "" msgstr ""
#: lib/cannery_web/live/range_live/index.html.heex:114 #: lib/cannery_web/live/range_live/index.html.heex:138
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit shot record of %{shot_group_count} shots" msgid "Edit shot record of %{shot_group_count} shots"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:98 #: lib/cannery_web/live/ammo_group_live/index.html.heex:120
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Stage" msgid "Stage"
msgstr "Preparar munición" msgstr "Preparar munición"
#: lib/cannery_web/live/container_live/index.html.heex:66 #: lib/cannery_web/live/container_live/index.html.heex:65
#: lib/cannery_web/live/container_live/index.html.heex:123 #: lib/cannery_web/live/container_live/index.html.heex:124
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Tag %{container_name}" msgid "Tag %{container_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:97 #: lib/cannery_web/live/ammo_group_live/index.html.heex:119
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Unstage" msgid "Unstage"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:65 #: lib/cannery_web/live/ammo_type_live/index.html.heex:90
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "View %{ammo_type_name}" msgid "View %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:156 #: lib/cannery_web/live/ammo_group_live/index.html.heex:178
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Clone ammo group of %{ammo_group_count} bullets" msgid "Clone ammo group of %{ammo_group_count} bullets"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:171 #: lib/cannery_web/live/ammo_group_live/index.html.heex:193
#: lib/cannery_web/live/ammo_group_live/show.html.heex:76 #: lib/cannery_web/live/ammo_group_live/show.html.heex:76
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Delete ammo group of %{ammo_group_count} bullets" msgid "Delete ammo group of %{ammo_group_count} bullets"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:132 #: lib/cannery_web/live/ammo_group_live/index.html.heex:154
#: lib/cannery_web/live/ammo_type_live/show.html.heex:189 #: lib/cannery_web/live/ammo_type_live/show.html.heex:206
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "View ammo group of %{ammo_group_count} bullets" msgid "View ammo group of %{ammo_group_count} bullets"
msgstr "" msgstr ""

File diff suppressed because it is too large Load Diff

View File

@ -69,6 +69,7 @@ msgstr "Correo o contraseña incorrecta"
msgid "Not found" msgid "Not found"
msgstr "No se encontró" 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_registration/new.html.heex:13
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:13 #: lib/cannery_web/templates/user_reset_password/edit.html.heex:13
#: lib/cannery_web/templates/user_settings/edit.html.heex:22 #: lib/cannery_web/templates/user_settings/edit.html.heex:22
@ -119,22 +120,22 @@ msgstr "El enlace de confirmación de usuario no es válido o ha caducado."
msgid "You are not authorized to view this page." msgid "You are not authorized to view this page."
msgstr "No está autorizado a ver esta página." 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 #, elixir-autogen, elixir-format
msgid "did not change" msgid "did not change"
msgstr "no cambió" msgstr "no cambió"
#: lib/cannery/accounts/user.ex:165 #: lib/cannery/accounts/user.ex:166
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "does not match password" msgid "does not match password"
msgstr "no coincide con la contraseña" msgstr "no coincide con la contraseña"
#: lib/cannery/accounts/user.ex:202 #: lib/cannery/accounts/user.ex:203
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "is not valid" msgid "is not valid"
msgstr "no es válido" msgstr "no es válido"
#: lib/cannery/accounts/user.ex:99 #: lib/cannery/accounts/user.ex:100
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "must have the @ sign and no spaces" msgid "must have the @ sign and no spaces"
msgstr "debe tener el signo @ y no contener espacios" msgstr "debe tener el signo @ y no contener espacios"
@ -170,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}" 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" msgstr "Número inválido de copias, debe ser entre 1 y %{max}. Fue %{multiplier"
#: lib/cannery/ammo.ex:1043 #: lib/cannery/ammo.ex:1123
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Invalid multiplier" msgid "Invalid multiplier"
msgstr "Multiplicador inválido" msgstr "Multiplicador inválido"
@ -185,27 +186,27 @@ msgstr "Por favor escoja un tipo de munición y un contenedor"
msgid "Your browser does not support the canvas element." msgid "Your browser does not support the canvas element."
msgstr "Su navegador no es compatible con el elemento lienzo." 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 #, elixir-autogen, elixir-format
msgid "Please select a valid user and ammo pack" msgid "Please select a valid user and ammo pack"
msgstr "Por favor escoja un usuario y tipo de munición valido" 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 #, elixir-autogen, elixir-format
msgid "Ammo left can be at most %{count} rounds" msgid "Ammo left can be at most %{count} rounds"
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:82 #: lib/cannery/activity_log/shot_group.ex:84
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Ammo left must be at least 0" msgid "Ammo left must be at least 0"
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:119 #: lib/cannery/activity_log/shot_group.ex:122
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Count can be at most %{count} shots" msgid "Count can be at most %{count} shots"
msgstr "El recuento debe ser menos de %{count}" 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 #, elixir-autogen, elixir-format
msgid "can't be blank" msgid "can't be blank"
msgstr "" msgstr ""

View File

@ -32,7 +32,7 @@ msgid "%{name} created successfully"
msgstr "%{name} creado exitosamente" msgstr "%{name} creado exitosamente"
#: lib/cannery_web/live/ammo_type_live/index.ex:72 #: lib/cannery_web/live/ammo_type_live/index.ex:72
#: lib/cannery_web/live/ammo_type_live/show.ex:49 #: lib/cannery_web/live/ammo_type_live/show.ex:27
#: lib/cannery_web/live/tag_live/index.ex:65 #: lib/cannery_web/live/tag_live/index.ex:65
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{name} deleted succesfully" msgid "%{name} deleted succesfully"
@ -65,15 +65,15 @@ msgstr ""
msgid "Are you sure you want to delete %{email}? This action is permanent!" 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!" msgstr "Está seguro que desea eliminar %{email}? Esta acción es permanente!"
#: lib/cannery_web/live/container_live/index.html.heex:100 #: lib/cannery_web/live/container_live/index.html.heex:99
#: lib/cannery_web/live/container_live/index.html.heex:156 #: lib/cannery_web/live/container_live/index.html.heex:157
#: lib/cannery_web/live/container_live/show.html.heex:52 #: lib/cannery_web/live/container_live/show.html.heex:45
#: lib/cannery_web/live/tag_live/index.html.heex:64 #: lib/cannery_web/live/tag_live/index.html.heex:63
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure you want to delete %{name}?" msgid "Are you sure you want to delete %{name}?"
msgstr "Está seguro que desea eliminar %{name}?" msgstr "Está seguro que desea eliminar %{name}?"
#: lib/cannery_web/live/ammo_group_live/index.html.heex:169 #: lib/cannery_web/live/ammo_group_live/index.html.heex:191
#: lib/cannery_web/live/ammo_group_live/show.html.heex:74 #: lib/cannery_web/live/ammo_group_live/show.html.heex:74
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure you want to delete this ammo?" 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" msgid "Please check your email to verify your account"
msgstr "Por favor chequea el correo para verificar tu cuenta" 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_group_live/form_component.html.heex:85
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:160 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:351
#: lib/cannery_web/live/container_live/form_component.html.heex:57 #: lib/cannery_web/live/container_live/form_component.html.heex:59
#: lib/cannery_web/live/invite_live/form_component.html.heex:34 #: lib/cannery_web/live/invite_live/form_component.html.heex:37
#: lib/cannery_web/live/range_live/form_component.html.heex:46 #: lib/cannery_web/live/range_live/form_component.html.heex:47
#: lib/cannery_web/live/tag_live/form_component.html.heex:39 #: lib/cannery_web/live/tag_live/form_component.html.heex:39
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Saving..." 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?" msgstr "Está seguro que desea desmontar esta munición?"
#: lib/cannery_web/live/ammo_group_live/show.ex:159 #: lib/cannery_web/live/ammo_group_live/show.ex:159
#: lib/cannery_web/live/range_live/index.html.heex:128 #: lib/cannery_web/live/range_live/index.html.heex:152
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure you want to delete this shot record?" msgid "Are you sure you want to delete this shot record?"
msgstr "¿Está segure que quiere borrar este récord de disparos?" msgstr "¿Está segure que quiere borrar este récord de disparos?"
@ -212,8 +212,8 @@ msgstr "Copiado al portapapeles"
msgid "%{name} removed successfully" msgid "%{name} removed successfully"
msgstr "%{name} eliminado exitosamente" 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:10
#: lib/cannery_web/live/ammo_group_live/index.html.heex:28 #: lib/cannery_web/live/ammo_group_live/index.html.heex:20
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "You'll need to" msgid "You'll need to"
msgstr "Necesitará hacerlo" msgstr "Necesitará hacerlo"
@ -233,7 +233,7 @@ msgstr "¿Está segure de que quiere cambiar el idioma?"
msgid "Language updated successfully." msgid "Language updated successfully."
msgstr "Idioma cambiado exitosamente." 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 #: lib/cannery_web/live/ammo_group_live/show.ex:55
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Ammo deleted succesfully" msgid "Ammo deleted succesfully"
@ -256,7 +256,7 @@ msgid_plural "Ammo added successfully"
msgstr[0] "Munición añadida exitosamente" msgstr[0] "Munición añadida exitosamente"
msgstr[1] "Municiones añadidas exitosamente" msgstr[1] "Municiones añadidas exitosamente"
#: lib/cannery_web/live/ammo_type_live/index.html.heex:97 #: lib/cannery_web/live/ammo_type_live/index.html.heex:122
#: lib/cannery_web/live/ammo_type_live/show.html.heex:29 #: lib/cannery_web/live/ammo_type_live/show.html.heex:29
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure you want to delete %{name}? This will delete all %{name} type ammo as well!" msgid "Are you sure you want to delete %{name}? This will delete all %{name} type ammo as well!"

View File

@ -23,14 +23,14 @@ msgstr ""
# # Run "mix gettext.extract" to bring this file up to # # Run "mix gettext.extract" to bring this file up to
# # date. Leave "msgstr"s empty as changing them here has no # # date. Leave "msgstr"s empty as changing them here has no
# # effect: edit them in PO (.po) files instead. # # 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:59
#: lib/cannery_web/live/ammo_group_live/index.ex:62 #: lib/cannery_web/live/ammo_group_live/index.ex:67
#: lib/cannery_web/live/ammo_group_live/index.html.heex:41 #: lib/cannery_web/live/ammo_group_live/index.html.heex:38
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Add Ammo" msgid "Add Ammo"
msgstr "ajouter munition" 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 #, elixir-autogen, elixir-format
msgid "Add your first box!" msgid "Add your first box!"
msgstr "Ajoutez votre première caisse !" msgstr "Ajoutez votre première caisse !"
@ -133,12 +133,12 @@ msgstr "Renvoyer les instructions de confirmation"
msgid "Reset password" msgid "Reset password"
msgstr "Réinitialisé le mot de passe" 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_group_live/form_component.html.heex:84
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:159 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:350
#: lib/cannery_web/live/container_live/form_component.html.heex:55 #: lib/cannery_web/live/container_live/form_component.html.heex:57
#: lib/cannery_web/live/invite_live/form_component.html.heex:32 #: lib/cannery_web/live/invite_live/form_component.html.heex:35
#: lib/cannery_web/live/range_live/form_component.html.heex:44 #: lib/cannery_web/live/range_live/form_component.html.heex:45
#: lib/cannery_web/live/tag_live/form_component.html.heex:37 #: lib/cannery_web/live/tag_live/form_component.html.heex:37
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Save" msgid "Save"
@ -149,7 +149,7 @@ msgstr "Sauvegarder"
msgid "Send instructions to reset password" msgid "Send instructions to reset password"
msgstr "Envoyer les instructions pour réinitialiser le mot de passe" 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 #, elixir-autogen, elixir-format
msgid "Why not add one?" msgid "Why not add one?"
msgstr "Pourquoi pas en ajouter un?" msgstr "Pourquoi pas en ajouter un?"
@ -169,7 +169,7 @@ msgstr "Munition préparée"
msgid "Why not get some ready to shoot?" msgid "Why not get some ready to shoot?"
msgstr "Pourquoi pas en préparer pour tirer?" msgstr "Pourquoi pas en préparer pour tirer?"
#: lib/cannery_web/live/ammo_group_live/index.html.heex:105 #: 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/ammo_group_live/show.html.heex:103
#: lib/cannery_web/live/range_live/index.html.heex:45 #: lib/cannery_web/live/range_live/index.html.heex:45
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -191,7 +191,7 @@ msgstr "Sélectionner"
msgid "Copy to clipboard" msgid "Copy to clipboard"
msgstr "Copier dans le presse-papier" 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 #, elixir-autogen, elixir-format
msgid "add a container first" msgid "add a container first"
msgstr "ajouter un conteneur en premier" msgstr "ajouter un conteneur en premier"
@ -216,13 +216,13 @@ msgstr "Changer la langue"
msgid "View in Catalog" msgid "View in Catalog"
msgstr "Voir en catalogue" 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 #, elixir-autogen, elixir-format
msgid "add an ammo type first" msgid "add an ammo type first"
msgstr "Ajoutez d'abord un type de munitions" msgstr "Ajoutez d'abord un type de munitions"
#: lib/cannery_web/components/move_ammo_group_component.ex:80 #: lib/cannery_web/components/move_ammo_group_component.ex:80
#: lib/cannery_web/live/ammo_group_live/index.html.heex:122 #: lib/cannery_web/live/ammo_group_live/index.html.heex:144
#: lib/cannery_web/live/ammo_group_live/show.html.heex:96 #: lib/cannery_web/live/ammo_group_live/show.html.heex:96
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Move ammo" msgid "Move ammo"
@ -250,13 +250,13 @@ msgstr ""
msgid "Export Data as JSON" msgid "Export Data as JSON"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:85 #: lib/cannery_web/live/ammo_type_live/index.html.heex:110
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Clone %{ammo_type_name}" msgid "Clone %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:88 #: lib/cannery_web/live/container_live/index.html.heex:87
#: lib/cannery_web/live/container_live/index.html.heex:144 #: lib/cannery_web/live/container_live/index.html.heex:145
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Clone %{container_name}" msgid "Clone %{container_name}"
msgstr "" msgstr ""
@ -266,20 +266,20 @@ msgstr ""
msgid "Copy invite link for %{invite_name}" msgid "Copy invite link for %{invite_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:104 #: lib/cannery_web/live/ammo_type_live/index.html.heex:129
#: lib/cannery_web/live/ammo_type_live/show.html.heex:36 #: lib/cannery_web/live/ammo_type_live/show.html.heex:36
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete %{ammo_type_name}" msgid "Delete %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:103 #: lib/cannery_web/live/container_live/index.html.heex:104
#: lib/cannery_web/live/container_live/index.html.heex:159 #: lib/cannery_web/live/container_live/index.html.heex:162
#: lib/cannery_web/live/container_live/show.html.heex:55 #: lib/cannery_web/live/container_live/show.html.heex:48
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete %{container_name}" msgid "Delete %{container_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/tag_live/index.html.heex:66 #: lib/cannery_web/live/tag_live/index.html.heex:65
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete %{tag_name}" msgid "Delete %{tag_name}"
msgstr "" msgstr ""
@ -290,30 +290,30 @@ msgid "Delete invite for %{invite_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/show.ex:161 #: lib/cannery_web/live/ammo_group_live/show.ex:161
#: lib/cannery_web/live/range_live/index.html.heex:131 #: lib/cannery_web/live/range_live/index.html.heex:155
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete shot record of %{shot_group_count} shots" msgid "Delete shot record of %{shot_group_count} shots"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:75 #: lib/cannery_web/live/ammo_type_live/index.html.heex:100
#: lib/cannery_web/live/ammo_type_live/show.html.heex:19 #: lib/cannery_web/live/ammo_type_live/show.html.heex:19
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit %{ammo_type_name}" msgid "Edit %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:78 #: lib/cannery_web/live/container_live/index.html.heex:77
#: lib/cannery_web/live/container_live/index.html.heex:134 #: lib/cannery_web/live/container_live/index.html.heex:135
#: lib/cannery_web/live/container_live/show.html.heex:42 #: lib/cannery_web/live/container_live/show.html.heex:35
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit %{container_name}" msgid "Edit %{container_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/tag_live/index.html.heex:53 #: lib/cannery_web/live/tag_live/index.html.heex:52
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit %{tag_name}" msgid "Edit %{tag_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:144 #: lib/cannery_web/live/ammo_group_live/index.html.heex:166
#: lib/cannery_web/live/ammo_group_live/show.html.heex:62 #: lib/cannery_web/live/ammo_group_live/show.html.heex:62
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit ammo group of %{ammo_group_count} bullets" msgid "Edit ammo group of %{ammo_group_count} bullets"
@ -329,45 +329,45 @@ msgstr ""
msgid "Edit shot group of %{shot_group_count} shots" msgid "Edit shot group of %{shot_group_count} shots"
msgstr "" msgstr ""
#: lib/cannery_web/live/range_live/index.html.heex:114 #: lib/cannery_web/live/range_live/index.html.heex:138
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit shot record of %{shot_group_count} shots" msgid "Edit shot record of %{shot_group_count} shots"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:98 #: lib/cannery_web/live/ammo_group_live/index.html.heex:120
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Stage" msgid "Stage"
msgstr "Munition préparée" msgstr "Munition préparée"
#: lib/cannery_web/live/container_live/index.html.heex:66 #: lib/cannery_web/live/container_live/index.html.heex:65
#: lib/cannery_web/live/container_live/index.html.heex:123 #: lib/cannery_web/live/container_live/index.html.heex:124
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Tag %{container_name}" msgid "Tag %{container_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:97 #: lib/cannery_web/live/ammo_group_live/index.html.heex:119
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Unstage" msgid "Unstage"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:65 #: lib/cannery_web/live/ammo_type_live/index.html.heex:90
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "View %{ammo_type_name}" msgid "View %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:156 #: lib/cannery_web/live/ammo_group_live/index.html.heex:178
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Clone ammo group of %{ammo_group_count} bullets" msgid "Clone ammo group of %{ammo_group_count} bullets"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:171 #: lib/cannery_web/live/ammo_group_live/index.html.heex:193
#: lib/cannery_web/live/ammo_group_live/show.html.heex:76 #: lib/cannery_web/live/ammo_group_live/show.html.heex:76
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Delete ammo group of %{ammo_group_count} bullets" msgid "Delete ammo group of %{ammo_group_count} bullets"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:132 #: lib/cannery_web/live/ammo_group_live/index.html.heex:154
#: lib/cannery_web/live/ammo_type_live/show.html.heex:189 #: lib/cannery_web/live/ammo_type_live/show.html.heex:206
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "View ammo group of %{ammo_group_count} bullets" msgid "View ammo group of %{ammo_group_count} bullets"
msgstr "" msgstr ""

File diff suppressed because it is too large Load Diff

View File

@ -69,6 +69,7 @@ msgstr "Mél ou mot de passe invalide"
msgid "Not found" msgid "Not found"
msgstr "Pas trouvé" 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_registration/new.html.heex:13
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:13 #: lib/cannery_web/templates/user_reset_password/edit.html.heex:13
#: lib/cannery_web/templates/user_settings/edit.html.heex:22 #: lib/cannery_web/templates/user_settings/edit.html.heex:22
@ -118,22 +119,22 @@ msgstr "Le lien de confirmation dutilisateur·ice est invalide ou a expiré."
msgid "You are not authorized to view this page." msgid "You are not authorized to view this page."
msgstr "Vous nêtes pas autorisé·e à voir cette 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 #, elixir-autogen, elixir-format
msgid "did not change" msgid "did not change"
msgstr "est inchangé" msgstr "est inchangé"
#: lib/cannery/accounts/user.ex:165 #: lib/cannery/accounts/user.ex:166
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "does not match password" msgid "does not match password"
msgstr "le mot de passe ne correspond pas" msgstr "le mot de passe ne correspond pas"
#: lib/cannery/accounts/user.ex:202 #: lib/cannery/accounts/user.ex:203
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "is not valid" msgid "is not valid"
msgstr "nest pas valide" msgstr "nest pas valide"
#: lib/cannery/accounts/user.ex:99 #: lib/cannery/accounts/user.ex:100
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "must have the @ sign and no spaces" msgid "must have the @ sign and no spaces"
msgstr "doit contenir le symbole @ et aucune espace" msgstr "doit contenir le symbole @ et aucune espace"
@ -171,7 +172,7 @@ msgstr "Impossible d'analyser le nombre de copies"
msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}" 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}" msgstr "Nombre de copies invalide, doit être 1 et %{max}. Été %{multiplier}"
#: lib/cannery/ammo.ex:1043 #: lib/cannery/ammo.ex:1123
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Invalid multiplier" msgid "Invalid multiplier"
msgstr "Multiplicateur invalide" msgstr "Multiplicateur invalide"
@ -186,27 +187,27 @@ msgstr "Veuillez choisir un type de munitions et un conteneur"
msgid "Your browser does not support the canvas element." msgid "Your browser does not support the canvas element."
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:72 #: lib/cannery/activity_log/shot_group.ex:74
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Please select a valid user and ammo pack" msgid "Please select a valid user and ammo pack"
msgstr "Veuillez choisir un utilisateur valide et un groupe de munitions" 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 #, elixir-autogen, elixir-format
msgid "Ammo left can be at most %{count} rounds" msgid "Ammo left can be at most %{count} rounds"
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:82 #: lib/cannery/activity_log/shot_group.ex:84
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Ammo left must be at least 0" msgid "Ammo left must be at least 0"
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:119 #: lib/cannery/activity_log/shot_group.ex:122
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Count can be at most %{count} shots" msgid "Count can be at most %{count} shots"
msgstr "La quantité doit être inférieur à %{count}" 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 #, elixir-autogen, elixir-format
msgid "can't be blank" msgid "can't be blank"
msgstr "" msgstr ""

View File

@ -32,7 +32,7 @@ msgid "%{name} created successfully"
msgstr "%{name} créé· avec succès" msgstr "%{name} créé· avec succès"
#: lib/cannery_web/live/ammo_type_live/index.ex:72 #: lib/cannery_web/live/ammo_type_live/index.ex:72
#: lib/cannery_web/live/ammo_type_live/show.ex:49 #: lib/cannery_web/live/ammo_type_live/show.ex:27
#: lib/cannery_web/live/tag_live/index.ex:65 #: lib/cannery_web/live/tag_live/index.ex:65
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{name} deleted succesfully" msgid "%{name} deleted succesfully"
@ -66,15 +66,15 @@ msgid "Are you sure you want to delete %{email}? This action is permanent!"
msgstr "" msgstr ""
"Êtes-vous certain·e de supprimer %{email}? Cette action est définitive!" "Êtes-vous certain·e de supprimer %{email}? Cette action est définitive!"
#: lib/cannery_web/live/container_live/index.html.heex:100 #: lib/cannery_web/live/container_live/index.html.heex:99
#: lib/cannery_web/live/container_live/index.html.heex:156 #: lib/cannery_web/live/container_live/index.html.heex:157
#: lib/cannery_web/live/container_live/show.html.heex:52 #: lib/cannery_web/live/container_live/show.html.heex:45
#: lib/cannery_web/live/tag_live/index.html.heex:64 #: lib/cannery_web/live/tag_live/index.html.heex:63
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure you want to delete %{name}?" msgid "Are you sure you want to delete %{name}?"
msgstr "Êtes-vous certain·e de supprimer %{name}?" msgstr "Êtes-vous certain·e de supprimer %{name}?"
#: lib/cannery_web/live/ammo_group_live/index.html.heex:169 #: lib/cannery_web/live/ammo_group_live/index.html.heex:191
#: lib/cannery_web/live/ammo_group_live/show.html.heex:74 #: lib/cannery_web/live/ammo_group_live/show.html.heex:74
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure you want to delete this ammo?" 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" msgid "Please check your email to verify your account"
msgstr "Veuillez vérifier votre mél pour confirmer votre compte" 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_group_live/form_component.html.heex:85
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:160 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:351
#: lib/cannery_web/live/container_live/form_component.html.heex:57 #: lib/cannery_web/live/container_live/form_component.html.heex:59
#: lib/cannery_web/live/invite_live/form_component.html.heex:34 #: lib/cannery_web/live/invite_live/form_component.html.heex:37
#: lib/cannery_web/live/range_live/form_component.html.heex:46 #: lib/cannery_web/live/range_live/form_component.html.heex:47
#: lib/cannery_web/live/tag_live/form_component.html.heex:39 #: lib/cannery_web/live/tag_live/form_component.html.heex:39
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Saving..." 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?" 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/ammo_group_live/show.ex:159
#: lib/cannery_web/live/range_live/index.html.heex:128 #: lib/cannery_web/live/range_live/index.html.heex:152
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure you want to delete this shot record?" msgid "Are you sure you want to delete this shot record?"
msgstr "Êtes-vous certain·e de vouloir supprimer cet enregistrement de tir?" msgstr "Êtes-vous certain·e de vouloir supprimer cet enregistrement de tir?"
@ -214,8 +214,8 @@ msgstr "Copié dans le presse-papier"
msgid "%{name} removed successfully" msgid "%{name} removed successfully"
msgstr "%{name} retiré avec succès" 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:10
#: lib/cannery_web/live/ammo_group_live/index.html.heex:28 #: lib/cannery_web/live/ammo_group_live/index.html.heex:20
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "You'll need to" msgid "You'll need to"
msgstr "Vous aurez besoin de" msgstr "Vous aurez besoin de"
@ -235,7 +235,7 @@ msgstr "Êtes-vous certain·e de vouloir changer votre langue?"
msgid "Language updated successfully." msgid "Language updated successfully."
msgstr "Langue mise à jour avec succès." 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 #: lib/cannery_web/live/ammo_group_live/show.ex:55
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Ammo deleted succesfully" msgid "Ammo deleted succesfully"
@ -258,7 +258,7 @@ msgid_plural "Ammo added successfully"
msgstr[0] "Groupe de munition mis à jour avec succès" msgstr[0] "Groupe de munition mis à jour avec succès"
msgstr[1] "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:97 #: lib/cannery_web/live/ammo_type_live/index.html.heex:122
#: lib/cannery_web/live/ammo_type_live/show.html.heex:29 #: lib/cannery_web/live/ammo_type_live/show.html.heex:29
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Are you sure you want to delete %{name}? This will delete all %{name} type ammo as well!" msgid "Are you sure you want to delete %{name}? This will delete all %{name} type ammo as well!"

View File

@ -21,14 +21,14 @@ msgstr ""
## Run "mix gettext.extract" to bring this file up to ## Run "mix gettext.extract" to bring this file up to
## date. Leave "msgstr"s empty as changing them here has no ## date. Leave "msgstr"s empty as changing them here has no
## effect: edit them in PO (.po) files instead. ## 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:59
#: lib/cannery_web/live/ammo_group_live/index.ex:62 #: lib/cannery_web/live/ammo_group_live/index.ex:67
#: lib/cannery_web/live/ammo_group_live/index.html.heex:41 #: lib/cannery_web/live/ammo_group_live/index.html.heex:38
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Add Ammo" msgid "Add Ammo"
msgstr "" 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 #, elixir-autogen, elixir-format
msgid "Add your first box!" msgid "Add your first box!"
msgstr "" msgstr ""
@ -131,12 +131,12 @@ msgstr ""
msgid "Reset password" msgid "Reset password"
msgstr "" 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_group_live/form_component.html.heex:84
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:159 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:350
#: lib/cannery_web/live/container_live/form_component.html.heex:55 #: lib/cannery_web/live/container_live/form_component.html.heex:57
#: lib/cannery_web/live/invite_live/form_component.html.heex:32 #: lib/cannery_web/live/invite_live/form_component.html.heex:35
#: lib/cannery_web/live/range_live/form_component.html.heex:44 #: lib/cannery_web/live/range_live/form_component.html.heex:45
#: lib/cannery_web/live/tag_live/form_component.html.heex:37 #: lib/cannery_web/live/tag_live/form_component.html.heex:37
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Save" msgid "Save"
@ -147,7 +147,7 @@ msgstr ""
msgid "Send instructions to reset password" msgid "Send instructions to reset password"
msgstr "" 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 #, elixir-autogen, elixir-format
msgid "Why not add one?" msgid "Why not add one?"
msgstr "" msgstr ""
@ -167,7 +167,7 @@ msgstr ""
msgid "Why not get some ready to shoot?" msgid "Why not get some ready to shoot?"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:105 #: 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/ammo_group_live/show.html.heex:103
#: lib/cannery_web/live/range_live/index.html.heex:45 #: lib/cannery_web/live/range_live/index.html.heex:45
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
@ -189,7 +189,7 @@ msgstr ""
msgid "Copy to clipboard" msgid "Copy to clipboard"
msgstr "" 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 #, elixir-autogen, elixir-format
msgid "add a container first" msgid "add a container first"
msgstr "" msgstr ""
@ -214,13 +214,13 @@ msgstr ""
msgid "View in Catalog" msgid "View in Catalog"
msgstr "" 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 #, elixir-autogen, elixir-format
msgid "add an ammo type first" msgid "add an ammo type first"
msgstr "" msgstr ""
#: lib/cannery_web/components/move_ammo_group_component.ex:80 #: lib/cannery_web/components/move_ammo_group_component.ex:80
#: lib/cannery_web/live/ammo_group_live/index.html.heex:122 #: lib/cannery_web/live/ammo_group_live/index.html.heex:144
#: lib/cannery_web/live/ammo_group_live/show.html.heex:96 #: lib/cannery_web/live/ammo_group_live/show.html.heex:96
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Move ammo" msgid "Move ammo"
@ -248,13 +248,13 @@ msgstr ""
msgid "Export Data as JSON" msgid "Export Data as JSON"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:85 #: lib/cannery_web/live/ammo_type_live/index.html.heex:110
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Clone %{ammo_type_name}" msgid "Clone %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:88 #: lib/cannery_web/live/container_live/index.html.heex:87
#: lib/cannery_web/live/container_live/index.html.heex:144 #: lib/cannery_web/live/container_live/index.html.heex:145
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Clone %{container_name}" msgid "Clone %{container_name}"
msgstr "" msgstr ""
@ -264,20 +264,20 @@ msgstr ""
msgid "Copy invite link for %{invite_name}" msgid "Copy invite link for %{invite_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:104 #: lib/cannery_web/live/ammo_type_live/index.html.heex:129
#: lib/cannery_web/live/ammo_type_live/show.html.heex:36 #: lib/cannery_web/live/ammo_type_live/show.html.heex:36
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete %{ammo_type_name}" msgid "Delete %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:103 #: lib/cannery_web/live/container_live/index.html.heex:104
#: lib/cannery_web/live/container_live/index.html.heex:159 #: lib/cannery_web/live/container_live/index.html.heex:162
#: lib/cannery_web/live/container_live/show.html.heex:55 #: lib/cannery_web/live/container_live/show.html.heex:48
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete %{container_name}" msgid "Delete %{container_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/tag_live/index.html.heex:66 #: lib/cannery_web/live/tag_live/index.html.heex:65
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete %{tag_name}" msgid "Delete %{tag_name}"
msgstr "" msgstr ""
@ -288,30 +288,30 @@ msgid "Delete invite for %{invite_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/show.ex:161 #: lib/cannery_web/live/ammo_group_live/show.ex:161
#: lib/cannery_web/live/range_live/index.html.heex:131 #: lib/cannery_web/live/range_live/index.html.heex:155
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete shot record of %{shot_group_count} shots" msgid "Delete shot record of %{shot_group_count} shots"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:75 #: lib/cannery_web/live/ammo_type_live/index.html.heex:100
#: lib/cannery_web/live/ammo_type_live/show.html.heex:19 #: lib/cannery_web/live/ammo_type_live/show.html.heex:19
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit %{ammo_type_name}" msgid "Edit %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:78 #: lib/cannery_web/live/container_live/index.html.heex:77
#: lib/cannery_web/live/container_live/index.html.heex:134 #: lib/cannery_web/live/container_live/index.html.heex:135
#: lib/cannery_web/live/container_live/show.html.heex:42 #: lib/cannery_web/live/container_live/show.html.heex:35
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit %{container_name}" msgid "Edit %{container_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/tag_live/index.html.heex:53 #: lib/cannery_web/live/tag_live/index.html.heex:52
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit %{tag_name}" msgid "Edit %{tag_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:144 #: lib/cannery_web/live/ammo_group_live/index.html.heex:166
#: lib/cannery_web/live/ammo_group_live/show.html.heex:62 #: lib/cannery_web/live/ammo_group_live/show.html.heex:62
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit ammo group of %{ammo_group_count} bullets" msgid "Edit ammo group of %{ammo_group_count} bullets"
@ -327,45 +327,45 @@ msgstr ""
msgid "Edit shot group of %{shot_group_count} shots" msgid "Edit shot group of %{shot_group_count} shots"
msgstr "" msgstr ""
#: lib/cannery_web/live/range_live/index.html.heex:114 #: lib/cannery_web/live/range_live/index.html.heex:138
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit shot record of %{shot_group_count} shots" msgid "Edit shot record of %{shot_group_count} shots"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:98 #: lib/cannery_web/live/ammo_group_live/index.html.heex:120
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Stage" msgid "Stage"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:66 #: lib/cannery_web/live/container_live/index.html.heex:65
#: lib/cannery_web/live/container_live/index.html.heex:123 #: lib/cannery_web/live/container_live/index.html.heex:124
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Tag %{container_name}" msgid "Tag %{container_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:97 #: lib/cannery_web/live/ammo_group_live/index.html.heex:119
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Unstage" msgid "Unstage"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:65 #: lib/cannery_web/live/ammo_type_live/index.html.heex:90
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "View %{ammo_type_name}" msgid "View %{ammo_type_name}"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:156 #: lib/cannery_web/live/ammo_group_live/index.html.heex:178
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Clone ammo group of %{ammo_group_count} bullets" msgid "Clone ammo group of %{ammo_group_count} bullets"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:171 #: lib/cannery_web/live/ammo_group_live/index.html.heex:193
#: lib/cannery_web/live/ammo_group_live/show.html.heex:76 #: lib/cannery_web/live/ammo_group_live/show.html.heex:76
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Delete ammo group of %{ammo_group_count} bullets" msgid "Delete ammo group of %{ammo_group_count} bullets"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:132 #: lib/cannery_web/live/ammo_group_live/index.html.heex:154
#: lib/cannery_web/live/ammo_type_live/show.html.heex:189 #: lib/cannery_web/live/ammo_type_live/show.html.heex:206
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "View ammo group of %{ammo_group_count} bullets" msgid "View ammo group of %{ammo_group_count} bullets"
msgstr "" msgstr ""

File diff suppressed because it is too large Load Diff

View File

@ -70,6 +70,7 @@ msgstr "Seoladh email nó pasfhocal neamhbhailí"
msgid "Not found" msgid "Not found"
msgstr "Ní feidir é a fáil" 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_registration/new.html.heex:13
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:13 #: lib/cannery_web/templates/user_reset_password/edit.html.heex:13
#: lib/cannery_web/templates/user_settings/edit.html.heex:22 #: lib/cannery_web/templates/user_settings/edit.html.heex:22
@ -119,22 +120,22 @@ msgstr "Tá nasc an úsáideoir a deimhnigh neamhbailí nó as dáta."
msgid "You are not authorized to view this page." msgid "You are not authorized to view this page."
msgstr "Níl cead agaibh féachaint ar an leathanach seo." 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 #, elixir-autogen, elixir-format
msgid "did not change" msgid "did not change"
msgstr "Níor athraigh sé" msgstr "Níor athraigh sé"
#: lib/cannery/accounts/user.ex:165 #: lib/cannery/accounts/user.ex:166
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "does not match password" msgid "does not match password"
msgstr "" msgstr ""
#: lib/cannery/accounts/user.ex:202 #: lib/cannery/accounts/user.ex:203
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "is not valid" msgid "is not valid"
msgstr "" msgstr ""
#: lib/cannery/accounts/user.ex:99 #: lib/cannery/accounts/user.ex:100
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "must have the @ sign and no spaces" msgid "must have the @ sign and no spaces"
msgstr "" msgstr ""
@ -170,7 +171,7 @@ msgstr ""
msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}" msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}"
msgstr "" msgstr ""
#: lib/cannery/ammo.ex:1043 #: lib/cannery/ammo.ex:1123
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Invalid multiplier" msgid "Invalid multiplier"
msgstr "" msgstr ""
@ -185,27 +186,27 @@ msgstr ""
msgid "Your browser does not support the canvas element." msgid "Your browser does not support the canvas element."
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:72 #: lib/cannery/activity_log/shot_group.ex:74
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Please select a valid user and ammo pack" msgid "Please select a valid user and ammo pack"
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:86 #: lib/cannery/activity_log/shot_group.ex:88
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Ammo left can be at most %{count} rounds" msgid "Ammo left can be at most %{count} rounds"
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:82 #: lib/cannery/activity_log/shot_group.ex:84
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Ammo left must be at least 0" msgid "Ammo left must be at least 0"
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:119 #: lib/cannery/activity_log/shot_group.ex:122
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Count can be at most %{count} shots" msgid "Count can be at most %{count} shots"
msgstr "" msgstr ""
#: lib/cannery/activity_log/shot_group.ex:78 #: lib/cannery/activity_log/shot_group.ex:80
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "can't be blank" msgid "can't be blank"
msgstr "" msgstr ""

View File

@ -30,7 +30,7 @@ msgid "%{name} created successfully"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.ex:72 #: lib/cannery_web/live/ammo_type_live/index.ex:72
#: lib/cannery_web/live/ammo_type_live/show.ex:49 #: lib/cannery_web/live/ammo_type_live/show.ex:27
#: lib/cannery_web/live/tag_live/index.ex:65 #: lib/cannery_web/live/tag_live/index.ex:65
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{name} deleted succesfully" msgid "%{name} deleted succesfully"
@ -61,15 +61,15 @@ msgstr ""
msgid "Are you sure you want to delete %{email}? This action is permanent!" msgid "Are you sure you want to delete %{email}? This action is permanent!"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:100 #: lib/cannery_web/live/container_live/index.html.heex:99
#: lib/cannery_web/live/container_live/index.html.heex:156 #: lib/cannery_web/live/container_live/index.html.heex:157
#: lib/cannery_web/live/container_live/show.html.heex:52 #: lib/cannery_web/live/container_live/show.html.heex:45
#: lib/cannery_web/live/tag_live/index.html.heex:64 #: lib/cannery_web/live/tag_live/index.html.heex:63
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure you want to delete %{name}?" msgid "Are you sure you want to delete %{name}?"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:169 #: lib/cannery_web/live/ammo_group_live/index.html.heex:191
#: lib/cannery_web/live/ammo_group_live/show.html.heex:74 #: lib/cannery_web/live/ammo_group_live/show.html.heex:74
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure you want to delete this ammo?" msgid "Are you sure you want to delete this ammo?"
@ -120,12 +120,12 @@ msgstr ""
msgid "Please check your email to verify your account" msgid "Please check your email to verify your account"
msgstr "" 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_group_live/form_component.html.heex:85
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:160 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:351
#: lib/cannery_web/live/container_live/form_component.html.heex:57 #: lib/cannery_web/live/container_live/form_component.html.heex:59
#: lib/cannery_web/live/invite_live/form_component.html.heex:34 #: lib/cannery_web/live/invite_live/form_component.html.heex:37
#: lib/cannery_web/live/range_live/form_component.html.heex:46 #: lib/cannery_web/live/range_live/form_component.html.heex:47
#: lib/cannery_web/live/tag_live/form_component.html.heex:39 #: lib/cannery_web/live/tag_live/form_component.html.heex:39
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Saving..." msgid "Saving..."
@ -167,7 +167,7 @@ msgid "Are you sure you want to unstage this ammo?"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/show.ex:159 #: lib/cannery_web/live/ammo_group_live/show.ex:159
#: lib/cannery_web/live/range_live/index.html.heex:128 #: lib/cannery_web/live/range_live/index.html.heex:152
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure you want to delete this shot record?" msgid "Are you sure you want to delete this shot record?"
msgstr "" msgstr ""
@ -203,8 +203,8 @@ msgstr ""
msgid "%{name} removed successfully" msgid "%{name} removed successfully"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:18 #: lib/cannery_web/live/ammo_group_live/index.html.heex:10
#: lib/cannery_web/live/ammo_group_live/index.html.heex:28 #: lib/cannery_web/live/ammo_group_live/index.html.heex:20
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "You'll need to" msgid "You'll need to"
msgstr "" msgstr ""
@ -224,7 +224,7 @@ msgstr ""
msgid "Language updated successfully." msgid "Language updated successfully."
msgstr "" 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 #: lib/cannery_web/live/ammo_group_live/show.ex:55
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Ammo deleted succesfully" msgid "Ammo deleted succesfully"
@ -250,7 +250,7 @@ msgstr[2] ""
msgstr[3] "" msgstr[3] ""
msgstr[4] "" msgstr[4] ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:97 #: lib/cannery_web/live/ammo_type_live/index.html.heex:122
#: lib/cannery_web/live/ammo_type_live/show.html.heex:29 #: lib/cannery_web/live/ammo_type_live/show.html.heex:29
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure you want to delete %{name}? This will delete all %{name} type ammo as well!" msgid "Are you sure you want to delete %{name}? This will delete all %{name} type ammo as well!"

View File

@ -19,7 +19,7 @@ msgid "%{name} created successfully"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_type_live/index.ex:72 #: lib/cannery_web/live/ammo_type_live/index.ex:72
#: lib/cannery_web/live/ammo_type_live/show.ex:49 #: lib/cannery_web/live/ammo_type_live/show.ex:27
#: lib/cannery_web/live/tag_live/index.ex:65 #: lib/cannery_web/live/tag_live/index.ex:65
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "%{name} deleted succesfully" msgid "%{name} deleted succesfully"
@ -50,15 +50,15 @@ msgstr ""
msgid "Are you sure you want to delete %{email}? This action is permanent!" msgid "Are you sure you want to delete %{email}? This action is permanent!"
msgstr "" msgstr ""
#: lib/cannery_web/live/container_live/index.html.heex:100 #: lib/cannery_web/live/container_live/index.html.heex:99
#: lib/cannery_web/live/container_live/index.html.heex:156 #: lib/cannery_web/live/container_live/index.html.heex:157
#: lib/cannery_web/live/container_live/show.html.heex:52 #: lib/cannery_web/live/container_live/show.html.heex:45
#: lib/cannery_web/live/tag_live/index.html.heex:64 #: lib/cannery_web/live/tag_live/index.html.heex:63
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure you want to delete %{name}?" msgid "Are you sure you want to delete %{name}?"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:169 #: lib/cannery_web/live/ammo_group_live/index.html.heex:191
#: lib/cannery_web/live/ammo_group_live/show.html.heex:74 #: lib/cannery_web/live/ammo_group_live/show.html.heex:74
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure you want to delete this ammo?" msgid "Are you sure you want to delete this ammo?"
@ -109,12 +109,12 @@ msgstr ""
msgid "Please check your email to verify your account" msgid "Please check your email to verify your account"
msgstr "" 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_group_live/form_component.html.heex:85
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:160 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:351
#: lib/cannery_web/live/container_live/form_component.html.heex:57 #: lib/cannery_web/live/container_live/form_component.html.heex:59
#: lib/cannery_web/live/invite_live/form_component.html.heex:34 #: lib/cannery_web/live/invite_live/form_component.html.heex:37
#: lib/cannery_web/live/range_live/form_component.html.heex:46 #: lib/cannery_web/live/range_live/form_component.html.heex:47
#: lib/cannery_web/live/tag_live/form_component.html.heex:39 #: lib/cannery_web/live/tag_live/form_component.html.heex:39
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Saving..." msgid "Saving..."
@ -156,7 +156,7 @@ msgid "Are you sure you want to unstage this ammo?"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/show.ex:159 #: lib/cannery_web/live/ammo_group_live/show.ex:159
#: lib/cannery_web/live/range_live/index.html.heex:128 #: lib/cannery_web/live/range_live/index.html.heex:152
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure you want to delete this shot record?" msgid "Are you sure you want to delete this shot record?"
msgstr "" msgstr ""
@ -192,8 +192,8 @@ msgstr ""
msgid "%{name} removed successfully" msgid "%{name} removed successfully"
msgstr "" msgstr ""
#: lib/cannery_web/live/ammo_group_live/index.html.heex:18 #: lib/cannery_web/live/ammo_group_live/index.html.heex:10
#: lib/cannery_web/live/ammo_group_live/index.html.heex:28 #: lib/cannery_web/live/ammo_group_live/index.html.heex:20
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "You'll need to" msgid "You'll need to"
msgstr "" msgstr ""
@ -213,7 +213,7 @@ msgstr ""
msgid "Language updated successfully." msgid "Language updated successfully."
msgstr "" 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 #: lib/cannery_web/live/ammo_group_live/show.ex:55
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Ammo deleted succesfully" msgid "Ammo deleted succesfully"
@ -236,7 +236,7 @@ msgid_plural "Ammo added successfully"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: lib/cannery_web/live/ammo_type_live/index.html.heex:97 #: lib/cannery_web/live/ammo_type_live/index.html.heex:122
#: lib/cannery_web/live/ammo_type_live/show.html.heex:29 #: lib/cannery_web/live/ammo_type_live/show.html.heex:29
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure you want to delete %{name}? This will delete all %{name} type ammo as well!" msgid "Are you sure you want to delete %{name}? This will delete all %{name} type ammo as well!"

View File

@ -2,7 +2,7 @@ defmodule Cannery.Repo.Migrations.AddLocaleSetting do
use Ecto.Migration use Ecto.Migration
def change do def change do
alter table("users") do alter table(:users) do
add :locale, :string add :locale, :string
end end
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 @moduletag :invites_test
@valid_attrs %{ @valid_attrs %{
"name" => "some name" name: "some name"
} }
@invalid_attrs %{ @invalid_attrs %{
"name" => nil, name: nil,
"token" => nil token: nil
} }
describe "invites" do describe "invites" do
@ -57,7 +57,7 @@ defmodule Cannery.InvitesTest do
assert {:ok, _user} = assert {:ok, _user} =
Accounts.register_user( Accounts.register_user(
%{"email" => unique_user_email(), "password" => valid_user_password()}, %{email: unique_user_email(), password: valid_user_password()},
token token
) )
@ -65,7 +65,7 @@ defmodule Cannery.InvitesTest do
assert {:ok, _user} = assert {:ok, _user} =
Accounts.register_user( Accounts.register_user(
%{"email" => unique_user_email(), "password" => valid_user_password()}, %{email: unique_user_email(), password: valid_user_password()},
token token
) )
@ -81,13 +81,13 @@ defmodule Cannery.InvitesTest do
assert {:ok, _user} = assert {:ok, _user} =
Accounts.register_user( Accounts.register_user(
%{"email" => unique_user_email(), "password" => valid_user_password()}, %{email: unique_user_email(), password: valid_user_password()},
token token
) )
assert {:ok, _user} = assert {:ok, _user} =
Accounts.register_user( Accounts.register_user(
%{"email" => unique_user_email(), "password" => valid_user_password()}, %{email: unique_user_email(), password: valid_user_password()},
another_token another_token
) )
@ -97,7 +97,7 @@ defmodule Cannery.InvitesTest do
assert {:ok, _user} = assert {:ok, _user} =
Accounts.register_user( Accounts.register_user(
%{"email" => unique_user_email(), "password" => valid_user_password()}, %{email: unique_user_email(), password: valid_user_password()},
token token
) )
@ -138,21 +138,14 @@ defmodule Cannery.InvitesTest do
test "create_invite/1 with valid data creates an unlimited invite", test "create_invite/1 with valid data creates an unlimited invite",
%{current_user: current_user} do %{current_user: current_user} do
assert {:ok, %Invite{} = invite} = assert {:ok, %Invite{} = invite} = Invites.create_invite(current_user, %{name: "some name"})
Invites.create_invite(current_user, %{
"name" => "some name"
})
assert invite.name == "some name" assert invite.name == "some name"
end end
test "create_invite/1 with valid data creates a limited invite", test "create_invite/1 with valid data creates a limited invite",
%{current_user: current_user} do %{current_user: current_user} do
assert {:ok, %Invite{} = invite} = assert {:ok, %Invite{} = invite} =
Invites.create_invite(current_user, %{ Invites.create_invite(current_user, %{name: "some name", uses_left: 10})
"name" => "some name",
"uses_left" => 10
})
assert invite.name == "some name" assert invite.name == "some name"
assert invite.uses_left == 10 assert invite.uses_left == 10
@ -168,7 +161,7 @@ defmodule Cannery.InvitesTest do
assert {:ok, %Invite{} = new_invite} = assert {:ok, %Invite{} = new_invite} =
Invites.update_invite( Invites.update_invite(
invite, invite,
%{"name" => "some updated name", "uses_left" => 5}, %{name: "some updated name", uses_left: 5},
current_user current_user
) )
@ -178,12 +171,12 @@ defmodule Cannery.InvitesTest do
test "update_invite/2 can set an invite to be unlimited", test "update_invite/2 can set an invite to be unlimited",
%{invite: invite, current_user: current_user} do %{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} = assert {:ok, %Invite{} = new_invite} =
Invites.update_invite( Invites.update_invite(
invite, invite,
%{"name" => "some updated name", "uses_left" => nil}, %{name: "some updated name", uses_left: nil},
current_user current_user
) )

View File

@ -63,8 +63,7 @@ defmodule Cannery.AccountsTest do
end end
test "validates email and password when given" do test "validates email and password when given" do
{:error, changeset} = {:error, changeset} = Accounts.register_user(%{email: "not valid", password: "not valid"})
Accounts.register_user(%{"email" => "not valid", "password" => "not valid"})
assert %{ assert %{
email: ["must have the @ sign and no spaces"], 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 test "validates maximum values for email and password for security" do
too_long = String.duplicate("db", 100) 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 160 character(s)" in errors_on(changeset).email
assert "should be at most 80 character(s)" in errors_on(changeset).password assert "should be at most 80 character(s)" in errors_on(changeset).password
end end
test "validates email uniqueness" do test "validates email uniqueness" do
%{email: email} = user_fixture() %{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 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. # 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 assert "has already been taken" in errors_on(changeset).email
end end
test "registers users with a hashed password" do test "registers users with a hashed password" do
email = unique_user_email() email = unique_user_email()
{:ok, user} = {:ok, user} = Accounts.register_user(%{email: email, password: valid_user_password()})
Accounts.register_user(%{"email" => email, "password" => valid_user_password()})
assert user.email == email assert user.email == email
assert is_binary(user.hashed_password) assert is_binary(user.hashed_password)
@ -103,11 +101,11 @@ defmodule Cannery.AccountsTest do
test "records used invite during registration" do test "records used invite during registration" do
{:ok, %{id: invite_id, token: token}} = {: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}} = assert {:ok, %{invite_id: ^invite_id}} =
Accounts.register_user( Accounts.register_user(
%{"email" => unique_user_email(), "password" => valid_user_password()}, %{email: unique_user_email(), password: valid_user_password()},
token token
) )
end end
@ -123,7 +121,7 @@ defmodule Cannery.AccountsTest do
email = unique_user_email() email = unique_user_email()
password = valid_user_password() 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 changeset.valid?
assert get_change(changeset, :email) == email assert get_change(changeset, :email) == email
@ -151,7 +149,7 @@ defmodule Cannery.AccountsTest do
test "validates email", %{user: user} do test "validates email", %{user: user} do
{:error, changeset} = {: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) assert %{email: ["must have the @ sign and no spaces"]} = errors_on(changeset)
end end
@ -160,7 +158,7 @@ defmodule Cannery.AccountsTest do
too_long = String.duplicate("db", 100) too_long = String.duplicate("db", 100)
{:error, changeset} = {: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 assert "should be at most 160 character(s)" in errors_on(changeset).email
end end
@ -169,21 +167,21 @@ defmodule Cannery.AccountsTest do
%{email: email} = user_fixture() %{email: email} = user_fixture()
{:error, changeset} = {: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 assert "has already been taken" in errors_on(changeset).email
end end
test "validates current password", %{user: user} do test "validates current password", %{user: user} do
{:error, changeset} = {: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) assert %{current_password: ["is not valid"]} = errors_on(changeset)
end end
test "applies the email without persisting it", %{user: user} do test "applies the email without persisting it", %{user: user} do
email = unique_user_email() 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 user.email == email
assert Accounts.get_user!(user.id).email != email assert Accounts.get_user!(user.id).email != email
end end
@ -258,11 +256,7 @@ defmodule Cannery.AccountsTest do
end end
test "allows fields to be set" do test "allows fields to be set" do
changeset = changeset = Accounts.change_user_password(%User{}, %{password: "new valid password"})
Accounts.change_user_password(%User{}, %{
"password" => "new valid password"
})
assert changeset.valid? assert changeset.valid?
assert get_change(changeset, :password) == "new valid password" assert get_change(changeset, :password) == "new valid password"
assert is_nil(get_change(changeset, :hashed_password)) assert is_nil(get_change(changeset, :hashed_password))
@ -277,8 +271,8 @@ defmodule Cannery.AccountsTest do
test "validates password", %{user: user} do test "validates password", %{user: user} do
{:error, changeset} = {:error, changeset} =
Accounts.update_user_password(user, valid_user_password(), %{ Accounts.update_user_password(user, valid_user_password(), %{
"password" => "not valid", password: "not valid",
"password_confirmation" => "another" password_confirmation: "another"
}) })
assert %{ assert %{
@ -291,14 +285,14 @@ defmodule Cannery.AccountsTest do
too_long = String.duplicate("db", 100) too_long = String.duplicate("db", 100)
{:error, changeset} = {: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 assert "should be at most 80 character(s)" in errors_on(changeset).password
end end
test "validates current password", %{user: user} do test "validates current password", %{user: user} do
{:error, changeset} = {: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) assert %{current_password: ["is not valid"]} = errors_on(changeset)
end end
@ -306,7 +300,7 @@ defmodule Cannery.AccountsTest do
test "updates the password", %{user: user} do test "updates the password", %{user: user} do
{:ok, user} = {:ok, user} =
Accounts.update_user_password(user, valid_user_password(), %{ Accounts.update_user_password(user, valid_user_password(), %{
"password" => "new valid password" password: "new valid password"
}) })
assert is_nil(user.password) assert is_nil(user.password)
@ -318,7 +312,7 @@ defmodule Cannery.AccountsTest do
{:ok, _} = {:ok, _} =
Accounts.update_user_password(user, valid_user_password(), %{ 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) refute Repo.get_by(UserToken, user_id: user.id)
@ -486,8 +480,8 @@ defmodule Cannery.AccountsTest do
test "validates password", %{user: user} do test "validates password", %{user: user} do
{:error, changeset} = {:error, changeset} =
Accounts.reset_user_password(user, %{ Accounts.reset_user_password(user, %{
"password" => "not valid", password: "not valid",
"password_confirmation" => "another" password_confirmation: "another"
}) })
assert %{ assert %{
@ -498,13 +492,12 @@ defmodule Cannery.AccountsTest do
test "validates maximum values for password for security", %{user: user} do test "validates maximum values for password for security", %{user: user} do
too_long = String.duplicate("db", 100) 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 assert "should be at most 80 character(s)" in errors_on(changeset).password
end end
test "updates the password", %{user: user} do test "updates the password", %{user: user} do
{:ok, updated_user} = {:ok, updated_user} = Accounts.reset_user_password(user, %{password: "new valid password"})
Accounts.reset_user_password(user, %{"password" => "new valid password"})
assert is_nil(updated_user.password) assert is_nil(updated_user.password)
assert Accounts.get_user_by_email_and_password(user.email, "new valid 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 test "deletes all tokens for the given user", %{user: user} do
_session_token = Accounts.generate_user_session_token(user) _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) refute Repo.get_by(UserToken, user_id: user.id)
end end
end end

View File

@ -5,12 +5,7 @@ defmodule Cannery.ActivityLogTest do
use Cannery.DataCase use Cannery.DataCase
import Cannery.Fixtures import Cannery.Fixtures
alias Cannery.{ActivityLog, ActivityLog.ShotGroup, Ammo}
alias Cannery.{
ActivityLog,
ActivityLog.ShotGroup,
Ammo
}
@moduletag :activity_log_test @moduletag :activity_log_test
@ -21,10 +16,10 @@ defmodule Cannery.ActivityLogTest do
ammo_type = ammo_type_fixture(current_user) ammo_type = ammo_type_fixture(current_user)
{1, [%{id: ammo_group_id} = ammo_group]} = {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 = 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_fixture(current_user, ammo_group)
ammo_group = ammo_group_id |> Ammo.get_ammo_group!(current_user) ammo_group = ammo_group_id |> Ammo.get_ammo_group!(current_user)
@ -38,50 +33,6 @@ defmodule Cannery.ActivityLogTest do
] ]
end 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", test "get_shot_group!/2 returns the shot_group with given id",
%{shot_group: shot_group, current_user: current_user} do %{shot_group: shot_group, current_user: current_user} do
assert ActivityLog.get_shot_group!(shot_group.id, current_user) == shot_group 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", test "create_shot_group/3 with valid data creates a shot_group",
%{current_user: current_user, ammo_group: ammo_group} do %{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} = assert {:ok, %ShotGroup{} = shot_group} =
ActivityLog.create_shot_group(valid_attrs, current_user, ammo_group) ActivityLog.create_shot_group(valid_attrs, current_user, ammo_group)
@ -113,7 +64,7 @@ defmodule Cannery.ActivityLogTest do
current_user: current_user, current_user: current_user,
ammo_group: %{id: ammo_group_id, count: org_count} = ammo_group ammo_group: %{id: ammo_group_id, count: org_count} = ammo_group
} do } 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} = assert {:ok, %ShotGroup{} = shot_group} =
ActivityLog.create_shot_group(valid_attrs, current_user, ammo_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", 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 %{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{}} = assert {:ok, %ShotGroup{}} =
ActivityLog.create_shot_group(valid_attrs, current_user, ammo_group) ActivityLog.create_shot_group(valid_attrs, current_user, ammo_group)
@ -136,12 +87,12 @@ defmodule Cannery.ActivityLogTest do
assert ammo_group.count == 0 assert ammo_group.count == 0
assert {:error, %Ecto.Changeset{}} = 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 end
test "create_shot_group/3 with invalid data returns error changeset", test "create_shot_group/3 with invalid data returns error changeset",
%{current_user: current_user, ammo_group: ammo_group} do %{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{}} = assert {:error, %Ecto.Changeset{}} =
ActivityLog.create_shot_group(invalid_params, current_user, ammo_group) ActivityLog.create_shot_group(invalid_params, current_user, ammo_group)
@ -157,9 +108,9 @@ defmodule Cannery.ActivityLogTest do
ActivityLog.update_shot_group( ActivityLog.update_shot_group(
shot_group, shot_group,
%{ %{
"count" => 10, count: 10,
"date" => ~D[2022-02-13], date: ~D[2022-02-13],
"notes" => "some updated notes" notes: "some updated notes"
}, },
current_user current_user
) )
@ -175,9 +126,9 @@ defmodule Cannery.ActivityLogTest do
ActivityLog.update_shot_group( ActivityLog.update_shot_group(
shot_group, shot_group,
%{ %{
"count" => 25, count: 25,
"date" => ~D[2022-02-13], date: ~D[2022-02-13],
"notes" => "some updated notes" notes: "some updated notes"
}, },
current_user current_user
) )
@ -193,14 +144,14 @@ defmodule Cannery.ActivityLogTest do
assert {:error, %Ecto.Changeset{}} = assert {:error, %Ecto.Changeset{}} =
ActivityLog.update_shot_group( ActivityLog.update_shot_group(
shot_group, shot_group,
%{"count" => 26, "date" => nil, "notes" => nil}, %{count: 26, date: nil, notes: nil},
current_user current_user
) )
assert {:error, %Ecto.Changeset{}} = assert {:error, %Ecto.Changeset{}} =
ActivityLog.update_shot_group( ActivityLog.update_shot_group(
shot_group, shot_group,
%{"count" => -1, "date" => nil, "notes" => nil}, %{count: -1, date: nil, notes: nil},
current_user current_user
) )
@ -228,10 +179,10 @@ defmodule Cannery.ActivityLogTest do
assert 0 = another_ammo_group |> ActivityLog.get_used_count(current_user) assert 0 = another_ammo_group |> ActivityLog.get_used_count(current_user)
assert 5 = 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) 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) assert 30 = ammo_group |> ActivityLog.get_used_count(current_user)
{1, [another_ammo_group]} = ammo_group_fixture(ammo_type, container, 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} == assert %{ammo_group_id => 5} ==
[ammo_group, another_ammo_group] |> ActivityLog.get_used_counts(current_user) [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) used_counts = [ammo_group, another_ammo_group] |> ActivityLog.get_used_counts(current_user)
assert %{^ammo_group_id => 5} = used_counts assert %{^ammo_group_id => 5} = used_counts
assert %{^another_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) used_counts = [ammo_group, another_ammo_group] |> ActivityLog.get_used_counts(current_user)
assert %{^ammo_group_id => 20} = used_counts assert %{^ammo_group_id => 20} = used_counts
assert %{^another_ammo_group_id => 5} = 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) used_counts = [ammo_group, another_ammo_group] |> ActivityLog.get_used_counts(current_user)
assert %{^ammo_group_id => 30} = used_counts assert %{^ammo_group_id => 30} = used_counts
assert %{^another_ammo_group_id => 5} = 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 another_ammo_group |> ActivityLog.get_last_used_date(current_user) |> is_nil()
assert ^date = ammo_group |> ActivityLog.get_last_used_date(current_user) 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) 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) assert ^date = ammo_group |> ActivityLog.get_last_used_date(current_user)
end end
@ -298,7 +249,7 @@ defmodule Cannery.ActivityLogTest do
assert %{ammo_group_id => date} == assert %{ammo_group_id => date} ==
[ammo_group, another_ammo_group] |> ActivityLog.get_last_used_dates(current_user) [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 # setting initial date
last_used_shot_groups = 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 assert %{^another_ammo_group_id => ~D[2022-11-09]} = last_used_shot_groups
# setting another date # 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 = last_used_shot_groups =
[ammo_group, another_ammo_group] |> ActivityLog.get_last_used_dates(current_user) [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 assert %{^another_ammo_group_id => ~D[2022-11-09]} = last_used_shot_groups
# setting yet another date # 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 = last_used_shot_groups =
[ammo_group, another_ammo_group] |> ActivityLog.get_last_used_dates(current_user) [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 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) 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) 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) assert 11 = ammo_type |> ActivityLog.get_used_count_for_ammo_type(current_user)
end end
@ -353,7 +304,7 @@ defmodule Cannery.ActivityLogTest do
|> ActivityLog.get_used_count_for_ammo_types(current_user) |> ActivityLog.get_used_count_for_ammo_types(current_user)
# use generated ammo group # use generated ammo group
shot_group_fixture(%{"count" => 5}, current_user, ammo_group) shot_group_fixture(%{count: 5}, current_user, ammo_group)
used_counts = used_counts =
[ammo_type, another_ammo_type] |> ActivityLog.get_used_count_for_ammo_types(current_user) [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 assert %{^another_ammo_type_id => 5} = used_counts
# use generated ammo group again # 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 = used_counts =
[ammo_type, another_ammo_type] |> ActivityLog.get_used_count_for_ammo_types(current_user) [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 assert %{^another_ammo_type_id => 6} = used_counts
end end
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 end

File diff suppressed because it is too large Load Diff

View File

@ -10,37 +10,37 @@ defmodule Cannery.ContainersTest do
@moduletag :containers_test @moduletag :containers_test
@valid_attrs %{ @valid_attrs %{
"desc" => "some desc", desc: "some desc",
"location" => "some location", location: "some location",
"name" => "some name", name: "some name",
"type" => "some type" type: "some type"
} }
@update_attrs %{ @update_attrs %{
"desc" => "some updated desc", desc: "some updated desc",
"location" => "some updated location", location: "some updated location",
"name" => "some updated name", name: "some updated name",
"type" => "some updated type" type: "some updated type"
} }
@invalid_attrs %{ @invalid_attrs %{
"desc" => nil, desc: nil,
"location" => nil, location: nil,
"name" => nil, name: nil,
"type" => nil type: nil
} }
@valid_tag_attrs %{ @valid_tag_attrs %{
"bg_color" => "some bg-color", bg_color: "#100000",
"name" => "some name", name: "some name",
"text_color" => "some text-color" text_color: "#000000"
} }
@update_tag_attrs %{ @update_tag_attrs %{
"bg_color" => "some updated bg-color", bg_color: "#100001",
"name" => "some updated name", name: "some updated name",
"text_color" => "some updated text-color" text_color: "#000001"
} }
@invalid_tag_attrs %{ @invalid_tag_attrs %{
"bg_color" => nil, bg_color: nil,
"name" => nil, name: nil,
"text_color" => nil text_color: nil
} }
describe "containers" do describe "containers" do
@ -57,25 +57,24 @@ defmodule Cannery.ContainersTest do
test "list_containers/2 returns relevant containers for a user", test "list_containers/2 returns relevant containers for a user",
%{current_user: current_user} do %{current_user: current_user} do
container_a = container_fixture(%{"name" => "my cool container"}, current_user) container_a = container_fixture(%{name: "my cool container"}, current_user)
container_b = container_fixture(%{"desc" => "a fascinating description"}, current_user) container_b = container_fixture(%{desc: "a fascinating description"}, current_user)
%{id: container_c_id} = %{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) Containers.add_tag!(container_c, tag, current_user)
container_c = container_c_id |> Containers.get_container!(current_user) container_c = container_c_id |> Containers.get_container!(current_user)
%{id: container_d_id} = %{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) Containers.add_tag!(container_d, tag, current_user)
container_d = container_d_id |> Containers.get_container!(current_user) container_d = container_d_id |> Containers.get_container!(current_user)
_shouldnt_return = _shouldnt_return = container_fixture(%{name: "another person's container"}, user_fixture())
container_fixture(%{"name" => "another person's container"}, user_fixture())
# attributes # attributes
assert Containers.list_containers("cool", current_user) == [container_a] assert Containers.list_containers("cool", current_user) == [container_a]
@ -109,7 +108,7 @@ defmodule Cannery.ContainersTest do
test "create_container/2 with valid data creates a container", %{current_user: current_user} do test "create_container/2 with valid data creates a container", %{current_user: current_user} do
assert {:ok, %Container{} = container} = 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.desc == "some desc"
assert container.location == "some location" assert container.location == "some location"
@ -120,7 +119,7 @@ defmodule Cannery.ContainersTest do
test "create_container/2 with invalid data returns error changeset", test "create_container/2 with invalid data returns error changeset",
%{current_user: current_user} do %{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 end
test "update_container/3 with valid data updates the container", test "update_container/3 with valid data updates the container",
@ -163,15 +162,10 @@ defmodule Cannery.ContainersTest do
end end
test "list_tags/2 returns relevant tags for a user", %{current_user: current_user} do test "list_tags/2 returns relevant tags for a user", %{current_user: current_user} do
tag_a = tag_fixture(%{"name" => "bullets"}, current_user) tag_a = tag_fixture(%{name: "bullets"}, current_user)
tag_b = tag_fixture(%{"name" => "hollows"}, current_user) tag_b = tag_fixture(%{name: "hollows"}, current_user)
_shouldnt_return = tag_fixture(%{name: "bullet", desc: "pews brass shell"}, user_fixture())
%{
"name" => "bullet",
"desc" => "pews brass shell"
}
|> tag_fixture(user_fixture())
# name # name
assert Containers.list_tags("bullet", current_user) == [tag_a] assert Containers.list_tags("bullet", current_user) == [tag_a]
@ -186,9 +180,9 @@ defmodule Cannery.ContainersTest do
test "create_tag/2 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 {: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.name == "some name"
assert tag.text_color == "some text-color" assert tag.text_color == "#000000"
end end
test "create_tag/2 with invalid data returns error changeset", test "create_tag/2 with invalid data returns error changeset",
@ -198,9 +192,9 @@ defmodule Cannery.ContainersTest do
test "update_tag/3 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 {: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.name == "some updated name"
assert tag.text_color == "some updated text-color" assert tag.text_color == "#000001"
end end
test "update_tag/3 with invalid data returns error changeset", test "update_tag/3 with invalid data returns error changeset",

View File

@ -4,7 +4,6 @@ defmodule CanneryWeb.UserAuthTest do
""" """
use CanneryWeb.ConnCase, async: true use CanneryWeb.ConnCase, async: true
import CanneryWeb.Gettext
alias Cannery.Accounts alias Cannery.Accounts
alias CanneryWeb.UserAuth alias CanneryWeb.UserAuth
@ -148,7 +147,7 @@ defmodule CanneryWeb.UserAuthTest do
assert redirected_to(conn) == Routes.user_session_path(conn, :new) assert redirected_to(conn) == Routes.user_session_path(conn, :new)
assert get_flash(conn, :error) == 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 end
test "stores the path to redirect to on GET", %{conn: conn} do 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 use CanneryWeb.ConnCase, async: true
import CanneryWeb.Gettext
alias Cannery.{Accounts, Repo} alias Cannery.{Accounts, Repo}
@moduletag :user_confirmation_controller_test @moduletag :user_confirmation_controller_test
@ -17,7 +16,7 @@ defmodule CanneryWeb.UserConfirmationControllerTest do
test "renders the confirmation page", %{conn: conn} do test "renders the confirmation page", %{conn: conn} do
conn = get(conn, Routes.user_confirmation_path(conn, :new)) conn = get(conn, Routes.user_confirmation_path(conn, :new))
response = html_response(conn, 200) response = html_response(conn, 200)
assert response =~ dgettext("actions", "Resend confirmation instructions") assert response =~ "Resend confirmation instructions"
end end
end end
@ -25,18 +24,12 @@ defmodule CanneryWeb.UserConfirmationControllerTest do
@tag :capture_log @tag :capture_log
test "sends a new confirmation token", %{conn: conn, user: user} do test "sends a new confirmation token", %{conn: conn, user: user} do
conn = conn =
post(conn, Routes.user_confirmation_path(conn, :create), %{ post(conn, Routes.user_confirmation_path(conn, :create), %{user: %{email: user.email}})
"user" => %{"email" => user.email}
})
assert redirected_to(conn) == "/" assert redirected_to(conn) == "/"
assert get_flash(conn, :info) =~ assert get_flash(conn, :info) =~
dgettext( "If your email is in our system and it has not been confirmed yet, you will receive an email with instructions shortly."
"prompts",
"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" assert Repo.get_by!(Accounts.UserToken, user_id: user.id).context == "confirm"
end end
@ -45,34 +38,24 @@ defmodule CanneryWeb.UserConfirmationControllerTest do
Repo.update!(Accounts.User.confirm_changeset(user)) Repo.update!(Accounts.User.confirm_changeset(user))
conn = conn =
post(conn, Routes.user_confirmation_path(conn, :create), %{ post(conn, Routes.user_confirmation_path(conn, :create), %{user: %{email: user.email}})
"user" => %{"email" => user.email}
})
assert redirected_to(conn) == "/" assert redirected_to(conn) == "/"
assert get_flash(conn, :info) =~ assert get_flash(conn, :info) =~
dgettext( "If your email is in our system and it has not been confirmed yet, you will receive an email with instructions shortly."
"prompts",
"If your email is in our system and it has not been confirmed yet, " <>
"you will receive an email with instructions shortly."
)
end end
test "does not send confirmation token if email is invalid", %{conn: conn} do test "does not send confirmation token if email is invalid", %{conn: conn} do
conn = conn =
post(conn, Routes.user_confirmation_path(conn, :create), %{ post(conn, Routes.user_confirmation_path(conn, :create), %{
"user" => %{"email" => "unknown@example.com"} user: %{email: "unknown@example.com"}
}) })
assert redirected_to(conn) == "/" assert redirected_to(conn) == "/"
assert get_flash(conn, :info) =~ assert get_flash(conn, :info) =~
dgettext( "If your email is in our system and it has not been confirmed yet, you will receive an email with instructions shortly."
"prompts",
"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) == [] assert Repo.all(Accounts.UserToken) == []
end end
@ -87,10 +70,7 @@ defmodule CanneryWeb.UserConfirmationControllerTest do
conn = get(conn, Routes.user_confirmation_path(conn, :confirm, token)) conn = get(conn, Routes.user_confirmation_path(conn, :confirm, token))
assert redirected_to(conn) == "/" assert redirected_to(conn) == "/"
assert get_flash(conn, :info) =~ "#{user.email} confirmed successfully"
assert get_flash(conn, :info) =~
dgettext("prompts", "%{email} confirmed successfully", email: user.email)
assert Accounts.get_user!(user.id).confirmed_at assert Accounts.get_user!(user.id).confirmed_at
refute get_session(conn, :user_token) refute get_session(conn, :user_token)
assert Repo.all(Accounts.UserToken) == [] assert Repo.all(Accounts.UserToken) == []
@ -99,8 +79,7 @@ defmodule CanneryWeb.UserConfirmationControllerTest do
conn = get(conn, Routes.user_confirmation_path(conn, :confirm, token)) conn = get(conn, Routes.user_confirmation_path(conn, :confirm, token))
assert redirected_to(conn) == "/" assert redirected_to(conn) == "/"
assert get_flash(conn, :error) =~ assert get_flash(conn, :error) =~ "User confirmation link is invalid or it has expired"
dgettext("errors", "User confirmation link is invalid or it has expired")
# When logged in # When logged in
conn = conn =
@ -115,10 +94,7 @@ defmodule CanneryWeb.UserConfirmationControllerTest do
test "does not confirm email with invalid token", %{conn: conn, user: user} do test "does not confirm email with invalid token", %{conn: conn, user: user} do
conn = get(conn, Routes.user_confirmation_path(conn, :confirm, "oops")) conn = get(conn, Routes.user_confirmation_path(conn, :confirm, "oops"))
assert redirected_to(conn) == "/" assert redirected_to(conn) == "/"
assert get_flash(conn, :error) =~ "User confirmation link is invalid or it has expired"
assert get_flash(conn, :error) =~
dgettext("errors", "User confirmation link is invalid or it has expired")
refute Accounts.get_user!(user.id).confirmed_at refute Accounts.get_user!(user.id).confirmed_at
end end
end end

View File

@ -4,7 +4,6 @@ defmodule CanneryWeb.UserRegistrationControllerTest do
""" """
use CanneryWeb.ConnCase, async: true use CanneryWeb.ConnCase, async: true
import CanneryWeb.Gettext
@moduletag :user_registration_controller_test @moduletag :user_registration_controller_test
@ -12,8 +11,8 @@ defmodule CanneryWeb.UserRegistrationControllerTest do
test "renders registration page", %{conn: conn} do test "renders registration page", %{conn: conn} do
conn = get(conn, Routes.user_registration_path(conn, :new)) conn = get(conn, Routes.user_registration_path(conn, :new))
response = html_response(conn, 200) response = html_response(conn, 200)
assert response =~ dgettext("actions", "Register") assert response =~ "Register"
assert response =~ dgettext("actions", "Log in") assert response =~ "Log in"
end end
test "redirects if already logged in", %{conn: conn} do test "redirects if already logged in", %{conn: conn} do
@ -29,11 +28,11 @@ defmodule CanneryWeb.UserRegistrationControllerTest do
conn = conn =
post(conn, Routes.user_registration_path(conn, :create), %{ 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) == %{ 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) =~ "/" assert redirected_to(conn) =~ "/"
@ -48,11 +47,11 @@ defmodule CanneryWeb.UserRegistrationControllerTest do
test "render errors for invalid data", %{conn: conn} do test "render errors for invalid data", %{conn: conn} do
conn = conn =
post(conn, Routes.user_registration_path(conn, :create), %{ 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) response = html_response(conn, 200)
assert response =~ gettext("Register") assert response =~ "Register"
assert response =~ "must have the @ sign and no spaces" assert response =~ "must have the @ sign and no spaces"
assert response =~ "should be at least 12 character" assert response =~ "should be at least 12 character"
end end

View File

@ -4,7 +4,6 @@ defmodule CanneryWeb.UserResetPasswordControllerTest do
""" """
use CanneryWeb.ConnCase, async: true use CanneryWeb.ConnCase, async: true
import CanneryWeb.Gettext
alias Cannery.{Accounts, Repo} alias Cannery.{Accounts, Repo}
@moduletag :user_reset_password_controller_test @moduletag :user_reset_password_controller_test
@ -17,7 +16,7 @@ defmodule CanneryWeb.UserResetPasswordControllerTest do
test "renders the reset password page", %{conn: conn} do test "renders the reset password page", %{conn: conn} do
conn = get(conn, Routes.user_reset_password_path(conn, :new)) conn = get(conn, Routes.user_reset_password_path(conn, :new))
response = html_response(conn, 200) response = html_response(conn, 200)
assert response =~ dgettext("actions", "Forgot your password?") assert response =~ "Forgot your password?"
end end
end end
@ -25,17 +24,12 @@ defmodule CanneryWeb.UserResetPasswordControllerTest do
@tag :capture_log @tag :capture_log
test "sends a new reset password token", %{conn: conn, user: user} do test "sends a new reset password token", %{conn: conn, user: user} do
conn = conn =
post(conn, Routes.user_reset_password_path(conn, :create), %{ post(conn, Routes.user_reset_password_path(conn, :create), %{user: %{email: user.email}})
"user" => %{"email" => user.email}
})
assert redirected_to(conn) == "/" assert redirected_to(conn) == "/"
assert get_flash(conn, :info) =~ assert get_flash(conn, :info) =~
dgettext( "If your email is in our system, you will receive instructions to reset your password shortly."
"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" assert Repo.get_by!(Accounts.UserToken, user_id: user.id).context == "reset_password"
end end
@ -43,16 +37,13 @@ defmodule CanneryWeb.UserResetPasswordControllerTest do
test "does not send reset password token if email is invalid", %{conn: conn} do test "does not send reset password token if email is invalid", %{conn: conn} do
conn = conn =
post(conn, Routes.user_reset_password_path(conn, :create), %{ post(conn, Routes.user_reset_password_path(conn, :create), %{
"user" => %{"email" => "unknown@example.com"} user: %{email: "unknown@example.com"}
}) })
assert redirected_to(conn) == "/" assert redirected_to(conn) == "/"
assert get_flash(conn, :info) =~ assert get_flash(conn, :info) =~
dgettext( "If your email is in our system, you will receive instructions to reset your password shortly."
"prompts",
"If your email is in our system, you will receive instructions to reset your password shortly."
)
assert Repo.all(Accounts.UserToken) == [] assert Repo.all(Accounts.UserToken) == []
end end
@ -70,15 +61,13 @@ defmodule CanneryWeb.UserResetPasswordControllerTest do
test "renders reset password", %{conn: conn, token: token} do test "renders reset password", %{conn: conn, token: token} do
conn = get(conn, Routes.user_reset_password_path(conn, :edit, token)) 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 end
test "does not render reset password with invalid token", %{conn: conn} do test "does not render reset password with invalid token", %{conn: conn} do
conn = get(conn, Routes.user_reset_password_path(conn, :edit, "oops")) conn = get(conn, Routes.user_reset_password_path(conn, :edit, "oops"))
assert redirected_to(conn) == "/" assert redirected_to(conn) == "/"
assert get_flash(conn, :error) =~ "Reset password link is invalid or it has expired"
assert get_flash(conn, :error) =~
dgettext("errors", "Reset password link is invalid or it has expired")
end end
end end
@ -95,39 +84,37 @@ defmodule CanneryWeb.UserResetPasswordControllerTest do
test "resets password once", %{conn: conn, user: user, token: token} do test "resets password once", %{conn: conn, user: user, token: token} do
conn = conn =
put(conn, Routes.user_reset_password_path(conn, :update, token), %{ put(conn, Routes.user_reset_password_path(conn, :update, token), %{
"user" => %{ user: %{
"password" => "new valid password", password: "new valid password",
"password_confirmation" => "new valid password" password_confirmation: "new valid password"
} }
}) })
assert redirected_to(conn) == Routes.user_session_path(conn, :new) assert redirected_to(conn) == Routes.user_session_path(conn, :new)
refute get_session(conn, :user_token) 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") assert Accounts.get_user_by_email_and_password(user.email, "new valid password")
end end
test "does not reset password on invalid data", %{conn: conn, token: token} do test "does not reset password on invalid data", %{conn: conn, token: token} do
conn = conn =
put(conn, Routes.user_reset_password_path(conn, :update, token), %{ put(conn, Routes.user_reset_password_path(conn, :update, token), %{
"user" => %{ user: %{
"password" => "too short", password: "too short",
"password_confirmation" => "does not match" password_confirmation: "does not match"
} }
}) })
response = html_response(conn, 200) response = html_response(conn, 200)
assert response =~ gettext("Reset password") assert response =~ "Reset password"
assert response =~ dgettext("errors", "should be at least 12 character(s)") assert response =~ "should be at least 12 character(s)"
assert response =~ dgettext("errors", "does not match password") assert response =~ "does not match password"
end end
test "does not reset password with invalid token", %{conn: conn} do test "does not reset password with invalid token", %{conn: conn} do
conn = put(conn, Routes.user_reset_password_path(conn, :update, "oops")) conn = put(conn, Routes.user_reset_password_path(conn, :update, "oops"))
assert redirected_to(conn) == "/" assert redirected_to(conn) == "/"
assert get_flash(conn, :error) =~ "Reset password link is invalid or it has expired"
assert get_flash(conn, :error) =~
dgettext("errors", "Reset password link is invalid or it has expired")
end end
end end
end end

View File

@ -4,7 +4,6 @@ defmodule CanneryWeb.UserSessionControllerTest do
""" """
use CanneryWeb.ConnCase, async: true use CanneryWeb.ConnCase, async: true
import CanneryWeb.Gettext
@moduletag :user_session_controller_test @moduletag :user_session_controller_test
@ -16,7 +15,7 @@ defmodule CanneryWeb.UserSessionControllerTest do
test "renders log in page", %{conn: conn} do test "renders log in page", %{conn: conn} do
conn = get(conn, Routes.user_session_path(conn, :new)) conn = get(conn, Routes.user_session_path(conn, :new))
response = html_response(conn, 200) response = html_response(conn, 200)
assert response =~ dgettext("actions", "Log in") assert response =~ "Log in"
end end
test "redirects if already logged in", %{conn: conn, current_user: current_user} do 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 test "logs the user in", %{conn: conn, current_user: current_user} do
conn = conn =
post(conn, Routes.user_session_path(conn, :create), %{ 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) assert get_session(conn, :user_token)
@ -39,16 +38,16 @@ defmodule CanneryWeb.UserSessionControllerTest do
conn = get(conn, "/") conn = get(conn, "/")
response = html_response(conn, 200) response = html_response(conn, 200)
assert response =~ current_user.email 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 end
test "logs the user in with remember me", %{conn: conn, current_user: current_user} do test "logs the user in with remember me", %{conn: conn, current_user: current_user} do
conn = conn =
post(conn, Routes.user_session_path(conn, :create), %{ post(conn, Routes.user_session_path(conn, :create), %{
"user" => %{ user: %{
"email" => current_user.email, email: current_user.email,
"password" => valid_user_password(), password: valid_user_password(),
"remember_me" => "true" remember_me: "true"
} }
}) })
@ -61,9 +60,9 @@ defmodule CanneryWeb.UserSessionControllerTest do
conn conn
|> init_test_session(user_return_to: "/foo/bar") |> init_test_session(user_return_to: "/foo/bar")
|> post(Routes.user_session_path(conn, :create), %{ |> post(Routes.user_session_path(conn, :create), %{
"user" => %{ user: %{
"email" => current_user.email, email: current_user.email,
"password" => valid_user_password() password: valid_user_password()
} }
}) })
@ -74,12 +73,12 @@ defmodule CanneryWeb.UserSessionControllerTest do
%{conn: conn, current_user: current_user} do %{conn: conn, current_user: current_user} do
conn = conn =
post(conn, Routes.user_session_path(conn, :create), %{ 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) response = html_response(conn, 200)
assert response =~ dgettext("actions", "Log in") assert response =~ "Log in"
assert response =~ dgettext("errors", "Invalid email or password") assert response =~ "Invalid email or password"
end end
end end
@ -88,14 +87,14 @@ defmodule CanneryWeb.UserSessionControllerTest do
conn = conn |> log_in_user(current_user) |> delete(Routes.user_session_path(conn, :delete)) conn = conn |> log_in_user(current_user) |> delete(Routes.user_session_path(conn, :delete))
assert redirected_to(conn) == "/" assert redirected_to(conn) == "/"
refute get_session(conn, :user_token) 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
test "succeeds even if the user is not logged in", %{conn: conn} do test "succeeds even if the user is not logged in", %{conn: conn} do
conn = delete(conn, Routes.user_session_path(conn, :delete)) conn = delete(conn, Routes.user_session_path(conn, :delete))
assert redirected_to(conn) == "/" assert redirected_to(conn) == "/"
refute get_session(conn, :user_token) 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 end
end end

View File

@ -4,7 +4,6 @@ defmodule CanneryWeb.UserSettingsControllerTest do
""" """
use CanneryWeb.ConnCase, async: true use CanneryWeb.ConnCase, async: true
import CanneryWeb.Gettext
alias Cannery.Accounts alias Cannery.Accounts
@moduletag :user_settings_controller_test @moduletag :user_settings_controller_test
@ -15,7 +14,7 @@ defmodule CanneryWeb.UserSettingsControllerTest do
test "renders settings page", %{conn: conn} do test "renders settings page", %{conn: conn} do
conn = get(conn, Routes.user_settings_path(conn, :edit)) conn = get(conn, Routes.user_settings_path(conn, :edit))
response = html_response(conn, 200) response = html_response(conn, 200)
assert response =~ gettext("Settings") assert response =~ "Settings"
end end
test "redirects if user is not logged in" do test "redirects if user is not logged in" do
@ -30,40 +29,36 @@ defmodule CanneryWeb.UserSettingsControllerTest do
%{conn: conn, current_user: current_user} do %{conn: conn, current_user: current_user} do
new_password_conn = new_password_conn =
put(conn, Routes.user_settings_path(conn, :update), %{ put(conn, Routes.user_settings_path(conn, :update), %{
"action" => "update_password", action: "update_password",
"current_password" => valid_user_password(), current_password: valid_user_password(),
"user" => %{ user: %{
"password" => "new valid password", password: "new valid password",
"password_confirmation" => "new valid password" password_confirmation: "new valid password"
} }
}) })
assert redirected_to(new_password_conn) == Routes.user_settings_path(conn, :edit) 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_session(new_password_conn, :user_token) != get_session(conn, :user_token)
assert get_flash(new_password_conn, :info) =~ "Password updated successfully"
assert get_flash(new_password_conn, :info) =~
dgettext("actions", "Password updated successfully")
assert Accounts.get_user_by_email_and_password(current_user.email, "new valid password") assert Accounts.get_user_by_email_and_password(current_user.email, "new valid password")
end end
test "does not update password on invalid data", %{conn: conn} do test "does not update password on invalid data", %{conn: conn} do
old_password_conn = old_password_conn =
put(conn, Routes.user_settings_path(conn, :update), %{ put(conn, Routes.user_settings_path(conn, :update), %{
"action" => "update_password", action: "update_password",
"current_password" => "invalid", current_password: "invalid",
"user" => %{ user: %{
"password" => "too short", password: "too short",
"password_confirmation" => "does not match" password_confirmation: "does not match"
} }
}) })
response = html_response(old_password_conn, 200) response = html_response(old_password_conn, 200)
assert response =~ gettext("Settings") assert response =~ "Settings"
assert response =~ dgettext("errors", "should be at least 12 character(s)") assert response =~ "should be at least 12 character(s)"
assert response =~ dgettext("errors", "does not match password") assert response =~ "does not match password"
assert response =~ dgettext("errors", "is not valid") assert response =~ "is not valid"
assert get_session(old_password_conn, :user_token) == get_session(conn, :user_token) assert get_session(old_password_conn, :user_token) == get_session(conn, :user_token)
end end
end end
@ -73,18 +68,15 @@ defmodule CanneryWeb.UserSettingsControllerTest do
test "updates the user email", %{conn: conn, current_user: current_user} do test "updates the user email", %{conn: conn, current_user: current_user} do
conn = conn =
put(conn, Routes.user_settings_path(conn, :update), %{ put(conn, Routes.user_settings_path(conn, :update), %{
"action" => "update_email", action: "update_email",
"current_password" => valid_user_password(), current_password: valid_user_password(),
"user" => %{"email" => unique_user_email()} user: %{email: unique_user_email()}
}) })
assert redirected_to(conn) == Routes.user_settings_path(conn, :edit) assert redirected_to(conn) == Routes.user_settings_path(conn, :edit)
assert get_flash(conn, :info) =~ assert get_flash(conn, :info) =~
dgettext( "A link to confirm your email change has been sent to the new address."
"prompts",
"A link to confirm your email change has been sent to the new address."
)
assert Accounts.get_user_by_email(current_user.email) assert Accounts.get_user_by_email(current_user.email)
end end
@ -92,15 +84,15 @@ defmodule CanneryWeb.UserSettingsControllerTest do
test "does not update email on invalid data", %{conn: conn} do test "does not update email on invalid data", %{conn: conn} do
conn = conn =
put(conn, Routes.user_settings_path(conn, :update), %{ put(conn, Routes.user_settings_path(conn, :update), %{
"action" => "update_email", action: "update_email",
"current_password" => "invalid", current_password: "invalid",
"user" => %{"email" => "with spaces"} user: %{email: "with spaces"}
}) })
response = html_response(conn, 200) response = html_response(conn, 200)
assert response =~ gettext("Settings") assert response =~ "Settings"
assert response =~ dgettext("errors", "must have the @ sign and no spaces") assert response =~ "must have the @ sign and no spaces"
assert response =~ dgettext("errors", "is not valid") assert response =~ "is not valid"
end end
end end
@ -124,24 +116,19 @@ defmodule CanneryWeb.UserSettingsControllerTest do
%{conn: conn, current_user: current_user, token: token, email: email} do %{conn: conn, current_user: current_user, token: token, email: email} do
conn = get(conn, Routes.user_settings_path(conn, :confirm_email, token)) conn = get(conn, Routes.user_settings_path(conn, :confirm_email, token))
assert redirected_to(conn) == Routes.user_settings_path(conn, :edit) 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) refute Accounts.get_user_by_email(current_user.email)
assert Accounts.get_user_by_email(email) assert Accounts.get_user_by_email(email)
conn = get(conn, Routes.user_settings_path(conn, :confirm_email, token)) conn = get(conn, Routes.user_settings_path(conn, :confirm_email, token))
assert redirected_to(conn) == Routes.user_settings_path(conn, :edit) assert redirected_to(conn) == Routes.user_settings_path(conn, :edit)
assert get_flash(conn, :error) =~ "Email change link is invalid or it has expired"
assert get_flash(conn, :error) =~
dgettext("errors", "Email change link is invalid or it has expired")
end end
test "does not update email with invalid token", %{conn: conn, current_user: current_user} do 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")) conn = get(conn, Routes.user_settings_path(conn, :confirm_email, "oops"))
assert redirected_to(conn) == Routes.user_settings_path(conn, :edit) assert redirected_to(conn) == Routes.user_settings_path(conn, :edit)
assert get_flash(conn, :error) =~ "Email change link is invalid or it has expired"
assert get_flash(conn, :error) =~
dgettext("errors", "Email change link is invalid or it has expired")
assert Accounts.get_user_by_email(current_user.email) assert Accounts.get_user_by_email(current_user.email)
end end

View File

@ -5,43 +5,40 @@ defmodule CanneryWeb.AmmoGroupLiveTest do
use CanneryWeb.ConnCase use CanneryWeb.ConnCase
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
import CanneryWeb.Gettext
alias Cannery.{Ammo, Repo} alias Cannery.{Ammo, Repo}
@moduletag :ammo_group_live_test @moduletag :ammo_group_live_test
@shot_group_create_attrs %{"ammo_left" => 5, "notes" => "some notes"} @create_attrs %{count: 42, notes: "some notes", price_paid: 120.5}
@shot_group_update_attrs %{ @update_attrs %{count: 43, notes: "some updated notes", price_paid: 456.7}
"count" => 5, @invalid_attrs %{count: nil, notes: nil, price_paid: nil}
"date" => ~N[2022-02-13 03:17:00],
"notes" => "some updated notes"
}
@create_attrs %{"count" => 42, "notes" => "some notes", "price_paid" => 120.5}
@update_attrs %{"count" => 43, "notes" => "some updated notes", "price_paid" => 456.7}
@ammo_group_create_limit 10_000 @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 %{ @empty_attrs %{
"price_paid" => 50, price_paid: 50,
"count" => 20 count: 20
} }
@shot_group_attrs %{ @shot_group_attrs %{
"price_paid" => 50, price_paid: 50,
"count" => 20 count: 20
} }
# @invalid_attrs %{count: -1, notes: nil, price_paid: nil}
defp create_ammo_group(%{current_user: current_user}) do defp create_ammo_group(%{current_user: current_user}) do
ammo_type = ammo_type_fixture(current_user) ammo_type = ammo_type_fixture(current_user)
container = container_fixture(current_user) container = container_fixture(current_user)
{1, [ammo_group]} = ammo_group_fixture(@create_attrs, ammo_type, container, 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 end
defp create_shot_group(%{current_user: current_user, ammo_group: ammo_group}) do 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) shot_group = shot_group_fixture(@shot_group_update_attrs, current_user, ammo_group)
ammo_group = ammo_group |> Repo.reload!() ammo_group = ammo_group |> Repo.reload!()
[ammo_group: ammo_group, shot_group: shot_group]
%{ammo_group: ammo_group, shot_group: shot_group}
end end
defp create_empty_ammo_group(%{ 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) {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) shot_group = shot_group_fixture(@shot_group_attrs, current_user, ammo_group)
ammo_group = ammo_group |> Repo.reload!() 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 end
describe "Index of ammo group" do 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 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)) {:ok, _index_live, html} = live(conn, Routes.ammo_group_index_path(conn, :index))
ammo_group = ammo_group |> Repo.preload(:ammo_type) ammo_group = ammo_group |> Repo.preload(:ammo_type)
assert html =~ gettext("Ammo") assert html =~ "Ammo"
assert html =~ ammo_group.ammo_type.name assert html =~ ammo_group.ammo_type.name
end 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 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)) {: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 html =~ ammo_group.ammo_type.name
assert index_live assert index_live
|> form(~s/form[phx-change="search"]/, |> form(~s/form[phx-change="search"]/)
search: %{search_term: ammo_group.ammo_type.name} |> render_change(search: %{search_term: ammo_group.ammo_type.name}) =~
) ammo_group.ammo_type.name
|> render_change() =~ ammo_group.ammo_type.name
assert_patch( assert_patch(
index_live, index_live,
@ -85,14 +134,15 @@ defmodule CanneryWeb.AmmoGroupLiveTest do
) )
refute index_live refute index_live
|> form(~s/form[phx-change="search"]/, search: %{search_term: "something_else"}) |> form(~s/form[phx-change="search"]/)
|> render_change() =~ ammo_group.ammo_type.name |> 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_patch(index_live, Routes.ammo_group_index_path(conn, :search, "something_else"))
assert index_live assert index_live
|> form(~s/form[phx-change="search"]/, search: %{search_term: ""}) |> form(~s/form[phx-change="search"]/)
|> render_change() =~ ammo_group.ammo_type.name |> render_change(search: %{search_term: ""}) =~ ammo_group.ammo_type.name
assert_patch(index_live, Routes.ammo_group_index_path(conn, :index)) assert_patch(index_live, Routes.ammo_group_index_path(conn, :index))
end end
@ -100,85 +150,65 @@ defmodule CanneryWeb.AmmoGroupLiveTest do
test "saves a single new ammo_group", %{conn: conn} do test "saves a single new ammo_group", %{conn: conn} do
{:ok, index_live, _html} = live(conn, Routes.ammo_group_index_path(conn, :index)) {:ok, index_live, _html} = live(conn, Routes.ammo_group_index_path(conn, :index))
assert index_live |> element("a", dgettext("actions", "Add Ammo")) |> render_click() =~ assert index_live |> element("a", "Add Ammo") |> render_click() =~ "Add Ammo"
dgettext("actions", "Add Ammo")
assert_patch(index_live, Routes.ammo_group_index_path(conn, :new)) assert_patch(index_live, Routes.ammo_group_index_path(conn, :new))
# assert index_live assert index_live
# |> form("#ammo_group-form", ammo_group: @invalid_attrs) |> form("#ammo_group-form")
# |> render_change() =~ dgettext("errors", "can't be blank") |> render_change(ammo_group: @invalid_attrs) =~ "can&#39;t be blank"
{:ok, _view, html} = {:ok, _view, html} =
index_live index_live
|> form("#ammo_group-form", ammo_group: @create_attrs) |> form("#ammo_group-form")
|> render_submit() |> render_submit(ammo_group: @create_attrs)
|> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index)) |> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index))
assert html =~ dgettext("prompts", "Ammo added successfully") assert html =~ "Ammo added successfully"
assert html =~ "\n42\n" assert html =~ "\n42\n"
end end
test "saves multiple new ammo_groups", %{conn: conn, current_user: current_user} do test "saves multiple new ammo_groups", %{conn: conn, current_user: current_user} do
multiplier = 25 multiplier = 25
{:ok, index_live, _html} = live(conn, Routes.ammo_group_index_path(conn, :index)) {:ok, index_live, _html} = live(conn, Routes.ammo_group_index_path(conn, :index))
assert index_live |> element("a", dgettext("actions", "Add Ammo")) |> render_click() =~ assert index_live |> element("a", "Add Ammo") |> render_click() =~ "Add Ammo"
dgettext("actions", "Add Ammo")
assert_patch(index_live, Routes.ammo_group_index_path(conn, :new)) assert_patch(index_live, Routes.ammo_group_index_path(conn, :new))
# assert index_live assert index_live
# |> form("#ammo_group-form", ammo_group: @invalid_attrs) |> form("#ammo_group-form")
# |> render_change() =~ dgettext("errors", "can't be blank") |> render_change(ammo_group: @invalid_attrs) =~ "can&#39;t be blank"
{:ok, _view, html} = {:ok, _view, html} =
index_live index_live
|> form("#ammo_group-form", |> form("#ammo_group-form")
ammo_group: @create_attrs |> Map.put("multiplier", to_string(multiplier)) |> render_submit(ammo_group: @create_attrs |> Map.put(:multiplier, multiplier))
)
|> render_submit()
|> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index)) |> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index))
assert html =~ dgettext("prompts", "Ammo added successfully") assert html =~ "Ammo added successfully"
assert Ammo.list_ammo_groups(nil, false, current_user) |> Enum.count() == multiplier + 1 assert Ammo.list_ammo_groups(nil, :all, current_user) |> Enum.count() == multiplier + 1
end end
test "does not save invalid number of new ammo_groups", %{conn: conn} do 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)) {:ok, index_live, _html} = live(conn, Routes.ammo_group_index_path(conn, :index))
assert index_live |> element("a", dgettext("actions", "Add Ammo")) |> render_click() =~ assert index_live |> element("a", "Add Ammo") |> render_click() =~ "Add Ammo"
dgettext("actions", "Add Ammo")
assert_patch(index_live, Routes.ammo_group_index_path(conn, :new)) assert_patch(index_live, Routes.ammo_group_index_path(conn, :new))
# assert index_live assert index_live
# |> form("#ammo_group-form", ammo_group: @invalid_attrs) |> form("#ammo_group-form")
# |> render_change() =~ dgettext("errors", "can't be blank") |> render_change(ammo_group: @invalid_attrs) =~ "can&#39;t be blank"
assert index_live assert index_live
|> form("#ammo_group-form", ammo_group: @create_attrs |> Map.put("multiplier", "0")) |> form("#ammo_group-form")
|> render_submit() =~ |> render_submit(ammo_group: @create_attrs |> Map.put(:multiplier, "0")) =~
dgettext( "Invalid number of copies, must be between 1 and #{@ammo_group_create_limit}. Was 0"
"errors",
"Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}",
multiplier: 0,
max: @ammo_group_create_limit
)
assert index_live assert index_live
|> form("#ammo_group-form", |> form("#ammo_group-form")
ammo_group: |> render_submit(
@create_attrs |> Map.put("multiplier", to_string(@ammo_group_create_limit + 1)) ammo_group: @create_attrs |> Map.put(:multiplier, @ammo_group_create_limit + 1)
) ) =~
|> render_submit() =~ "Invalid number of copies, must be between 1 and #{@ammo_group_create_limit}. Was #{@ammo_group_create_limit + 1}"
dgettext(
"errors",
"Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}",
multiplier: @ammo_group_create_limit + 1,
max: @ammo_group_create_limit
)
end end
test "updates ammo_group in listing", %{conn: conn, ammo_group: ammo_group} do test "updates ammo_group in listing", %{conn: conn, ammo_group: ammo_group} do
@ -186,22 +216,21 @@ defmodule CanneryWeb.AmmoGroupLiveTest do
assert index_live assert index_live
|> element(~s/a[aria-label="Edit ammo group of #{ammo_group.count} bullets"]/) |> element(~s/a[aria-label="Edit ammo group of #{ammo_group.count} bullets"]/)
|> render_click() =~ |> render_click() =~ "Edit ammo"
gettext("Edit ammo")
assert_patch(index_live, Routes.ammo_group_index_path(conn, :edit, ammo_group)) assert_patch(index_live, Routes.ammo_group_index_path(conn, :edit, ammo_group))
# assert index_live assert index_live
# |> form("#ammo_group-form", ammo_group: @invalid_attrs) |> form("#ammo_group-form")
# |> render_change() =~ dgettext("errors", "can't be blank") |> render_change(ammo_group: @invalid_attrs) =~ "can&#39;t be blank"
{:ok, _view, html} = {:ok, _view, html} =
index_live index_live
|> form("#ammo_group-form", ammo_group: @update_attrs) |> form("#ammo_group-form")
|> render_submit() |> render_submit(ammo_group: @update_attrs)
|> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index)) |> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index))
assert html =~ dgettext("prompts", "Ammo updated successfully") assert html =~ "Ammo updated successfully"
assert html =~ "\n43\n" assert html =~ "\n43\n"
end end
@ -213,24 +242,38 @@ defmodule CanneryWeb.AmmoGroupLiveTest do
|> element(~s/a[aria-label="Clone ammo group of #{ammo_group.count} bullets"]/) |> element(~s/a[aria-label="Clone ammo group of #{ammo_group.count} bullets"]/)
|> render_click() |> render_click()
assert html =~ dgettext("actions", "Add Ammo") assert html =~ "Add Ammo"
assert html =~ gettext("$%{amount}", amount: display_currency(120.5)) assert html =~ "$#{display_currency(120.5)}"
assert_patch(index_live, Routes.ammo_group_index_path(conn, :clone, ammo_group)) assert_patch(index_live, Routes.ammo_group_index_path(conn, :clone, ammo_group))
# assert index_live {:ok, _index_live, html} =
# |> form("#ammo_group-form", ammo_group: @invalid_attrs)
# |> render_change() =~ dgettext("errors", "can't be blank")
{:ok, _view, html} =
index_live index_live
|> form("#ammo_group-form") |> form("#ammo_group-form")
|> render_submit() |> render_submit()
|> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index)) |> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index))
assert html =~ dgettext("prompts", "Ammo added successfully") assert html =~ "Ammo added successfully"
assert html =~ "\n42\n" assert html =~ "\n42\n"
assert html =~ gettext("$%{amount}", amount: display_currency(120.5)) 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 end
test "clones ammo_group in listing with updates", %{conn: conn, ammo_group: ammo_group} do 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"]/) |> element(~s/a[aria-label="Clone ammo group of #{ammo_group.count} bullets"]/)
|> render_click() |> render_click()
assert html =~ dgettext("actions", "Add Ammo") assert html =~ "Add Ammo"
assert html =~ gettext("$%{amount}", amount: display_currency(120.5)) assert html =~ "$#{display_currency(120.5)}"
assert_patch(index_live, Routes.ammo_group_index_path(conn, :clone, ammo_group)) assert_patch(index_live, Routes.ammo_group_index_path(conn, :clone, ammo_group))
# assert index_live assert index_live
# |> form("#ammo_group-form", ammo_group: @invalid_attrs) |> form("#ammo_group-form")
# |> render_change() =~ dgettext("errors", "can't be blank") |> render_change(ammo_group: @invalid_attrs) =~ "can&#39;t be blank"
{:ok, _view, html} = {:ok, _view, html} =
index_live index_live
|> form("#ammo_group-form", ammo_group: Map.merge(@create_attrs, %{"count" => 43})) |> form("#ammo_group-form")
|> render_submit() |> render_submit(ammo_group: @create_attrs |> Map.put(:count, 43))
|> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index)) |> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index))
assert html =~ dgettext("prompts", "Ammo added successfully") assert html =~ "Ammo added successfully"
assert html =~ "\n43\n" assert html =~ "\n43\n"
assert html =~ gettext("$%{amount}", amount: display_currency(120.5)) assert html =~ "$#{display_currency(120.5)}"
end end
test "deletes ammo_group in listing", %{conn: conn, ammo_group: ammo_group} do 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 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)) {:ok, index_live, _html} = live(conn, Routes.ammo_group_index_path(conn, :index))
assert index_live |> element("a", dgettext("actions", "Record shots")) |> render_click() =~ assert index_live |> element("a", "Record shots") |> render_click() =~ "Record shots"
gettext("Record shots")
assert_patch(index_live, Routes.ammo_group_index_path(conn, :add_shot_group, ammo_group)) assert_patch(index_live, Routes.ammo_group_index_path(conn, :add_shot_group, ammo_group))
# assert index_live assert index_live
# |> form("#shot_group-form", shot_group: @invalid_attrs) |> form("#shot-group-form")
# |> render_change() =~ dgettext("errors", "is invalid") |> render_change(shot_group: @shot_group_invalid_attrs) =~ "can&#39;t be blank"
{:ok, _view, html} = {:ok, _view, html} =
index_live index_live
|> form("#shot-group-form", shot_group: @shot_group_create_attrs) |> form("#shot-group-form")
|> render_submit() |> render_submit(shot_group: @shot_group_create_attrs)
|> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index)) |> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index))
assert html =~ dgettext("prompts", "Shots recorded successfully") assert html =~ "Shots recorded successfully"
end end
@spec display_currency(float()) :: String.t() @spec display_currency(float()) :: String.t()
@ -306,21 +346,20 @@ defmodule CanneryWeb.AmmoGroupLiveTest do
} do } do
{:ok, show_live, html} = live(conn, Routes.ammo_group_index_path(conn, :index)) {:ok, show_live, html} = live(conn, Routes.ammo_group_index_path(conn, :index))
assert html =~ dgettext("actions", "Show used") assert html =~ "Show used"
refute html =~ gettext("$%{amount}", amount: display_currency(50.00)) refute html =~ "$#{display_currency(50.00)}"
percentage = ammo_group |> Ammo.get_percentage_remaining(current_user) percentage = ammo_group |> Ammo.get_percentage_remaining(current_user)
refute html =~ "\n#{gettext("%{percentage}%", percentage: percentage)}\n" refute html =~ "\n#{"#{percentage}%"}\n"
html = html =
show_live show_live
|> element(~s/input[type="checkbox"][aria-labelledby="toggle_show_used-label"}]/) |> element(~s/input[type="checkbox"][aria-labelledby="toggle_show_used-label"}]/)
|> render_click() |> render_click()
assert html =~ gettext("$%{amount}", amount: display_currency(50.00)) assert html =~ "$#{display_currency(50.00)}"
percentage = ammo_group |> Ammo.get_percentage_remaining(current_user) percentage = ammo_group |> Ammo.get_percentage_remaining(current_user)
assert html =~ "\n#{gettext("%{percentage}%", percentage: percentage)}\n" assert html =~ "\n#{"#{percentage}%"}\n"
end end
end end
@ -329,9 +368,8 @@ defmodule CanneryWeb.AmmoGroupLiveTest do
test "displays ammo_group", %{conn: conn, ammo_group: ammo_group} 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)) {:ok, _show_live, html} = live(conn, Routes.ammo_group_show_path(conn, :show, ammo_group))
ammo_group = ammo_group |> Repo.preload(:ammo_type) ammo_group = ammo_group |> Repo.preload(:ammo_type)
assert html =~ gettext("Show Ammo") assert html =~ "Show Ammo"
assert html =~ ammo_group.ammo_type.name assert html =~ ammo_group.ammo_type.name
end end
@ -340,44 +378,41 @@ defmodule CanneryWeb.AmmoGroupLiveTest do
assert show_live assert show_live
|> element(~s/a[aria-label="Edit ammo group of #{ammo_group.count} bullets"]/) |> element(~s/a[aria-label="Edit ammo group of #{ammo_group.count} bullets"]/)
|> render_click() =~ |> render_click() =~ "Edit Ammo"
gettext("Edit Ammo")
assert_patch(show_live, Routes.ammo_group_show_path(conn, :edit, ammo_group)) assert_patch(show_live, Routes.ammo_group_show_path(conn, :edit, ammo_group))
# assert show_live assert show_live
# |> form("#ammo_group-form", ammo_group: @invalid_attrs) |> form("#ammo_group-form")
# |> render_change() =~ dgettext("errors", "can't be blank") |> render_change(ammo_group: @invalid_attrs) =~ "can&#39;t be blank"
{:ok, _view, html} = {:ok, _view, html} =
show_live show_live
|> form("#ammo_group-form", ammo_group: @update_attrs) |> form("#ammo_group-form")
|> render_submit() |> render_submit(ammo_group: @update_attrs)
|> follow_redirect(conn, Routes.ammo_group_show_path(conn, :show, ammo_group)) |> 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" assert html =~ "some updated notes"
end end
test "saves new shot_group", %{conn: conn, ammo_group: ammo_group} do 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)) {: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() =~ assert index_live |> element("a", "Record shots") |> render_click() =~ "Record shots"
gettext("Record shots")
assert_patch(index_live, Routes.ammo_group_show_path(conn, :add_shot_group, ammo_group)) assert_patch(index_live, Routes.ammo_group_show_path(conn, :add_shot_group, ammo_group))
# assert index_live assert index_live
# |> form("#shot_group-form", shot_group: @invalid_attrs) |> form("#shot-group-form")
# |> render_change() =~ dgettext("errors", "is invalid") |> render_change(shot_group: @shot_group_invalid_attrs) =~ "can&#39;t be blank"
{:ok, _view, html} = {:ok, _view, html} =
index_live index_live
|> form("#shot-group-form", shot_group: @shot_group_create_attrs) |> form("#shot-group-form")
|> render_submit() |> render_submit(shot_group: @shot_group_create_attrs)
|> follow_redirect(conn, Routes.ammo_group_show_path(conn, :show, ammo_group)) |> 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
end end
@ -390,25 +425,24 @@ defmodule CanneryWeb.AmmoGroupLiveTest do
assert index_live assert index_live
|> element(~s/a[aria-label="Edit shot group of #{shot_group.count} shots"]/) |> element(~s/a[aria-label="Edit shot group of #{shot_group.count} shots"]/)
|> render_click() =~ |> render_click() =~ "Edit Shot Records"
gettext("Edit Shot Records")
assert_patch( assert_patch(
index_live, index_live,
Routes.ammo_group_show_path(conn, :edit_shot_group, ammo_group, shot_group) Routes.ammo_group_show_path(conn, :edit_shot_group, ammo_group, shot_group)
) )
# assert index_live assert index_live
# |> form("#shot_group-form", shot_group: @invalid_attrs) |> form("#shot-group-form")
# |> render_change() =~ dgettext("errors", "is invalid") |> render_change(shot_group: @shot_group_invalid_attrs) =~ "can&#39;t be blank"
{:ok, _view, html} = {:ok, _view, html} =
index_live index_live
|> form("#shot-group-form", shot_group: @shot_group_update_attrs) |> form("#shot-group-form")
|> render_submit() |> render_submit(shot_group: @shot_group_update_attrs)
|> follow_redirect(conn, Routes.ammo_group_show_path(conn, :show, ammo_group)) |> 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" assert html =~ "some updated notes"
end end

View File

@ -5,54 +5,51 @@ defmodule CanneryWeb.AmmoTypeLiveTest do
use CanneryWeb.ConnCase use CanneryWeb.ConnCase
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
import CanneryWeb.Gettext
alias Cannery.{Ammo, Repo} alias Cannery.{Ammo, Repo}
@moduletag :ammo_type_live_test @moduletag :ammo_type_live_test
@create_attrs %{ @create_attrs %{
"bullet_type" => "some bullet_type", bullet_type: "some bullet_type",
"case_material" => "some case_material", case_material: "some case_material",
"desc" => "some desc", desc: "some desc",
"manufacturer" => "some manufacturer", manufacturer: "some manufacturer",
"name" => "some name", name: "some name",
"grains" => 120 grains: 120
} }
@update_attrs %{ @update_attrs %{
"bullet_type" => "some updated bullet_type", bullet_type: "some updated bullet_type",
"case_material" => "some updated case_material", case_material: "some updated case_material",
"desc" => "some updated desc", desc: "some updated desc",
"manufacturer" => "some updated manufacturer", manufacturer: "some updated manufacturer",
"name" => "some updated name", name: "some updated name",
"grains" => 456 grains: 456
}
@invalid_attrs %{
bullet_type: nil,
case_material: nil,
desc: nil,
manufacturer: nil,
name: nil,
grains: nil
} }
@ammo_group_attrs %{ @ammo_group_attrs %{
"notes" => "some ammo group", notes: "some ammo group",
"count" => 20 count: 20
} }
@shot_group_attrs %{ @shot_group_attrs %{
"notes" => "some shot group", notes: "some shot group",
"count" => 20 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 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 end
defp create_ammo_group(%{ammo_type: ammo_type, current_user: current_user}) do defp create_ammo_group(%{ammo_type: ammo_type, current_user: current_user}) do
container = container_fixture(current_user) container = container_fixture(current_user)
{1, [ammo_group]} = ammo_group_fixture(@ammo_group_attrs, ammo_type, container, 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 end
defp create_empty_ammo_group(%{ammo_type: ammo_type, current_user: current_user}) do 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) {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) shot_group = shot_group_fixture(@shot_group_attrs, current_user, ammo_group)
ammo_group = ammo_group |> Repo.reload!() 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 end
describe "Index" do describe "Index" do
@ -69,33 +65,81 @@ defmodule CanneryWeb.AmmoTypeLiveTest do
test "lists all ammo_types", %{conn: conn, ammo_type: ammo_type} 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)) {:ok, _index_live, html} = live(conn, Routes.ammo_type_index_path(conn, :index))
assert html =~ "Catalog"
assert html =~ gettext("Catalog")
assert html =~ ammo_type.bullet_type assert html =~ ammo_type.bullet_type
end 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 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)) {:ok, index_live, html} = live(conn, Routes.ammo_type_index_path(conn, :index))
assert html =~ ammo_type.bullet_type assert html =~ ammo_type.bullet_type
assert index_live assert index_live
|> form(~s/form[phx-change="search"]/, |> form(~s/form[phx-change="search"]/)
search: %{search_term: ammo_type.bullet_type} |> render_change(search: %{search_term: ammo_type.bullet_type}) =~
) ammo_type.bullet_type
|> render_change() =~ ammo_type.bullet_type
assert_patch(index_live, Routes.ammo_type_index_path(conn, :search, ammo_type.bullet_type)) assert_patch(index_live, Routes.ammo_type_index_path(conn, :search, ammo_type.bullet_type))
refute index_live refute index_live
|> form(~s/form[phx-change="search"]/, search: %{search_term: "something_else"}) |> form(~s/form[phx-change="search"]/)
|> render_change() =~ ammo_type.bullet_type |> 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_patch(index_live, Routes.ammo_type_index_path(conn, :search, "something_else"))
assert index_live assert index_live
|> form(~s/form[phx-change="search"]/, search: %{search_term: ""}) |> form(~s/form[phx-change="search"]/)
|> render_change() =~ ammo_type.bullet_type |> render_change(search: %{search_term: ""}) =~ ammo_type.bullet_type
assert_patch(index_live, Routes.ammo_type_index_path(conn, :index)) assert_patch(index_live, Routes.ammo_type_index_path(conn, :index))
end 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 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)) {: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() =~ assert index_live |> element("a", "New Ammo type") |> render_click() =~ "New Ammo type"
gettext("New Ammo type")
assert_patch(index_live, Routes.ammo_type_index_path(conn, :new)) assert_patch(index_live, Routes.ammo_type_index_path(conn, :new))
# assert index_live assert index_live
# |> form("#ammo_type-form", ammo_type: @invalid_attrs) |> form("#ammo_type-form")
# |> render_change() =~ dgettext("errors", "can't be blank") |> render_change(ammo_type: @invalid_attrs) =~ "can&#39;t be blank"
{:ok, _view, html} = {:ok, _view, html} =
index_live index_live
|> form("#ammo_type-form", ammo_type: @create_attrs) |> form("#ammo_type-form")
|> render_submit() |> render_submit(ammo_type: @create_attrs)
|> follow_redirect(conn, Routes.ammo_type_index_path(conn, :index)) |> follow_redirect(conn, Routes.ammo_type_index_path(conn, :index))
ammo_type = ammo_type.id |> Ammo.get_ammo_type!(current_user) 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" assert html =~ "some bullet_type"
end end
@ -128,22 +170,22 @@ defmodule CanneryWeb.AmmoTypeLiveTest do
{:ok, index_live, _html} = live(conn, Routes.ammo_type_index_path(conn, :index)) {: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() =~ 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_patch(index_live, Routes.ammo_type_index_path(conn, :edit, ammo_type))
# assert index_live assert index_live
# |> form("#ammo_type-form", ammo_type: @invalid_attrs) |> form("#ammo_type-form")
# |> render_change() =~ dgettext("errors", "can't be blank") |> render_change(ammo_type: @invalid_attrs) =~ "can&#39;t be blank"
{:ok, _view, html} = {:ok, _view, html} =
index_live index_live
|> form("#ammo_type-form", ammo_type: @update_attrs) |> form("#ammo_type-form")
|> render_submit() |> render_submit(ammo_type: @update_attrs)
|> follow_redirect(conn, Routes.ammo_type_index_path(conn, :index)) |> follow_redirect(conn, Routes.ammo_type_index_path(conn, :index))
ammo_type = ammo_type.id |> Ammo.get_ammo_type!(current_user) 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" assert html =~ "some updated bullet_type"
end end
@ -152,23 +194,23 @@ defmodule CanneryWeb.AmmoTypeLiveTest do
{:ok, index_live, _html} = live(conn, Routes.ammo_type_index_path(conn, :index)) {: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() 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 html =~ "some bullet_type"
assert_patch(index_live, Routes.ammo_type_index_path(conn, :clone, ammo_type)) assert_patch(index_live, Routes.ammo_type_index_path(conn, :clone, ammo_type))
# assert index_live assert index_live
# |> form("#ammo_type-form", ammo_type: @invalid_attrs) |> form("#ammo_type-form")
# |> render_change() =~ dgettext("errors", "can't be blank") |> render_change(ammo_type: @invalid_attrs) =~ "can&#39;t be blank"
{:ok, _view, html} = {:ok, _view, html} =
index_live index_live
|> form("#ammo_type-form", ammo_type: @create_attrs) |> form("#ammo_type-form")
|> render_submit() |> render_submit(ammo_type: @create_attrs)
|> follow_redirect(conn, Routes.ammo_type_index_path(conn, :index)) |> follow_redirect(conn, Routes.ammo_type_index_path(conn, :index))
ammo_type = ammo_type.id |> Ammo.get_ammo_type!(current_user) 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" assert html =~ "some bullet_type"
end end
@ -177,31 +219,30 @@ defmodule CanneryWeb.AmmoTypeLiveTest do
{:ok, index_live, _html} = live(conn, Routes.ammo_type_index_path(conn, :index)) {: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() 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 html =~ "some bullet_type"
assert_patch(index_live, Routes.ammo_type_index_path(conn, :clone, ammo_type)) assert_patch(index_live, Routes.ammo_type_index_path(conn, :clone, ammo_type))
# assert index_live assert index_live
# |> form("#ammo_type-form", ammo_type: @invalid_attrs) |> form("#ammo_type-form")
# |> render_change() =~ dgettext("errors", "can't be blank") |> render_change(ammo_type: @invalid_attrs) =~ "can&#39;t be blank"
{:ok, _view, html} = {:ok, _view, html} =
index_live index_live
|> form("#ammo_type-form", |> form("#ammo_type-form")
ammo_type: Map.merge(@create_attrs, %{"bullet_type" => "some updated bullet_type"}) |> 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)) |> follow_redirect(conn, Routes.ammo_type_index_path(conn, :index))
ammo_type = ammo_type.id |> Ammo.get_ammo_type!(current_user) 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" assert html =~ "some updated bullet_type"
end end
test "deletes ammo_type in listing", %{conn: conn, ammo_type: ammo_type} do 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)) {: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() assert index_live |> element(~s/a[aria-label="Delete #{ammo_type.name}"]/) |> render_click()
refute has_element?(index_live, "#ammo_type-#{ammo_type.id}") refute has_element?(index_live, "#ammo_type-#{ammo_type.id}")
end end
@ -214,27 +255,27 @@ defmodule CanneryWeb.AmmoTypeLiveTest do
%{conn: conn, ammo_group: ammo_group, current_user: current_user} 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)) {:ok, index_live, html} = live(conn, Routes.ammo_type_index_path(conn, :index))
assert html =~ dgettext("actions", "Show used") assert html =~ "Show used"
refute html =~ gettext("Used rounds") refute html =~ "Used rounds"
refute html =~ gettext("Total ever rounds") refute html =~ "Total ever rounds"
refute html =~ gettext("Used packs") refute html =~ "Used packs"
refute html =~ gettext("Total ever packs") refute html =~ "Total ever packs"
html = html =
index_live index_live
|> element(~s/input[type="checkbox"][aria-labelledby="toggle_show_used-label"}]/) |> element(~s/input[type="checkbox"][aria-labelledby="toggle_show_used-label"}]/)
|> render_click() |> render_click()
assert html =~ gettext("Used rounds") assert html =~ "Used rounds"
assert html =~ gettext("Total ever rounds") assert html =~ "Total ever rounds"
assert html =~ gettext("Used packs") assert html =~ "Used packs"
assert html =~ gettext("Total ever packs") assert html =~ "Total ever packs"
assert html =~ "\n20\n" assert html =~ "\n20\n"
assert html =~ "\n0\n" assert html =~ "\n0\n"
assert html =~ "\n1\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)) {:ok, index_live, _html} = live(conn, Routes.ammo_type_index_path(conn, :index))
@ -266,22 +307,22 @@ defmodule CanneryWeb.AmmoTypeLiveTest do
{:ok, show_live, _html} = live(conn, Routes.ammo_type_show_path(conn, :show, ammo_type)) {: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() =~ 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_patch(show_live, Routes.ammo_type_show_path(conn, :edit, ammo_type))
# assert show_live assert show_live
# |> form("#ammo_type-form", ammo_type: @invalid_attrs) |> form("#ammo_type-form")
# |> render_change() =~ dgettext("errors", "can't be blank") |> render_change(ammo_type: @invalid_attrs) =~ "can&#39;t be blank"
{:ok, _view, html} = {:ok, _view, html} =
show_live show_live
|> form("#ammo_type-form", ammo_type: @update_attrs) |> form("#ammo_type-form")
|> render_submit() |> render_submit(ammo_type: @update_attrs)
|> follow_redirect(conn, Routes.ammo_type_show_path(conn, :show, ammo_type)) |> follow_redirect(conn, Routes.ammo_type_show_path(conn, :show, ammo_type))
ammo_type = ammo_type.id |> Ammo.get_ammo_type!(current_user) 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" assert html =~ "some updated bullet_type"
end end
end end
@ -321,8 +362,7 @@ defmodule CanneryWeb.AmmoTypeLiveTest do
test "displays empty ammo groups on toggle", test "displays empty ammo groups on toggle",
%{conn: conn, ammo_type: ammo_type, container: %{name: container_name}} do %{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)) {:ok, show_live, html} = live(conn, Routes.ammo_type_show_path(conn, :show, ammo_type))
assert html =~ "Show used"
assert html =~ dgettext("actions", "Show used")
refute html =~ "\n20\n" refute html =~ "\n20\n"
html = html =
@ -344,7 +384,7 @@ defmodule CanneryWeb.AmmoTypeLiveTest do
|> element(~s/input[type="checkbox"][aria-labelledby="toggle_table-label"}]/) |> element(~s/input[type="checkbox"][aria-labelledby="toggle_table-label"}]/)
|> render_click() |> render_click()
assert html =~ dgettext("actions", "Show used") assert html =~ "Show used"
refute html =~ "\n20\n" refute html =~ "\n20\n"
html = html =

View File

@ -5,61 +5,46 @@ defmodule CanneryWeb.ContainerLiveTest do
use CanneryWeb.ConnCase use CanneryWeb.ConnCase
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
import CanneryWeb.Gettext alias Cannery.Containers
alias Cannery.{Containers, Repo}
@moduletag :container_live_test @moduletag :container_live_test
@create_attrs %{ @create_attrs %{
"desc" => "some desc", desc: "some desc",
"location" => "some location", location: "some location",
"name" => "some name", name: "some name",
"type" => "some type" type: "some type"
} }
@update_attrs %{ @update_attrs %{
"desc" => "some updated desc", desc: "some updated desc",
"location" => "some updated location", location: "some updated location",
"name" => "some updated name", name: "some updated name",
"type" => "some updated type" type: "some updated type"
} }
@invalid_attrs %{desc: nil, location: nil, name: nil, type: nil}
@ammo_type_attrs %{ @ammo_type_attrs %{
"bullet_type" => "some bullet_type", bullet_type: "some bullet_type",
"case_material" => "some case_material", case_material: "some case_material",
"desc" => "some desc", desc: "some desc",
"manufacturer" => "some manufacturer", manufacturer: "some manufacturer",
"name" => "some name", name: "some name",
"grains" => 120 grains: 120
} }
@ammo_group_attrs %{ @ammo_group_attrs %{
"notes" => "some ammo group", notes: "some ammo group",
"count" => 20 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 defp create_container(%{current_user: current_user}) do
container = container_fixture(@create_attrs, current_user) container = container_fixture(@create_attrs, current_user)
%{container: container} [container: container]
end end
defp create_ammo_group(%{container: container, current_user: current_user}) do defp create_ammo_group(%{container: container, current_user: current_user}) do
ammo_type = ammo_type_fixture(@ammo_type_attrs, current_user) ammo_type = ammo_type_fixture(@ammo_type_attrs, current_user)
{1, [ammo_group]} = ammo_group_fixture(@ammo_group_attrs, ammo_type, container, current_user) {1, [ammo_group]} = ammo_group_fixture(@ammo_group_attrs, ammo_type, container, current_user)
%{ammo_type: ammo_type, ammo_group: ammo_group} [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}
end end
describe "Index" do describe "Index" do
@ -68,7 +53,7 @@ defmodule CanneryWeb.ContainerLiveTest do
test "lists all containers", %{conn: conn, container: container} do test "lists all containers", %{conn: conn, container: container} do
{:ok, _index_live, html} = live(conn, Routes.container_index_path(conn, :index)) {:ok, _index_live, html} = live(conn, Routes.container_index_path(conn, :index))
assert html =~ gettext("Containers") assert html =~ "Containers"
assert html =~ container.location assert html =~ container.location
end end
@ -80,7 +65,7 @@ defmodule CanneryWeb.ContainerLiveTest do
|> element(~s/input[type="checkbox"][aria-labelledby="toggle_table-label"}]/) |> element(~s/input[type="checkbox"][aria-labelledby="toggle_table-label"}]/)
|> render_click() |> render_click()
assert html =~ gettext("Containers") assert html =~ "Containers"
assert html =~ container.location assert html =~ container.location
end end
@ -90,22 +75,20 @@ defmodule CanneryWeb.ContainerLiveTest do
assert html =~ container.location assert html =~ container.location
assert index_live assert index_live
|> form(~s/form[phx-change="search"]/, |> form(~s/form[phx-change="search"]/)
search: %{search_term: container.location} |> render_change(search: %{search_term: container.location}) =~ container.location
)
|> render_change() =~ container.location
assert_patch(index_live, Routes.container_index_path(conn, :search, container.location)) assert_patch(index_live, Routes.container_index_path(conn, :search, container.location))
refute index_live refute index_live
|> form(~s/form[phx-change="search"]/, search: %{search_term: "something_else"}) |> form(~s/form[phx-change="search"]/)
|> render_change() =~ container.location |> render_change(search: %{search_term: "something_else"}) =~ container.location
assert_patch(index_live, Routes.container_index_path(conn, :search, "something_else")) assert_patch(index_live, Routes.container_index_path(conn, :search, "something_else"))
assert index_live assert index_live
|> form(~s/form[phx-change="search"]/, search: %{search_term: ""}) |> form(~s/form[phx-change="search"]/)
|> render_change() =~ container.location |> render_change(search: %{search_term: ""}) =~ container.location
assert_patch(index_live, Routes.container_index_path(conn, :index)) assert_patch(index_live, Routes.container_index_path(conn, :index))
end end
@ -113,22 +96,20 @@ defmodule CanneryWeb.ContainerLiveTest do
test "saves new container", %{conn: conn, container: container} do test "saves new container", %{conn: conn, container: container} do
{:ok, index_live, _html} = live(conn, Routes.container_index_path(conn, :index)) {:ok, index_live, _html} = live(conn, Routes.container_index_path(conn, :index))
assert index_live |> element("a", dgettext("actions", "New Container")) |> render_click() =~ assert index_live |> element("a", "New Container") |> render_click() =~ "New Container"
gettext("New Container")
assert_patch(index_live, Routes.container_index_path(conn, :new)) assert_patch(index_live, Routes.container_index_path(conn, :new))
# assert index_live assert index_live
# |> form("#container-form", container: @invalid_attrs) |> form("#container-form")
# |> render_change() =~ dgettext("errors", "can't be blank") |> render_change(container: @invalid_attrs) =~ "can&#39;t be blank"
{:ok, _view, html} = {:ok, _view, html} =
index_live index_live
|> form("#container-form", container: @create_attrs) |> form("#container-form")
|> render_submit() |> render_submit(container: @create_attrs)
|> follow_redirect(conn, Routes.container_index_path(conn, :index)) |> 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" assert html =~ "some location"
end end
@ -140,22 +121,22 @@ defmodule CanneryWeb.ContainerLiveTest do
{:ok, index_live, _html} = live(conn, Routes.container_index_path(conn, :index)) {: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() =~ 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_patch(index_live, Routes.container_index_path(conn, :edit, container))
# assert index_live assert index_live
# |> form("#container-form", container: @invalid_attrs) |> form("#container-form")
# |> render_change() =~ dgettext("errors", "can't be blank") |> render_change(container: @invalid_attrs) =~ "can&#39;t be blank"
{:ok, _view, html} = {:ok, _view, html} =
index_live index_live
|> form("#container-form", container: @update_attrs) |> form("#container-form")
|> render_submit() |> render_submit(container: @update_attrs)
|> follow_redirect(conn, Routes.container_index_path(conn, :index)) |> follow_redirect(conn, Routes.container_index_path(conn, :index))
container = container.id |> Containers.get_container!(current_user) 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" assert html =~ "some updated location"
end end
@ -167,23 +148,23 @@ defmodule CanneryWeb.ContainerLiveTest do
{:ok, index_live, _html} = live(conn, Routes.container_index_path(conn, :index)) {: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() 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 html =~ "some location"
assert_patch(index_live, Routes.container_index_path(conn, :clone, container)) assert_patch(index_live, Routes.container_index_path(conn, :clone, container))
# assert index_live assert index_live
# |> form("#container-form", container: @invalid_attrs) |> form("#container-form")
# |> render_change() =~ dgettext("errors", "can't be blank") |> render_change(container: @invalid_attrs) =~ "can&#39;t be blank"
{:ok, _view, html} = {:ok, _view, html} =
index_live index_live
|> form("#container-form", container: @create_attrs) |> form("#container-form")
|> render_submit() |> render_submit(container: @create_attrs)
|> follow_redirect(conn, Routes.container_index_path(conn, :index)) |> follow_redirect(conn, Routes.container_index_path(conn, :index))
container = container.id |> Containers.get_container!(current_user) 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" assert html =~ "some location"
end end
@ -195,30 +176,29 @@ defmodule CanneryWeb.ContainerLiveTest do
{:ok, index_live, _html} = live(conn, Routes.container_index_path(conn, :index)) {: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() =~ 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_patch(index_live, Routes.container_index_path(conn, :clone, container))
# assert index_live assert index_live
# |> form("#container-form", container: @invalid_attrs) |> form("#container-form")
# |> render_change() =~ dgettext("errors", "can't be blank") |> render_change(container: @invalid_attrs) =~ "can&#39;t be blank"
{:ok, _view, html} = {:ok, _view, html} =
index_live index_live
|> form("#container-form", |> form("#container-form")
|> render_submit(
container: Map.merge(@create_attrs, %{location: "some updated location"}) container: Map.merge(@create_attrs, %{location: "some updated location"})
) )
|> render_submit()
|> follow_redirect(conn, Routes.container_index_path(conn, :index)) |> follow_redirect(conn, Routes.container_index_path(conn, :index))
container = container.id |> Containers.get_container!(current_user) 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" assert html =~ "some updated location"
end end
test "deletes container in listing", %{conn: conn, container: container} do test "deletes container in listing", %{conn: conn, container: container} do
{:ok, index_live, _html} = live(conn, Routes.container_index_path(conn, :index)) {: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() assert index_live |> element(~s/a[aria-label="Delete #{container.name}"]/) |> render_click()
refute has_element?(index_live, "#container-#{container.id}") refute has_element?(index_live, "#container-#{container.id}")
end end
@ -232,7 +212,6 @@ defmodule CanneryWeb.ContainerLiveTest do
container: %{name: name, location: location} = container container: %{name: name, location: location} = container
} do } do
{:ok, _show_live, html} = live(conn, Routes.container_show_path(conn, :show, container)) {:ok, _show_live, html} = live(conn, Routes.container_show_path(conn, :show, container))
assert html =~ name assert html =~ name
assert html =~ location assert html =~ location
end end
@ -245,24 +224,78 @@ defmodule CanneryWeb.ContainerLiveTest do
{:ok, show_live, _html} = live(conn, Routes.container_show_path(conn, :show, container)) {: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() =~ 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_patch(show_live, Routes.container_show_path(conn, :edit, container))
# assert show_live assert show_live
# |> form("#container-form", container: @invalid_attrs) |> form("#container-form")
# |> render_change() =~ dgettext("errors", "can't be blank") |> render_change(container: @invalid_attrs) =~ "can&#39;t be blank"
{:ok, _view, html} = {:ok, _view, html} =
show_live show_live
|> form("#container-form", container: @update_attrs) |> form("#container-form")
|> render_submit() |> render_submit(container: @update_attrs)
|> follow_redirect(conn, Routes.container_show_path(conn, :show, container)) |> follow_redirect(conn, Routes.container_show_path(conn, :show, container))
container = container.id |> Containers.get_container!(current_user) 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" assert html =~ "some updated location"
end 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 end
describe "Show with ammo group" do describe "Show with ammo group" do
@ -289,47 +322,4 @@ defmodule CanneryWeb.ContainerLiveTest do
assert html =~ "\n20\n" assert html =~ "\n20\n"
end end
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 =~ "\n20\n"
html =
show_live
|> element(~s/input[type="checkbox"][aria-labelledby="toggle_show_used-label"}]/)
|> render_click()
assert html =~ ammo_type_name
assert html =~ "\n20\n"
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 =~ "\n20\n"
html =
show_live
|> element(~s/input[type="checkbox"][aria-labelledby="toggle_show_used-label"}]/)
|> render_click()
assert html =~ ammo_type_name
assert html =~ "\n20\n"
assert html =~ "Empty"
end
end
end end

View File

@ -5,14 +5,13 @@ defmodule CanneryWeb.HomeLiveTest do
use CanneryWeb.ConnCase use CanneryWeb.ConnCase
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
import CanneryWeb.Gettext
@moduletag :home_live_test @moduletag :home_live_test
test "disconnected and connected render", %{conn: conn} do test "disconnected and connected render", %{conn: conn} do
{:ok, home_live, disconnected_html} = live(conn, "/") {:ok, home_live, disconnected_html} = live(conn, "/")
assert disconnected_html =~ gettext("Welcome to %{name}", name: "Cannery") assert disconnected_html =~ "Welcome to Cannery"
assert render(home_live) =~ gettext("Welcome to %{name}", name: "Cannery") assert render(home_live) =~ "Welcome to Cannery"
end end
test "displays version number", %{conn: conn} do test "displays version number", %{conn: conn} do

View File

@ -5,13 +5,12 @@ defmodule CanneryWeb.InviteLiveTest do
use CanneryWeb.ConnCase use CanneryWeb.ConnCase
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
import CanneryWeb.Gettext
alias Cannery.Accounts.Invites alias Cannery.Accounts.Invites
@moduletag :invite_live_test @moduletag :invite_live_test
@create_attrs %{"name" => "some name"} @create_attrs %{name: "some name"}
@update_attrs %{"name" => "some updated name"} @update_attrs %{name: "some updated name"}
# @invalid_attrs %{"name" => nil} @invalid_attrs %{name: nil}
describe "Index" do describe "Index" do
setup [:register_and_log_in_user] setup [:register_and_log_in_user]
@ -24,32 +23,27 @@ defmodule CanneryWeb.InviteLiveTest do
test "lists all invites", %{conn: conn, invite: invite} do test "lists all invites", %{conn: conn, invite: invite} do
{:ok, _index_live, html} = live(conn, Routes.invite_index_path(conn, :index)) {:ok, _index_live, html} = live(conn, Routes.invite_index_path(conn, :index))
assert html =~ gettext("Invites") assert html =~ "Invites"
assert html =~ invite.name assert html =~ invite.name
end end
test "saves new invite", %{conn: conn} do test "saves new invite", %{conn: conn} do
{:ok, index_live, _html} = live(conn, Routes.invite_index_path(conn, :index)) {:ok, index_live, _html} = live(conn, Routes.invite_index_path(conn, :index))
assert index_live |> element("a", dgettext("actions", "Create Invite")) |> render_click() =~ assert index_live |> element("a", "Create Invite") |> render_click() =~ "New Invite"
gettext("New Invite")
assert_patch(index_live, Routes.invite_index_path(conn, :new)) assert_patch(index_live, Routes.invite_index_path(conn, :new))
# assert index_live assert index_live
# |> form("#invite-form", invite: @invalid_attrs) |> form("#invite-form")
# |> render_change() =~ dgettext("errors", "can't be blank") |> render_change(invite: @invalid_attrs) =~ "can&#39;t be blank"
{:ok, _live, html} = {:ok, _live, html} =
index_live index_live
|> form("#invite-form", invite: @create_attrs) |> form("#invite-form")
|> render_submit() |> render_submit(invite: @create_attrs)
|> follow_redirect(conn, Routes.invite_index_path(conn, :index)) |> follow_redirect(conn, Routes.invite_index_path(conn, :index))
assert html =~ assert html =~ "some name created successfully"
dgettext("prompts", "%{invite_name} created successfully", invite_name: "some name")
assert html =~ "some name"
end end
test "updates invite in listing", %{conn: conn, invite: invite} do test "updates invite in listing", %{conn: conn, invite: invite} do
@ -57,27 +51,21 @@ defmodule CanneryWeb.InviteLiveTest do
assert index_live assert index_live
|> element(~s/a[aria-label="Edit invite for #{invite.name}"]/) |> element(~s/a[aria-label="Edit invite for #{invite.name}"]/)
|> render_click() =~ |> render_click() =~ "Edit Invite"
gettext("Edit Invite")
assert_patch(index_live, Routes.invite_index_path(conn, :edit, invite)) assert_patch(index_live, Routes.invite_index_path(conn, :edit, invite))
# assert index_live assert index_live
# |> form("#invite-form", invite: @invalid_attrs) |> form("#invite-form")
# |> render_change() =~ dgettext("errors", "can't be blank") |> render_change(invite: @invalid_attrs) =~ "can&#39;t be blank"
{:ok, _live, html} = {:ok, _live, html} =
index_live index_live
|> form("#invite-form", invite: @update_attrs) |> form("#invite-form")
|> render_submit() |> render_submit(invite: @update_attrs)
|> follow_redirect(conn, Routes.invite_index_path(conn, :index)) |> follow_redirect(conn, Routes.invite_index_path(conn, :index))
assert html =~ assert html =~ "some updated name updated successfully"
dgettext("prompts", "%{invite_name} updated successfully",
invite_name: "some updated name"
)
assert html =~ "some updated name"
end end
test "deletes invite in listing", %{conn: conn, invite: invite} do test "deletes invite in listing", %{conn: conn, invite: invite} do

View File

@ -6,25 +6,28 @@ defmodule CanneryWeb.RangeLiveTest do
use CanneryWeb.ConnCase use CanneryWeb.ConnCase
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
import Cannery.Fixtures import Cannery.Fixtures
import CanneryWeb.Gettext
@moduletag :range_live_test @moduletag :range_live_test
@create_attrs %{"ammo_left" => 5, "notes" => "some notes"} @create_attrs %{ammo_left: 5, notes: "some notes"}
@update_attrs %{"count" => 16, "notes" => "some updated notes"} @update_attrs %{count: 16, notes: "some updated notes"}
# @invalid_attrs %{"count" => nil, "notes" => nil} @invalid_attrs %{count: nil, notes: nil}
defp create_shot_group(%{current_user: current_user}) do 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) ammo_type = ammo_type_fixture(current_user)
{1, [ammo_group]} = {1, [ammo_group]} = ammo_group_fixture(%{staged: true}, ammo_type, container, current_user)
ammo_group_fixture(%{"staged" => true}, ammo_type, container, current_user)
shot_group = 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_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 end
describe "Index" do describe "Index" do
@ -33,32 +36,94 @@ defmodule CanneryWeb.RangeLiveTest do
test "lists all shot_groups", %{conn: conn, shot_group: shot_group} 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)) {: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 assert html =~ shot_group.notes
end 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 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)) {:ok, index_live, html} = live(conn, Routes.range_index_path(conn, :index))
assert html =~ shot_group.notes assert html =~ shot_group.notes
assert index_live assert index_live
|> form(~s/form[phx-change="search"]/, |> form(~s/form[phx-change="search"]/)
search: %{search_term: shot_group.notes} |> render_change(search: %{search_term: shot_group.notes}) =~ shot_group.notes
)
|> render_change() =~ shot_group.notes
assert_patch(index_live, Routes.range_index_path(conn, :search, shot_group.notes)) assert_patch(index_live, Routes.range_index_path(conn, :search, shot_group.notes))
refute index_live refute index_live
|> form(~s/form[phx-change="search"]/, search: %{search_term: "something_else"}) |> form(~s/form[phx-change="search"]/)
|> render_change() =~ shot_group.notes |> render_change(search: %{search_term: "something_else"}) =~ shot_group.notes
assert_patch(index_live, Routes.range_index_path(conn, :search, "something_else")) assert_patch(index_live, Routes.range_index_path(conn, :search, "something_else"))
assert index_live assert index_live
|> form(~s/form[phx-change="search"]/, search: %{search_term: ""}) |> form(~s/form[phx-change="search"]/)
|> render_change() =~ shot_group.notes |> render_change(search: %{search_term: ""}) =~ shot_group.notes
assert_patch(index_live, Routes.range_index_path(conn, :index)) assert_patch(index_live, Routes.range_index_path(conn, :index))
end end
@ -66,22 +131,20 @@ defmodule CanneryWeb.RangeLiveTest do
test "saves new shot_group", %{conn: conn, ammo_group: ammo_group} 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)) {:ok, index_live, _html} = live(conn, Routes.range_index_path(conn, :index))
assert index_live |> element("a", dgettext("actions", "Record shots")) |> render_click() =~ assert index_live |> element("a", "Record shots") |> render_click() =~ "Record shots"
gettext("Record shots")
assert_patch(index_live, Routes.range_index_path(conn, :add_shot_group, ammo_group)) assert_patch(index_live, Routes.range_index_path(conn, :add_shot_group, ammo_group))
# assert index_live assert index_live
# |> form("#shot_group-form", shot_group: @invalid_attrs) |> form("#shot-group-form")
# |> render_change() =~ dgettext("errors", "is invalid") |> render_change(shot_group: @invalid_attrs) =~ "can&#39;t be blank"
{:ok, _view, html} = {:ok, _view, html} =
index_live index_live
|> form("#shot-group-form", shot_group: @create_attrs) |> form("#shot-group-form")
|> render_submit() |> render_submit(shot_group: @create_attrs)
|> follow_redirect(conn, Routes.range_index_path(conn, :index)) |> 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" assert html =~ "some notes"
end end
@ -90,14 +153,13 @@ defmodule CanneryWeb.RangeLiveTest do
assert index_live assert index_live
|> element(~s/a[aria-label="Edit shot record of #{shot_group.count} shots"]/) |> element(~s/a[aria-label="Edit shot record of #{shot_group.count} shots"]/)
|> render_click() =~ |> render_click() =~ "Edit Shot Records"
gettext("Edit Shot Records")
assert_patch(index_live, Routes.range_index_path(conn, :edit, shot_group)) assert_patch(index_live, Routes.range_index_path(conn, :edit, shot_group))
# assert index_live assert index_live
# |> form("#shot_group-form", shot_group: @invalid_attrs) |> form("#shot-group-form")
# |> render_change() =~ dgettext("errors", "is invalid") |> render_change(shot_group: @invalid_attrs) =~ "can&#39;t be blank"
{:ok, _view, html} = {:ok, _view, html} =
index_live index_live
@ -105,7 +167,7 @@ defmodule CanneryWeb.RangeLiveTest do
|> render_submit() |> render_submit()
|> follow_redirect(conn, Routes.range_index_path(conn, :index)) |> 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" assert html =~ "some updated notes"
end end

View File

@ -5,26 +5,24 @@ defmodule CanneryWeb.TagLiveTest do
use CanneryWeb.ConnCase use CanneryWeb.ConnCase
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
import CanneryWeb.Gettext
@moduletag :tag_live_test @moduletag :tag_live_test
@create_attrs %{ @create_attrs %{
"bg_color" => "some bg-color", bg_color: "#100000",
"name" => "some name", name: "some name",
"text_color" => "some text-color" text_color: "#000000"
} }
@update_attrs %{ @update_attrs %{
"bg_color" => "some updated bg-color", bg_color: "#100001",
"name" => "some updated name", name: "some updated name",
"text_color" => "some updated text-color" 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 def create_tag(%{current_user: current_user}) do
tag = tag_fixture(current_user) tag = tag_fixture(current_user)
@ -37,7 +35,7 @@ defmodule CanneryWeb.TagLiveTest do
test "lists all tags", %{conn: conn, tag: tag} do test "lists all tags", %{conn: conn, tag: tag} do
{:ok, _index_live, html} = live(conn, Routes.tag_index_path(conn, :index)) {:ok, _index_live, html} = live(conn, Routes.tag_index_path(conn, :index))
assert html =~ gettext("Tags") assert html =~ "Tags"
assert html =~ tag.bg_color assert html =~ tag.bg_color
end end
@ -47,22 +45,20 @@ defmodule CanneryWeb.TagLiveTest do
assert html =~ tag.name assert html =~ tag.name
assert index_live assert index_live
|> form(~s/form[phx-change="search"]/, |> form(~s/form[phx-change="search"]/)
search: %{search_term: tag.name} |> render_change(search: %{search_term: tag.name}) =~ tag.name
)
|> render_change() =~ tag.name
assert_patch(index_live, Routes.tag_index_path(conn, :search, tag.name)) assert_patch(index_live, Routes.tag_index_path(conn, :search, tag.name))
refute index_live refute index_live
|> form(~s/form[phx-change="search"]/, search: %{search_term: "something_else"}) |> form(~s/form[phx-change="search"]/)
|> render_change() =~ tag.name |> render_change(search: %{search_term: "something_else"}) =~ tag.name
assert_patch(index_live, Routes.tag_index_path(conn, :search, "something_else")) assert_patch(index_live, Routes.tag_index_path(conn, :search, "something_else"))
assert index_live assert index_live
|> form(~s/form[phx-change="search"]/, search: %{search_term: ""}) |> form(~s/form[phx-change="search"]/)
|> render_change() =~ tag.name |> render_change(search: %{search_term: ""}) =~ tag.name
assert_patch(index_live, Routes.tag_index_path(conn, :index)) assert_patch(index_live, Routes.tag_index_path(conn, :index))
end end
@ -70,47 +66,43 @@ defmodule CanneryWeb.TagLiveTest do
test "saves new tag", %{conn: conn} do test "saves new tag", %{conn: conn} do
{:ok, index_live, _html} = live(conn, Routes.tag_index_path(conn, :index)) {:ok, index_live, _html} = live(conn, Routes.tag_index_path(conn, :index))
assert index_live |> element("a", dgettext("actions", "New Tag")) |> render_click() =~ assert index_live |> element("a", "New Tag") |> render_click() =~ "New Tag"
dgettext("actions", "New Tag")
assert_patch(index_live, Routes.tag_index_path(conn, :new)) assert_patch(index_live, Routes.tag_index_path(conn, :new))
# assert index_live assert index_live
# |> form("#tag-form", tag: @invalid_attrs) |> form("#tag-form")
# |> render_change() =~ dgettext("errors", "can't be blank") |> render_change(tag: @invalid_attrs) =~ "can&#39;t be blank"
{:ok, _view, html} = {:ok, _view, html} =
index_live index_live
|> form("#tag-form", tag: @create_attrs) |> form("#tag-form")
|> render_submit() |> render_submit(tag: @create_attrs)
|> follow_redirect(conn, Routes.tag_index_path(conn, :index)) |> follow_redirect(conn, Routes.tag_index_path(conn, :index))
assert html =~ dgettext("actions", "%{name} created successfully", name: "some name") assert html =~ "some name created successfully"
assert html =~ "some bg-color" assert html =~ "#100000"
end end
test "updates tag in listing", %{conn: conn, tag: tag} do test "updates tag in listing", %{conn: conn, tag: tag} do
{:ok, index_live, _html} = live(conn, Routes.tag_index_path(conn, :index)) {: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() =~ 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_patch(index_live, Routes.tag_index_path(conn, :edit, tag))
# assert index_live assert index_live
# |> form("#tag-form", tag: @invalid_attrs) |> form("#tag-form")
# |> render_change() =~ dgettext("errors", "can't be blank") |> render_change(tag: @invalid_attrs) =~ "can&#39;t be blank"
{:ok, _view, html} = {:ok, _view, html} =
index_live index_live
|> form("#tag-form", tag: @update_attrs) |> form("#tag-form")
|> render_submit() |> render_submit(tag: @update_attrs)
|> follow_redirect(conn, Routes.tag_index_path(conn, :index)) |> follow_redirect(conn, Routes.tag_index_path(conn, :index))
assert html =~ assert html =~ "some updated name updated successfully"
dgettext("prompts", "%{name} updated successfully", name: "some updated name") assert html =~ "#100001"
assert html =~ "some updated bg-color"
end end
test "deletes tag in listing", %{conn: conn, tag: tag} do 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 use CanneryWeb.ConnCase, async: true
import CanneryWeb.Gettext
# Bring render/3 and render_to_string/3 for testing custom views # Bring render/3 and render_to_string/3 for testing custom views
import Phoenix.View import Phoenix.View
@moduletag :error_view_test @moduletag :error_view_test
test "renders 404.html" do test "renders 404.html" do
assert render_to_string(CanneryWeb.ErrorView, "404.html", []) =~ assert render_to_string(CanneryWeb.ErrorView, "404.html", []) =~ "Not found"
dgettext("errors", "Not found")
end end
test "renders 500.html" do test "renders 500.html" do
assert render_to_string(CanneryWeb.ErrorView, "500.html", []) =~ assert render_to_string(CanneryWeb.ErrorView, "500.html", []) =~ "Internal Server Error"
dgettext("errors", "Internal Server Error")
end end
end end

View File

@ -26,7 +26,7 @@ defmodule CanneryWeb.ConnCase do
import Plug.Conn import Plug.Conn
import Phoenix.ConnTest import Phoenix.ConnTest
# credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse # credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
import Cannery.Fixtures import Cannery.{DataCase, Fixtures}
import CanneryWeb.ConnCase import CanneryWeb.ConnCase
alias CanneryWeb.Router.Helpers, as: Routes alias CanneryWeb.Router.Helpers, as: Routes

View File

@ -48,4 +48,15 @@ defmodule Cannery.DataCase do
end) end)
end) 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 end

View File

@ -3,6 +3,8 @@ defmodule Cannery.Fixtures do
This module defines test helpers for creating entities This module defines test helpers for creating entities
""" """
import Cannery.DataCase
alias Cannery.{ alias Cannery.{
Accounts, Accounts,
Accounts.User, Accounts.User,
@ -17,16 +19,13 @@ defmodule Cannery.Fixtures do
Repo 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() :: User.t()
@spec user_fixture(attrs :: map()) :: User.t() @spec user_fixture(attrs :: map()) :: User.t()
def user_fixture(attrs \\ %{}) do def user_fixture(attrs \\ %{}) do
attrs attrs
|> Enum.into(%{ |> Enum.into(%{
"email" => unique_user_email(), email: unique_user_email(),
"password" => valid_user_password() password: valid_user_password()
}) })
|> Accounts.register_user() |> Accounts.register_user()
|> unwrap_ok_tuple() |> unwrap_ok_tuple()
@ -37,8 +36,8 @@ defmodule Cannery.Fixtures do
def admin_fixture(attrs \\ %{}) do def admin_fixture(attrs \\ %{}) do
attrs attrs
|> Enum.into(%{ |> Enum.into(%{
"email" => unique_user_email(), email: unique_user_email(),
"password" => valid_user_password() password: valid_user_password()
}) })
|> Accounts.register_user() |> Accounts.register_user()
|> unwrap_ok_tuple() |> unwrap_ok_tuple()
@ -77,9 +76,9 @@ defmodule Cannery.Fixtures do
def shot_group_fixture(attrs \\ %{}, %User{} = user, %AmmoGroup{} = ammo_group) do def shot_group_fixture(attrs \\ %{}, %User{} = user, %AmmoGroup{} = ammo_group) do
attrs attrs
|> Enum.into(%{ |> Enum.into(%{
"count" => 20, count: 20,
"date" => ~N[2022-02-13 03:17:00], date: ~N[2022-02-13 03:17:00],
"notes" => "some notes" notes: random_string()
}) })
|> Cannery.ActivityLog.create_shot_group(user, ammo_group) |> Cannery.ActivityLog.create_shot_group(user, ammo_group)
|> unwrap_ok_tuple() |> unwrap_ok_tuple()
@ -92,7 +91,7 @@ defmodule Cannery.Fixtures do
@spec container_fixture(attrs :: map(), User.t()) :: Container.t() @spec container_fixture(attrs :: map(), User.t()) :: Container.t()
def container_fixture(attrs \\ %{}, %User{} = user) do def container_fixture(attrs \\ %{}, %User{} = user) do
attrs attrs
|> Enum.into(%{"name" => "My container", "type" => "Ammo can"}) |> Enum.into(%{name: random_string(), type: "Ammo can"})
|> Containers.create_container(user) |> Containers.create_container(user)
|> unwrap_ok_tuple() |> unwrap_ok_tuple()
end end
@ -104,7 +103,7 @@ defmodule Cannery.Fixtures do
@spec ammo_type_fixture(attrs :: map(), User.t()) :: AmmoType.t() @spec ammo_type_fixture(attrs :: map(), User.t()) :: AmmoType.t()
def ammo_type_fixture(attrs \\ %{}, %User{} = user) do def ammo_type_fixture(attrs \\ %{}, %User{} = user) do
attrs attrs
|> Enum.into(%{"name" => "ammo_type"}) |> Enum.into(%{name: random_string(), class: :rifle})
|> Ammo.create_ammo_type(user) |> Ammo.create_ammo_type(user)
|> unwrap_ok_tuple() |> unwrap_ok_tuple()
end end
@ -132,10 +131,10 @@ defmodule Cannery.Fixtures do
) do ) do
attrs attrs
|> Enum.into(%{ |> Enum.into(%{
"ammo_type_id" => ammo_type_id, ammo_type_id: ammo_type_id,
"container_id" => container_id, container_id: container_id,
"count" => 20, count: 20,
"purchased_on" => Date.utc_today() purchased_on: Date.utc_today()
}) })
|> Ammo.create_ammo_groups(multiplier, user) |> Ammo.create_ammo_groups(multiplier, user)
|> unwrap_ok_tuple() |> unwrap_ok_tuple()
@ -149,9 +148,9 @@ defmodule Cannery.Fixtures do
def tag_fixture(attrs \\ %{}, %User{} = user) do def tag_fixture(attrs \\ %{}, %User{} = user) do
attrs attrs
|> Enum.into(%{ |> Enum.into(%{
"bg_color" => "some bg-color", bg_color: "#100000",
"name" => "some name", name: random_string(),
"text_color" => "some text-color" text_color: "#000000"
}) })
|> Containers.create_tag(user) |> Containers.create_tag(user)
|> unwrap_ok_tuple() |> unwrap_ok_tuple()