61 Commits
0.1.0 ... 0.4.1

Author SHA1 Message Date
6455e2710d bump version
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-28 23:58:16 -04:00
6523b28aa2 update translations 2022-03-28 23:57:08 -04:00
bad1a23dfe fix ammo group overflow 2022-03-28 23:56:56 -04:00
e0ddefe1d7 fix tag and container overflow 2022-03-28 23:56:45 -04:00
5d6ecba9f7 display inline block, add min width to buttons and links 2022-03-28 23:56:15 -04:00
a2d1ff9b89 fix credo 2022-03-28 23:05:12 -04:00
34288a0070 make drone cache persistent
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-04 23:36:19 -05:00
f9b08222e1 bump version
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-04 23:19:11 -05:00
c0179f48bd mix format
Some checks reported errors
continuous-integration/drone/push Build was killed
2022-03-04 23:17:53 -05:00
8bb4aab49c Fix ammo group create form error bug 2022-03-04 23:16:37 -05:00
9f2cc54738 fix maintain attrs hook 2022-03-04 22:56:52 -05:00
0309e9d714 fix credo 2022-03-04 22:28:05 -05:00
af4af84515 use table component for shot group table 2022-03-04 22:06:01 -05:00
ec6946068e fix view helpers display bug 2022-03-04 22:05:08 -05:00
f120e54c3e use table component for ammo group show table 2022-03-04 21:57:22 -05:00
3d115c6383 fixup! use table component for move ammo group component 2022-03-04 21:56:48 -05:00
1d4622a285 fixup! use table component for ammo group table 2022-03-04 21:09:05 -05:00
d9e7948bb0 fixup! use new table component for ammo type index table 2022-03-04 21:07:32 -05:00
da8c788992 use table component for move ammo group component 2022-03-04 20:53:18 -05:00
bd20820361 use table component for ammo group table 2022-03-04 20:00:09 -05:00
d0ee81093a use new table component for ammo type index table 2022-03-04 18:28:10 -05:00
6080fdbe64 add table component 2022-03-04 18:26:57 -05:00
f42aaf9099 fix bug with move ammo group component 2022-03-04 00:06:08 -05:00
a6aa6f3386 fix elements flashing black 2022-02-26 00:16:36 -05:00
8405513337 add link to changelog 2022-02-24 20:42:27 -05:00
d0857eccc1 remove "listing" from titles
All checks were successful
continuous-integration/drone/push Build is passing
2022-02-24 00:52:46 -05:00
4c9e707181 merge translations
All checks were successful
continuous-integration/drone/push Build is passing
2022-02-24 00:30:23 -05:00
e6a4fbcfb5 bump version
All checks were successful
continuous-integration/drone/push Build is passing
2022-02-24 00:18:33 -05:00
a6b2c6181e add multiple ammo groups at one time
All checks were successful
continuous-integration/drone/push Build is passing
2022-02-24 00:17:23 -05:00
d79d0fa179 use today's date when adding new shot groups 2022-02-23 20:52:12 -05:00
46387e8d7a update changelog 2022-02-23 20:47:23 -05:00
dccea1b9de edit and delete shot groups from ammo group page 2022-02-23 20:45:58 -05:00
5ca21c64fd fix text styling 2022-02-23 20:35:18 -05:00
9773ccc6ff add prompt to create container before creating ammo group 2022-02-23 20:34:07 -05:00
9cd2bc574b conditionally load containers list 2022-02-23 19:51:07 -05:00
ee28de1178 fix not showing 0 if ammo type with 0 rounds 2022-02-23 19:41:21 -05:00
91cf9d0eb5 bump version
All checks were successful
continuous-integration/drone/push Build is passing
2022-02-20 20:43:04 -05:00
3ce8eda712 mix format
All checks were successful
continuous-integration/drone/push Build is passing
2022-02-20 20:40:24 -05:00
1a78e88b34 add page titles to registration and setting pages 2022-02-20 20:39:27 -05:00
968abd04ee fix grids having uneven margins in phone mode 2022-02-20 20:28:42 -05:00
dc209fa192 fix modals with overflowing content 2022-02-20 20:28:34 -05:00
a80df49fdd bump version
All checks were successful
continuous-integration/drone/push Build is passing
2022-02-20 15:20:09 -05:00
92d1d21d00 add changelog
Some checks reported errors
continuous-integration/drone/push Build was killed
2022-02-20 15:17:38 -05:00
917f627933 add back modal click-away 2022-02-20 15:17:12 -05:00
4946a6b119 fixes template error on modal refreshes 2022-02-20 15:01:15 -05:00
9f784c3190 fix closing modal without triggering reload 2022-02-20 15:00:53 -05:00
aa08e212ee fix loading and reconnecting pages 2022-02-19 21:29:36 -05:00
80ad939aab fix docker image tag for latest builds 2022-02-19 21:29:29 -05:00
08c9cddc78 fix user card confirmation time spacing
Some checks failed
continuous-integration/drone/push Build is failing
2022-02-19 19:54:52 -05:00
e6285c282b add version tags
Some checks failed
continuous-integration/drone/push Build is failing
2022-02-19 19:38:29 -05:00
61829fc042 fix grid spacing in mobile mode
Some checks reported errors
continuous-integration/drone/tag Build is passing
continuous-integration/drone/push Build was killed
2022-02-19 18:08:46 -05:00
e2f8fd3ee5 fix checkbox spacing 2022-02-19 18:06:42 -05:00
763c9e521e fix readme image embed
All checks were successful
continuous-integration/drone/push Build is passing
2022-02-19 00:55:37 -05:00
98747be7c2 add preview to readme
All checks were successful
continuous-integration/drone/push Build is passing
2022-02-19 00:51:01 -05:00
91288a9ffa fix tests
All checks were successful
continuous-integration/drone/push Build is passing
2022-02-19 00:31:17 -05:00
a19ec682e6 add ammo type round totals and total rounds shot 2022-02-19 00:18:24 -05:00
dba53106fb show original count, current value, percentage remaining and shot history for ammo groups 2022-02-19 00:05:22 -05:00
91ff0c14e4 fix ammo type displays 2022-02-18 23:25:44 -05:00
bf27511caa remove default boolean columns if all false 2022-02-18 23:06:32 -05:00
4ff2f64a22 add tag editing to containers 2022-02-18 23:00:04 -05:00
146c8e7ab3 add Cannery to page titles 2022-02-18 22:47:31 -05:00
94 changed files with 2377 additions and 1173 deletions

View File

@ -157,17 +157,17 @@
# #
# Controversial and experimental checks (opt-in, just replace `false` with `[]`) # Controversial and experimental checks (opt-in, just replace `false` with `[]`)
# #
{Credo.Check.Consistency.MultiAliasImportRequireUse, false}, {Credo.Check.Consistency.MultiAliasImportRequireUse, []},
{Credo.Check.Consistency.UnusedVariableNames, false}, {Credo.Check.Consistency.UnusedVariableNames, [force: :meaningful]},
{Credo.Check.Design.DuplicatedCode, false}, {Credo.Check.Design.DuplicatedCode, false},
{Credo.Check.Readability.AliasAs, false}, {Credo.Check.Readability.AliasAs, false},
{Credo.Check.Readability.BlockPipe, false}, {Credo.Check.Readability.BlockPipe, false},
{Credo.Check.Readability.ImplTrue, false}, {Credo.Check.Readability.ImplTrue, false},
{Credo.Check.Readability.MultiAlias, false}, {Credo.Check.Readability.MultiAlias, false},
{Credo.Check.Readability.SeparateAliasRequire, false}, {Credo.Check.Readability.SeparateAliasRequire, []},
{Credo.Check.Readability.SinglePipe, false}, {Credo.Check.Readability.SinglePipe, false},
{Credo.Check.Readability.Specs, false}, {Credo.Check.Readability.Specs, false},
{Credo.Check.Readability.StrictModuleLayout, false}, {Credo.Check.Readability.StrictModuleLayout, []},
{Credo.Check.Readability.WithCustomTaggedTuple, false}, {Credo.Check.Readability.WithCustomTaggedTuple, false},
{Credo.Check.Refactor.ABCSize, false}, {Credo.Check.Refactor.ABCSize, false},
{Credo.Check.Refactor.AppendSingleItem, false}, {Credo.Check.Refactor.AppendSingleItem, false},
@ -176,9 +176,9 @@
{Credo.Check.Refactor.NegatedIsNil, false}, {Credo.Check.Refactor.NegatedIsNil, false},
{Credo.Check.Refactor.PipeChainStart, false}, {Credo.Check.Refactor.PipeChainStart, false},
{Credo.Check.Refactor.VariableRebinding, false}, {Credo.Check.Refactor.VariableRebinding, false},
{Credo.Check.Warning.LeakyEnvironment, false}, {Credo.Check.Warning.LeakyEnvironment, []},
{Credo.Check.Warning.MapGetUnsafePass, false}, {Credo.Check.Warning.MapGetUnsafePass, []},
{Credo.Check.Warning.UnsafeToAtom, false} {Credo.Check.Warning.UnsafeToAtom, []}
# #
# Custom checks can be created using `mix credo.gen.check`. # Custom checks can be created using `mix credo.gen.check`.

View File

@ -27,19 +27,33 @@ steps:
- npm install --prefix assets - npm install --prefix assets
- mix test - mix test
- name: build and publish - name: build and publish stable
image: plugins/docker image: plugins/docker
settings: settings:
repo: shibaobun/cannery repo: shibaobun/cannery
tags: latest
username: username:
from_secret: docker_username from_secret: docker_username
password: password:
from_secret: docker_password from_secret: docker_password
tags: latest
when: when:
branch: branch:
- stable - stable
- name: build and publish tagged version
image: plugins/docker
settings:
repo: shibaobun/cannery
username:
from_secret: docker_username
password:
from_secret: docker_password
tags:
- ${DRONE_TAG}
when:
event:
- tag
- name: rebuild-cache - name: rebuild-cache
image: drillster/drone-volume-cache image: drillster/drone-volume-cache
volumes: volumes:
@ -63,7 +77,7 @@ services:
volumes: volumes:
- name: cache - name: cache
host: host:
path: /tmp/drone-cache path: /run/media/default/ssdsrv/gitea/drone-cache
- name: docker_sock - name: docker_sock
host: host:
path: /var/run/docker.sock path: /var/run/docker.sock

45
CHANGELOG.md Normal file
View File

@ -0,0 +1,45 @@
# v0.4.1
- Fix button and tag text wrapping
- Code quality fixes
# v0.4.0
- Make tables sortable
- Add link to changelog from version number
- Fix some elements flashing with black background
- Fix bug with moving ammo group to new container
- Fix bug with no error showing up for create ammo group form
# v0.3.0
- Fix ammo type counts not showing when count is 0
- Add prompt to create first container before first ammo group
- Edit and delete shot groups from ammo group show page
- Use today's date when adding new shot groups
- Create multiple ammo groups at one time
# v0.2.3
- Fix modals with overflowing forms
- Fix grids having uneven margins in phone mode
- Add page titles to registration and setting pages
# v0.2.2
- Fix loading and reconnecting pages not being fixed
- Fix closing modal in some cases not triggering a page reload
- Fix error when display container and tag edit routes from a fresh reload
# v0.2.1
- Fix checkbox spacing for mobile view
- Fix spacing with form elements in mobile view
- Fix user card spacing
# v0.2.0
- Add or remove tags from containers list and details page
- Show tags on containers
- Add "Cannery" to page titles
- Don't show true/false column for ammo types if all values are false
- Fix ammo type firing type display
- Show original count, current value, and percentage remaining for ammo groups
- Show shot history for an ammo group
- Show ammo round totals and total shot for ammo types
# v0.1.0
- Initial release!

View File

@ -1,5 +1,7 @@
# Cannery # Cannery
![screenshot](https://gitea.bubbletea.dev/shibao/cannery/raw/branch/stable/home.png)
The self-hosted firearm tracker website. The self-hosted firearm tracker website.
* Easy to Use: Cannery lets you easily keep an eye on your ammo levels before * Easy to Use: Cannery lets you easily keep an eye on your ammo levels before

View File

@ -17,8 +17,7 @@
-o-transform: scale(1.5); -o-transform: scale(1.5);
transform: scale(1.5); transform: scale(1.5);
padding: 10px; padding: 10px;
margin-left: auto; margin: 1em auto;
margin-right: auto;
} }
.title { .title {
@ -26,6 +25,7 @@
} }
.btn { .btn {
@apply inline-block break-all min-w-4;
@apply focus:outline-none px-4 py-2 rounded-lg; @apply focus:outline-none px-4 py-2 rounded-lg;
@apply shadow-sm focus:shadow-lg; @apply shadow-sm focus:shadow-lg;
@apply transition-all duration-300 ease-in-out; @apply transition-all duration-300 ease-in-out;
@ -52,6 +52,7 @@
} }
.link { .link {
@apply inline-block break-all min-w-4;
@apply hover:underline; @apply hover:underline;
@apply transition-colors duration-500 ease-in-out; @apply transition-colors duration-500 ease-in-out;
} }

View File

@ -2,7 +2,10 @@
// update. https://github.com/phoenixframework/phoenix_live_view/issues/1011 // update. https://github.com/phoenixframework/phoenix_live_view/issues/1011
export default { export default {
attrs () { return this.el.getAttribute('data-attrs').split(', ') }, attrs () {
const attrs = this.el.getAttribute('data-attrs')
if (attrs) { return attrs.split(', ') } else { return [] }
},
beforeUpdate () { this.prevAttrs = this.attrs().map(name => [name, this.el.getAttribute(name)]) }, beforeUpdate () { this.prevAttrs = this.attrs().map(name => [name, this.el.getAttribute(name)]) },
updated () { this.prevAttrs.forEach(([name, val]) => this.el.setAttribute(name, val)) } updated () { this.prevAttrs.forEach(([name, val]) => this.el.setAttribute(name, val)) }
} }

View File

@ -28,6 +28,20 @@ module.exports = {
128: '32rem', 128: '32rem',
192: '48rem', 192: '48rem',
256: '64rem' 256: '64rem'
},
minWidth: {
4: '1rem',
8: '2rem',
12: '3rem',
16: '4rem',
20: '8rem'
},
maxWidth: {
4: '1rem',
8: '2rem',
12: '3rem',
16: '4rem',
20: '8rem'
} }
} }
}, },

BIN
home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

View File

@ -196,7 +196,7 @@ defmodule Cannery.Accounts do
{:ok, _} <- Repo.transaction(user_email_multi(user, email, context)) do {:ok, _} <- Repo.transaction(user_email_multi(user, email, context)) do
:ok :ok
else else
_ -> :error _error_tuple -> :error
end end
end end
@ -265,7 +265,7 @@ defmodule Cannery.Accounts do
|> Repo.transaction() |> Repo.transaction()
|> case do |> case do
{:ok, %{user: user}} -> {:ok, user} {:ok, %{user: user}} -> {:ok, user}
{:error, :user, changeset, _} -> {:error, changeset} {:error, :user, changeset, _changes_so_far} -> {:error, changeset}
end end
end end
@ -372,7 +372,7 @@ defmodule Cannery.Accounts do
{:ok, %{user: user}} <- Repo.transaction(confirm_user_multi(user)) do {:ok, %{user: user}} <- Repo.transaction(confirm_user_multi(user)) do
{:ok, user} {:ok, user}
else else
_ -> :error _error_tuple -> :error
end end
end end
@ -420,7 +420,7 @@ defmodule Cannery.Accounts do
%User{} = user <- Repo.one(query) do %User{} = user <- Repo.one(query) do
user user
else else
_ -> nil _error_tuple -> nil
end end
end end
@ -444,7 +444,7 @@ defmodule Cannery.Accounts do
|> Repo.transaction() |> Repo.transaction()
|> case do |> case do
{:ok, %{user: user}} -> {:ok, user} {:ok, %{user: user}} -> {:ok, user}
{:error, :user, changeset, _} -> {:error, changeset} {:error, :user, changeset, _changes_so_far} -> {:error, changeset}
end end
end end
end end

View File

@ -171,7 +171,7 @@ defmodule Cannery.Accounts.User do
Bcrypt.verify_pass(password, hashed_password) Bcrypt.verify_pass(password, hashed_password)
end end
def valid_password?(_, _) do def valid_password?(_invalid_user, _invalid_password) do
Bcrypt.no_user_verify() Bcrypt.no_user_verify()
false false
end end

View File

@ -8,6 +8,8 @@ defmodule Cannery.Ammo do
alias Cannery.Ammo.{AmmoGroup, AmmoType} alias Cannery.Ammo.{AmmoGroup, AmmoType}
alias Ecto.Changeset alias Ecto.Changeset
@ammo_group_create_limit 10_000
@doc """ @doc """
Returns the list of ammo_types. Returns the list of ammo_types.
@ -67,6 +69,59 @@ defmodule Cannery.Ammo do
) )
end end
@doc """
Gets the total number of rounds for an ammo type
Raises `Ecto.NoResultsError` if the Ammo type does not exist.
## Examples
iex> get_round_count_for_ammo_type(123, %User{id: 123})
%AmmoType{}
iex> get_round_count_for_ammo_type(456, %User{id: 123})
** (Ecto.NoResultsError)
"""
@spec get_round_count_for_ammo_type(AmmoType.t(), User.t()) :: non_neg_integer()
def get_round_count_for_ammo_type(
%AmmoType{id: ammo_type_id, user_id: user_id},
%User{id: user_id}
) do
Repo.one!(
from ag in AmmoGroup,
where: ag.ammo_type_id == ^ammo_type_id,
select: sum(ag.count)
) || 0
end
@doc """
Gets the total number of rounds shot for an ammo type
Raises `Ecto.NoResultsError` if the Ammo type does not exist.
## Examples
iex> get_used_count_for_ammo_type(123, %User{id: 123})
%AmmoType{}
iex> get_used_count_for_ammo_type(456, %User{id: 123})
** (Ecto.NoResultsError)
"""
@spec get_used_count_for_ammo_type(AmmoType.t(), User.t()) :: non_neg_integer()
def get_used_count_for_ammo_type(
%AmmoType{id: ammo_type_id, user_id: user_id},
%User{id: user_id}
) do
Repo.one!(
from ag in AmmoGroup,
left_join: sg in assoc(ag, :shot_groups),
where: ag.ammo_type_id == ^ammo_type_id,
select: sum(sg.count)
) || 0
end
@doc """ @doc """
Creates a ammo_type. Creates a ammo_type.
@ -162,10 +217,12 @@ 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()]
def list_ammo_groups_for_type(%AmmoType{id: ammo_type_id, user_id: user_id}, %User{id: user_id}) do def list_ammo_groups_for_type(%AmmoType{id: ammo_type_id, user_id: user_id}, %User{id: user_id}) do
Repo.all( Repo.all(
from am in AmmoGroup, from ag in AmmoGroup,
where: am.ammo_type_id == ^ammo_type_id, left_join: sg in assoc(ag, :shot_groups),
where: am.user_id == ^user_id, where: ag.ammo_type_id == ^ammo_type_id,
order_by: am.id where: ag.user_id == ^user_id,
preload: [shot_groups: sg],
order_by: ag.id
) )
end end
@ -182,12 +239,18 @@ defmodule Cannery.Ammo do
@spec list_ammo_groups(User.t(), include_empty :: boolean()) :: [AmmoGroup.t()] @spec list_ammo_groups(User.t(), include_empty :: boolean()) :: [AmmoGroup.t()]
def list_ammo_groups(%User{id: user_id}, include_empty \\ false) do def list_ammo_groups(%User{id: user_id}, include_empty \\ false) do
if include_empty do if include_empty do
from am in AmmoGroup, where: am.user_id == ^user_id, order_by: am.id from ag in AmmoGroup,
left_join: sg in assoc(ag, :shot_groups),
where: ag.user_id == ^user_id,
preload: [shot_groups: sg],
order_by: ag.id
else else
from am in AmmoGroup, from ag in AmmoGroup,
where: am.user_id == ^user_id, left_join: sg in assoc(ag, :shot_groups),
where: not (am.count == 0), where: ag.user_id == ^user_id,
order_by: am.id where: not (ag.count == 0),
preload: [shot_groups: sg],
order_by: ag.id
end end
|> Repo.all() |> Repo.all()
end end
@ -204,10 +267,12 @@ defmodule Cannery.Ammo do
@spec list_staged_ammo_groups(User.t()) :: [AmmoGroup.t()] @spec list_staged_ammo_groups(User.t()) :: [AmmoGroup.t()]
def list_staged_ammo_groups(%User{id: user_id}) do def list_staged_ammo_groups(%User{id: user_id}) do
Repo.all( Repo.all(
from am in AmmoGroup, from ag in AmmoGroup,
where: am.user_id == ^user_id, left_join: sg in assoc(ag, :shot_groups),
where: am.staged == true, where: ag.user_id == ^user_id,
order_by: am.id where: ag.staged == true,
preload: [shot_groups: sg],
order_by: ag.id
) )
end end
@ -226,40 +291,99 @@ defmodule Cannery.Ammo do
""" """
@spec get_ammo_group!(AmmoGroup.id(), User.t()) :: AmmoGroup.t() @spec get_ammo_group!(AmmoGroup.id(), User.t()) :: AmmoGroup.t()
def get_ammo_group!(id, %User{id: user_id}), def get_ammo_group!(id, %User{id: user_id}) do
do: Repo.one!(from am in AmmoGroup, where: am.id == ^id and am.user_id == ^user_id) Repo.one!(
from ag in AmmoGroup,
left_join: sg in assoc(ag, :shot_groups),
where: ag.id == ^id,
where: ag.user_id == ^user_id,
preload: [shot_groups: sg]
)
end
@doc """ @doc """
Creates a ammo_group. Returns the number of shot rounds for an ammo group
"""
@spec get_used_count(AmmoGroup.t()) :: non_neg_integer()
def get_used_count(%AmmoGroup{} = ammo_group) do
ammo_group
|> Repo.preload(:shot_groups)
|> Map.fetch!(:shot_groups)
|> Enum.map(fn %{count: count} -> count end)
|> Enum.sum()
end
@doc """
Calculates the percentage remaining of an ammo group out of 100
"""
@spec get_percentage_remaining(AmmoGroup.t()) :: non_neg_integer()
def get_percentage_remaining(%AmmoGroup{count: 0}), do: 0
def get_percentage_remaining(%AmmoGroup{count: count} = ammo_group) do
ammo_group = ammo_group |> Repo.preload(:shot_groups)
shot_group_sum =
ammo_group.shot_groups |> Enum.map(fn %{count: count} -> count end) |> Enum.sum()
round(count / (count + shot_group_sum) * 100)
end
@doc """
Creates multiple ammo_groups at once.
## Examples ## Examples
iex> create_ammo_group(%{field: value}, %User{id: 123}) iex> create_ammo_groups(%{field: value}, 3, %User{id: 123})
{:ok, %AmmoGroup{}} {:ok, {3, [%AmmoGroup{}]}}
iex> create_ammo_group(%{field: bad_value}, %User{id: 123}) iex> create_ammo_groups(%{field: bad_value}, 3, %User{id: 123})
{:error, %Changeset{}} {:error, %Changeset{}}
""" """
@spec create_ammo_group(attrs :: map(), User.t()) :: @spec create_ammo_groups(attrs :: map(), multiplier :: non_neg_integer(), User.t()) ::
{:ok, AmmoGroup.t()} | {:error, Changeset.t(AmmoGroup.new_ammo_group())} {:ok, {count :: non_neg_integer(), [AmmoGroup.t()] | nil}}
def create_ammo_group( | {:error, Changeset.t(AmmoGroup.new_ammo_group())}
def 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,
%User{id: user_id} = user %User{id: user_id} = user
) do )
when multiplier >= 1 and multiplier <= @ammo_group_create_limit do
# validate ammo type and container ids belong to user # validate ammo type and container ids belong to user
_valid_ammo_type = get_ammo_type!(ammo_type_id, user) _valid_ammo_type = get_ammo_type!(ammo_type_id, user)
_valid_container = Containers.get_container!(container_id, user) _valid_container = Containers.get_container!(container_id, user)
%AmmoGroup{} now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
|> AmmoGroup.create_changeset(attrs |> Map.put("user_id", user_id))
|> Repo.insert() changesets =
Enum.map(1..multiplier, fn _count ->
%AmmoGroup{} |> AmmoGroup.create_changeset(attrs |> Map.put("user_id", user_id))
end)
if changesets |> Enum.all?(fn %{valid?: valid} -> valid end) do
{count, inserted_ammo_groups} =
Repo.insert_all(
AmmoGroup,
changesets
|> Enum.map(fn changeset ->
changeset
|> Map.get(:changes)
|> Map.merge(%{inserted_at: now, updated_at: now})
end),
returning: true
)
{:ok, {count, inserted_ammo_groups}}
else
changesets
|> Enum.reject(fn %{valid?: valid} -> valid end)
|> List.first()
|> Changeset.apply_action(:insert)
end
end end
def create_ammo_group(invalid_attrs, _user) do def create_ammo_groups(invalid_attrs, _multiplier, _user) do
%AmmoGroup{} {:error, %AmmoGroup{} |> AmmoGroup.create_changeset(invalid_attrs)}
|> AmmoGroup.create_changeset(invalid_attrs |> Map.put("user_id", "-1"))
|> Repo.insert()
end end
@doc """ @doc """

View File

@ -19,8 +19,16 @@ defmodule Cannery.Containers do
""" """
@spec list_containers(User.t()) :: [Container.t()] @spec list_containers(User.t()) :: [Container.t()]
def list_containers(%User{id: user_id}), def list_containers(%User{id: user_id}) do
do: Repo.all(from c in Container, where: c.user_id == ^user_id, order_by: c.name) Repo.all(
from c in Container,
left_join: t in assoc(c, :tags),
left_join: ag in assoc(c, :ammo_groups),
where: c.user_id == ^user_id,
order_by: c.name,
preload: [tags: t, ammo_groups: ag]
)
end
@doc """ @doc """
Gets a single container. Gets a single container.
@ -37,8 +45,17 @@ defmodule Cannery.Containers do
""" """
@spec get_container!(Container.id(), User.t()) :: Container.t() @spec get_container!(Container.id(), User.t()) :: Container.t()
def get_container!(id, %User{id: user_id}), def get_container!(id, %User{id: user_id}) do
do: Repo.one!(from c in Container, where: c.id == ^id and c.user_id == ^user_id) Repo.one!(
from c in Container,
left_join: t in assoc(c, :tags),
left_join: ag in assoc(c, :ammo_groups),
where: c.user_id == ^user_id,
where: c.id == ^id,
order_by: c.name,
preload: [tags: t, ammo_groups: ag]
)
end
@doc """ @doc """
Creates a container. Creates a container.
@ -189,4 +206,17 @@ defmodule Cannery.Containers do
if count == 0, do: raise("could not delete container tag"), else: count if count == 0, do: raise("could not delete container tag"), else: count
end end
@doc """
Returns number of rounds in container. If data is already preloaded, then
there will be no db hit.
"""
@spec get_container_rounds!(Container.t()) :: non_neg_integer()
def get_container_rounds!(%Container{} = container) do
container
|> Repo.preload(:ammo_groups)
|> Map.fetch!(:ammo_groups)
|> Enum.map(fn %{count: count} -> count end)
|> Enum.sum()
end
end end

View File

@ -9,7 +9,7 @@ defmodule Cannery.Release do
def rollback(repo, version) do def rollback(repo, version) do
load_app() load_app()
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :down, to: version)) {:ok, _fun, _opts} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :down, to: version))
end end
defp load_app do defp load_app do
@ -20,7 +20,7 @@ defmodule Cannery.Release do
load_app() load_app()
for repo <- Application.fetch_env!(@app, :ecto_repos) do for repo <- Application.fetch_env!(@app, :ecto_repos) do
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true)) {:ok, _fun, _opts} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true))
end end
end end
end end

View File

@ -6,11 +6,11 @@ defmodule Cannery.Repo.Migrator do
use GenServer use GenServer
require Logger require Logger
def start_link(_) do def start_link(_opts) do
GenServer.start_link(__MODULE__, [], []) GenServer.start_link(__MODULE__, [], [])
end end
def init(_) do def init(_opts) do
migrate!() migrate!()
{:ok, nil} {:ok, nil}
end end

View File

@ -71,6 +71,7 @@ defmodule CanneryWeb do
quote do quote do
use Phoenix.Router use Phoenix.Router
# credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
import Plug.Conn import Plug.Conn
import Phoenix.Controller import Phoenix.Controller
import Phoenix.LiveView.Router import Phoenix.LiveView.Router
@ -79,7 +80,9 @@ defmodule CanneryWeb do
def channel do def channel do
quote do quote do
# credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
use Phoenix.Channel use Phoenix.Channel
# credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
import CanneryWeb.Gettext import CanneryWeb.Gettext
end end
end end
@ -87,14 +90,18 @@ defmodule CanneryWeb do
defp view_helpers do defp view_helpers do
quote do quote do
# Use all HTML functionality (forms, tags, etc) # Use all HTML functionality (forms, tags, etc)
# credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
use Phoenix.HTML use Phoenix.HTML
# Import LiveView and .heex helpers (live_render, live_patch, <.form>, etc) # Import LiveView and .heex helpers (live_render, live_patch, <.form>, etc)
# credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
import Phoenix.LiveView.Helpers import Phoenix.LiveView.Helpers
# Import basic rendering functionality (render, render_layout, etc) # Import basic rendering functionality (render, render_layout, etc)
# credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
import Phoenix.View import Phoenix.View
# credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
import CanneryWeb.{ErrorHelpers, Gettext, LiveHelpers, ViewHelpers} import CanneryWeb.{ErrorHelpers, Gettext, LiveHelpers, ViewHelpers}
alias CanneryWeb.Router.Helpers, as: Routes alias CanneryWeb.Router.Helpers, as: Routes
end end

View File

@ -7,7 +7,7 @@
let={f} let={f}
for={@changeset} for={@changeset}
id="shot-group-form" id="shot-group-form"
class="flex flex-col sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center" class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
phx-target={@myself} phx-target={@myself}
phx-change="validate" phx-change="validate"
phx-submit="save" phx-submit="save"
@ -36,7 +36,11 @@
<%= error_tag(f, :notes, "col-span-3") %> <%= error_tag(f, :notes, "col-span-3") %>
<%= label(f, :date, gettext("Date (UTC)"), class: "title text-lg text-primary-600") %> <%= label(f, :date, gettext("Date (UTC)"), class: "title text-lg text-primary-600") %>
<%= date_input(f, :date, class: "input input-primary col-span-2") %> <%= date_input(f, :date,
class: "input input-primary col-span-2",
phx_update: "ignore",
value: Date.utc_today()
) %>
<%= error_tag(f, :notes, "col-span-3") %> <%= error_tag(f, :notes, "col-span-3") %>
<%= submit(dgettext("actions", "Save"), <%= submit(dgettext("actions", "Save"),

View File

@ -4,17 +4,21 @@ defmodule CanneryWeb.Components.ContainerCard do
""" """
use CanneryWeb, :component use CanneryWeb, :component
import CanneryWeb.Components.TagCard
alias Cannery.{Containers, Repo}
alias CanneryWeb.Endpoint alias CanneryWeb.Endpoint
def container_card(assigns) do def container_card(%{container: container} = assigns) do
assigns = assigns |> Map.put(:container, container |> Repo.preload([:tags, :ammo_groups]))
~H""" ~H"""
<div <div
id={"container-#{@container.id}"} id={"container-#{@container.id}"}
class="mx-4 my-2 px-8 py-4 flex flex-col justify-center items-center class="overflow-hidden max-w-full mx-4 my-2 px-8 py-4 flex flex-col justify-center items-center space-y-4
border border-gray-400 rounded-lg shadow-lg hover:shadow-md border border-gray-400 rounded-lg shadow-lg hover:shadow-md
transition-all duration-300 ease-in-out" transition-all duration-300 ease-in-out"
> >
<div class="mb-4 flex flex-col justify-center items-center"> <div class="max-w-full mb-4 flex flex-col justify-center items-center space-y-2">
<%= live_redirect to: Routes.container_show_path(Endpoint, :show, @container), <%= live_redirect to: Routes.container_show_path(Endpoint, :show, @container),
class: "link" do %> class: "link" do %>
<h1 class="px-4 py-2 rounded-lg title text-xl"> <h1 class="px-4 py-2 rounded-lg title text-xl">
@ -40,6 +44,25 @@ defmodule CanneryWeb.Components.ContainerCard do
<%= @container.location %> <%= @container.location %>
</span> </span>
<% end %> <% end %>
<%= if @container.ammo_groups do %>
<span class="rounded-lg title text-lg">
<%= gettext("Rounds:") %>
<%= @container |> Containers.get_container_rounds!() %>
</span>
<% end %>
<div class="flex flex-wrap justify-center items-center">
<%= unless @container.tags |> Enum.empty?() do %>
<%= for tag <- @container.tags do %>
<.simple_tag_card tag={tag} />
<% end %>
<% end %>
<%= if assigns |> Map.has_key?(:tag_actions) do %>
<%= render_slot(@tag_actions) %>
<% end %>
</div>
</div> </div>
<%= if assigns |> Map.has_key?(:inner_block) do %> <%= if assigns |> Map.has_key?(:inner_block) do %>

View File

@ -4,7 +4,8 @@ defmodule CanneryWeb.Components.MoveAmmoGroupComponent do
""" """
use CanneryWeb, :live_component use CanneryWeb, :live_component
alias Cannery.{Accounts.User, Ammo, Ammo.AmmoGroup, Containers} alias Cannery.{Accounts.User, Ammo, Ammo.AmmoGroup, Containers, Containers.Container}
alias CanneryWeb.Endpoint
alias Phoenix.LiveView.Socket alias Phoenix.LiveView.Socket
@impl true @impl true
@ -27,7 +28,12 @@ defmodule CanneryWeb.Components.MoveAmmoGroupComponent do
Containers.list_containers(current_user) Containers.list_containers(current_user)
|> Enum.reject(fn %{id: id} -> id == container_id end) |> Enum.reject(fn %{id: id} -> id == container_id end)
{:ok, socket |> assign(assigns) |> assign(changeset: changeset, containers: containers)} socket =
socket
|> assign(assigns)
|> assign(changeset: changeset, containers: containers)
{:ok, socket}
end end
@impl true @impl true
@ -54,4 +60,76 @@ defmodule CanneryWeb.Components.MoveAmmoGroupComponent do
{:noreply, socket} {:noreply, socket}
end end
@impl true
def render(%{containers: containers} = assigns) do
columns = [
%{label: gettext("Container"), key: "name"},
%{label: gettext("Type"), key: "type"},
%{label: gettext("Location"), key: "location"},
%{label: nil, key: "actions", sortable: false}
]
rows = containers |> get_rows_for_containers(assigns, columns)
assigns = assigns |> Map.merge(%{columns: columns, rows: rows})
~H"""
<div class="w-full flex flex-col space-y-8 justify-center items-center">
<h2 class="mb-8 text-center title text-xl text-primary-600">
<%= gettext("Move ammo") %>
</h2>
<%= if @containers |> Enum.empty?() do %>
<h2 class="title text-xl text-primary-600">
<%= gettext("No other containers") %>
<%= display_emoji("😔") %>
</h2>
<%= live_patch(dgettext("actions", "Add another container!"),
to: Routes.container_index_path(Endpoint, :new),
class: "btn btn-primary"
) %>
<% else %>
<.live_component
module={CanneryWeb.Components.TableComponent}
id="move_ammo_group_table"
columns={@columns}
rows={@rows}
/>
<% end %>
</div>
"""
end
@spec get_rows_for_containers([Container.t()], map(), [map()]) :: [map()]
defp get_rows_for_containers(containers, assigns, columns) do
containers
|> Enum.map(fn container ->
columns
|> Enum.into(%{}, fn %{key: key} -> {key, get_row_value_by_key(key, container, assigns)} end)
end)
end
@spec get_row_value_by_key(String.t(), Container.t(), map()) :: any()
defp get_row_value_by_key("actions", container, assigns) do
assigns = assigns |> Map.put(:container, container)
~H"""
<div class="px-4 py-2 space-x-4 flex justify-center items-center">
<button
type="button"
class="btn btn-primary"
phx-click="move"
phx-target={@myself}
phx-value-container_id={container.id}
>
<%= dgettext("actions", "Select") %>
</button>
</div>
"""
end
defp get_row_value_by_key(key, container, _assigns),
do: container |> Map.get(key |> String.to_existing_atom())
end end

View File

@ -1,70 +0,0 @@
<div class="w-full flex flex-col space-y-8 justify-center items-center">
<h2 class="mb-8 text-center title text-xl text-primary-600">
<%= gettext("Move ammo") %>
</h2>
<%= if @containers |> Enum.empty?() do %>
<h2 class="title text-xl text-primary-600">
<%= gettext("No other containers") %>
<%= display_emoji("😔") %>
</h2>
<%= live_patch(dgettext("actions", "Add another container!"),
to: Routes.container_index_path(Endpoint, :new),
class: "btn btn-primary"
) %>
<% else %>
<div class="w-full overflow-x-auto border border-gray-600 rounded-lg shadow-lg bg-black">
<table class="min-w-full table-auto text-center bg-white">
<thead class="border-b border-primary-600">
<tr>
<th class="p-2">
<%= gettext("Container") %>
</th>
<th class="p-2">
<%= gettext("Type") %>
</th>
<th class="p-2">
<%= gettext("Location") %>
</th>
<th class="p-2"></th>
</tr>
</thead>
<tbody id="containers">
<%= for container <- @containers do %>
<tr id={"container-#{container.id}"}>
<td class="p-2">
<%= container.name %>
</td>
<td class="p-2">
<%= container.type %>
</td>
<td class="p-2">
<%= container.location %>
</td>
<td class="p-2">
<div class="px-4 py-2 space-x-4 flex justify-center items-center">
<button
type="button"
class="btn btn-primary"
phx-click="move"
phx-target={@myself}
phx-value-container_id={container.id}
>
<%= dgettext("actions", "Select") %>
</button>
</div>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% end %>
</div>

View File

@ -0,0 +1,87 @@
defmodule CanneryWeb.Components.TableComponent do
@moduledoc """
Livecomponent that presents a resortable table
It takes the following required assigns:
- `:columns`: An array of maps containing the following keys
- `:label`: A gettext'd or otherwise user-facing string label for the
column. Can be nil
- `:key`: A string key used for sorting
- `:class`: Extra classes to be applied to the column element, if desired.
Optional
- `:sortable`: If false, will prevent the user from sorting with it.
Optional
- `:values`: An array of maps containing data for each row. Each map is
string-keyed with the associated column key to the following values:
- A single element, like string, integer or Phoenix.LiveView.Rendered
object, like returned from the ~H sigil
- A tuple, containing a custom value used for sorting, and the displayed
content.
"""
use CanneryWeb, :live_component
alias Phoenix.LiveView.Socket
@impl true
@spec update(
%{
required(:columns) =>
list(%{
required(:label) => String.t() | nil,
required(:key) => String.t() | nil,
optional(:class) => String.t(),
optional(:sortable) => false
}),
required(:rows) =>
list(%{
(key :: String.t()) => any() | {custom_sort_value :: String.t(), value :: any()}
}),
optional(any()) => any()
},
Socket.t()
) :: {:ok, Socket.t()}
def update(%{columns: columns, rows: rows} = assigns, socket) do
initial_key = columns |> List.first() |> Map.get(:key)
rows = rows |> Enum.sort_by(fn row -> row |> Map.get(initial_key) end, :asc)
socket =
socket
|> assign(assigns)
|> assign(columns: columns, rows: rows, last_sort_key: initial_key, sort_mode: :asc)
{:ok, socket}
end
@impl true
def handle_event(
"sort_by",
%{"sort-key" => key},
%{assigns: %{rows: rows, last_sort_key: key, sort_mode: sort_mode}} = socket
) do
sort_mode = if sort_mode == :asc, do: :desc, else: :asc
rows = rows |> sort_by_custom_sort_value_or_value(key, sort_mode)
{:noreply, socket |> assign(sort_mode: sort_mode, rows: rows)}
end
def handle_event(
"sort_by",
%{"sort-key" => key},
%{assigns: %{rows: rows}} = socket
) do
rows = rows |> sort_by_custom_sort_value_or_value(key, :asc)
{:noreply, socket |> assign(last_sort_key: key, sort_mode: :asc, rows: rows)}
end
defp sort_by_custom_sort_value_or_value(rows, key, sort_mode) do
rows
|> Enum.sort_by(
fn row ->
case row |> Map.get(key) do
{custom_sort_key, _value} -> custom_sort_key
value -> value
end
end,
sort_mode
)
end
end

View File

@ -0,0 +1,52 @@
<div class="w-full overflow-x-auto border border-gray-600 rounded-lg shadow-lg bg-black">
<table class="min-w-full table-auto text-center bg-white">
<thead class="border-b border-primary-600">
<tr>
<%= for %{key: key, label: label} = column <- @columns do %>
<%= if column |> Map.get(:sortable, true) do %>
<th class={"p-2 #{column[:class]}"}>
<span
class="cursor-pointer"
phx-click="sort_by"
phx-value-sort-key={key}
phx-target={@myself}
>
<span class="underline"><%= label %></span>
<%= if @last_sort_key == key do %>
<%= case @sort_mode do %>
<% :asc -> %>
<i class="fas fa-sm fa-chevron-down"></i>
<% :desc -> %>
<i class="fas fa-sm fa-chevron-up"></i>
<% end %>
<% else %>
<i class="fas fa-sm fa-chevron-up opacity-0"></i>
<% end %>
</span>
</th>
<% else %>
<th class={"p-2 #{column[:class]}"}>
<%= label %>
</th>
<% end %>
<% end %>
</tr>
</thead>
<tbody>
<%= for values <- @rows do %>
<tr>
<%= for %{key: key} = value <- @columns do %>
<td class={"p-2 #{value[:class]}"}>
<%= case values |> Map.get(key) do %>
<% {_custom_sort_value, value} -> %>
<%= value %>
<% value -> %>
<%= value %>
<% end %>
</td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
</div>

View File

@ -13,15 +13,20 @@ defmodule CanneryWeb.Components.TagCard do
border border-gray-400 rounded-lg shadow-lg hover:shadow-md border border-gray-400 rounded-lg shadow-lg hover:shadow-md
transition-all duration-300 ease-in-out" transition-all duration-300 ease-in-out"
> >
<h1 <.simple_tag_card tag={@tag} />
class="px-4 py-2 rounded-lg title text-xl"
style={"color: #{@tag.text_color}; background-color: #{@tag.bg_color}"}
>
<%= @tag.name %>
</h1>
<%= render_slot(@inner_block) %> <%= render_slot(@inner_block) %>
</div> </div>
""" """
end end
def simple_tag_card(assigns) do
~H"""
<h1
class="inline-block break-all mx-2 my-1 px-4 py-2 rounded-lg title text-xl"
style={"color: #{@tag.text_color}; background-color: #{@tag.bg_color}"}
>
<%= @tag.name %>
</h1>
"""
end
end end

View File

@ -9,7 +9,7 @@ defmodule CanneryWeb.Components.UserCard do
~H""" ~H"""
<div <div
id={"user-#{@user.id}"} id={"user-#{@user.id}"}
class="mx-4 my-2 px-8 py-4 flex flex-col justify-center items-center class="mx-4 my-2 px-8 py-4 flex flex-col justify-center items-center text-center
border border-gray-400 rounded-lg shadow-lg hover:shadow-md border border-gray-400 rounded-lg shadow-lg hover:shadow-md
transition-all duration-300 ease-in-out" transition-all duration-300 ease-in-out"
> >
@ -21,7 +21,8 @@ defmodule CanneryWeb.Components.UserCard do
<%= if @user.confirmed_at |> is_nil() do %> <%= if @user.confirmed_at |> is_nil() do %>
Email unconfirmed Email unconfirmed
<% else %> <% else %>
User was confirmed at<%= @user.confirmed_at |> display_datetime() %> <p>User was confirmed at</p>
<%= @user.confirmed_at |> display_datetime() %>
<% end %> <% end %>
</h3> </h3>

View File

@ -1,10 +1,11 @@
defmodule CanneryWeb.UserConfirmationController do defmodule CanneryWeb.UserConfirmationController do
use CanneryWeb, :controller use CanneryWeb, :controller
import CanneryWeb.Gettext
alias Cannery.Accounts alias Cannery.Accounts
def new(conn, _params) do def new(conn, _params) do
render(conn, "new.html") render(conn, "new.html", page_title: gettext("Confirm your account"))
end end
def create(conn, %{"user" => %{"email" => email}}) do def create(conn, %{"user" => %{"email" => email}}) do

View File

@ -29,8 +29,11 @@ defmodule CanneryWeb.UserRegistrationController do
# renders new user registration page # renders new user registration page
defp render_new(conn, invite \\ nil) do defp render_new(conn, invite \\ nil) do
changeset = Accounts.change_user_registration(%User{}) render(conn, "new.html",
conn |> render("new.html", changeset: changeset, invite: invite) changeset: Accounts.change_user_registration(%User{}),
invite: invite,
page_title: gettext("Register")
)
end end
def create(conn, %{"user" => %{"invite_token" => invite_token}} = attrs) do def create(conn, %{"user" => %{"invite_token" => invite_token}} = attrs) do

View File

@ -6,7 +6,7 @@ defmodule CanneryWeb.UserResetPasswordController do
plug :get_user_by_reset_password_token when action in [:edit, :update] plug :get_user_by_reset_password_token when action in [:edit, :update]
def new(conn, _params) do def new(conn, _params) do
render(conn, "new.html") render(conn, "new.html", page_title: gettext("Forgot your password?"))
end end
def create(conn, %{"user" => %{"email" => email}}) do def create(conn, %{"user" => %{"email" => email}}) do
@ -31,7 +31,10 @@ defmodule CanneryWeb.UserResetPasswordController do
end end
def edit(conn, _params) do def edit(conn, _params) do
render(conn, "edit.html", changeset: Accounts.change_user_password(conn.assigns.user)) render(conn, "edit.html",
changeset: Accounts.change_user_password(conn.assigns.user),
page_title: gettext("Reset your password")
)
end end
# Do not log in the user after reset password to avoid a # Do not log in the user after reset password to avoid a

View File

@ -5,7 +5,7 @@ defmodule CanneryWeb.UserSessionController do
alias CanneryWeb.UserAuth alias CanneryWeb.UserAuth
def new(conn, _params) do def new(conn, _params) do
render(conn, "new.html", error_message: nil) render(conn, "new.html", error_message: nil, page_title: gettext("Log in"))
end end
def create(conn, %{"user" => user_params}) do def create(conn, %{"user" => user_params}) do

View File

@ -1,13 +1,13 @@
defmodule CanneryWeb.UserSettingsController do defmodule CanneryWeb.UserSettingsController do
use CanneryWeb, :controller use CanneryWeb, :controller
import CanneryWeb.Gettext
alias Cannery.Accounts alias Cannery.Accounts
alias CanneryWeb.{HomeLive, UserAuth} alias CanneryWeb.{HomeLive, UserAuth}
plug :assign_email_and_password_changesets plug :assign_email_and_password_changesets
def edit(conn, _params) do def edit(conn, _params) do
render(conn, "edit.html") render(conn, "edit.html", page_title: gettext("Settings"))
end end
def update(conn, %{"action" => "update_email"} = params) do def update(conn, %{"action" => "update_email"} = params) do

View File

@ -9,6 +9,8 @@ defmodule CanneryWeb.AmmoGroupLive.FormComponent do
alias Ecto.Changeset alias Ecto.Changeset
alias Phoenix.LiveView.Socket alias Phoenix.LiveView.Socket
@ammo_group_create_limit 10_000
@impl true @impl true
@spec update( @spec update(
%{:ammo_group => AmmoGroup.t(), :current_user => User.t(), optional(any) => any}, %{:ammo_group => AmmoGroup.t(), :current_user => User.t(), optional(any) => any},
@ -20,20 +22,37 @@ defmodule CanneryWeb.AmmoGroupLive.FormComponent do
@spec update(Socket.t()) :: {:ok, Socket.t()} @spec update(Socket.t()) :: {:ok, Socket.t()}
def update(%{assigns: %{ammo_group: ammo_group, current_user: current_user}} = socket) do def update(%{assigns: %{ammo_group: ammo_group, current_user: current_user}} = socket) do
changeset = Ammo.change_ammo_group(ammo_group) socket =
containers = Containers.list_containers(current_user) socket
ammo_types = Ammo.list_ammo_types(current_user) |> assign(:ammo_group_create_limit, @ammo_group_create_limit)
{:ok, socket |> assign(changeset: changeset, containers: containers, ammo_types: ammo_types)} |> assign(:changeset, Ammo.change_ammo_group(ammo_group))
|> assign(:ammo_types, Ammo.list_ammo_types(current_user))
|> assign_new(:containers, fn -> Containers.list_containers(current_user) end)
{:ok, socket}
end end
@impl true @impl true
def handle_event( def handle_event(
"validate", "validate",
%{"ammo_group" => ammo_group_params}, %{"ammo_group" => ammo_group_params},
%{assigns: %{ammo_group: ammo_group}} = socket %{assigns: %{action: action, ammo_group: ammo_group}} = socket
) do ) do
socket = socket |> assign(:changeset, ammo_group |> Ammo.change_ammo_group(ammo_group_params)) changeset_action =
{:noreply, socket} case action do
:new -> :insert
:edit -> :update
end
changeset = ammo_group |> Ammo.change_ammo_group(ammo_group_params)
changeset =
case changeset |> Changeset.apply_action(changeset_action) do
{:ok, _data} -> changeset
{:error, changeset} -> changeset
end
{:noreply, socket |> assign(:changeset, changeset)}
end end
def handle_event( def handle_event(
@ -77,20 +96,65 @@ defmodule CanneryWeb.AmmoGroupLive.FormComponent do
end end
defp save_ammo_group( defp save_ammo_group(
%{assigns: %{current_user: current_user, return_to: return_to}} = socket, %{assigns: %{changeset: changeset}} = socket,
:new, :new,
ammo_group_params %{"multiplier" => multiplier_str} = ammo_group_params
) do ) do
socket = socket =
case Ammo.create_ammo_group(ammo_group_params, current_user) do case multiplier_str |> Integer.parse() do
{:ok, _ammo_group} -> {multiplier, _remainder}
prompt = dgettext("prompts", "Ammo group created successfully") when multiplier >= 1 and multiplier <= @ammo_group_create_limit ->
socket |> create_multiple(ammo_group_params, multiplier)
{multiplier, _remainder} ->
error_msg =
dgettext(
"errors",
"Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}",
max: @ammo_group_create_limit,
multiplier: multiplier
)
{:error, changeset} =
changeset
|> Changeset.add_error(:multiplier, error_msg)
|> Changeset.apply_action(:insert)
socket |> assign(:changeset, changeset)
:error ->
error_msg = dgettext("errors", "Could not parse number of copies")
{:error, changeset} =
changeset
|> Changeset.add_error(:multiplier, error_msg)
|> Changeset.apply_action(:insert)
socket |> assign(:changeset, changeset)
end
{:noreply, socket}
end
defp create_multiple(
%{assigns: %{current_user: current_user, return_to: return_to}} = socket,
ammo_group_params,
multiplier
) do
case Ammo.create_ammo_groups(ammo_group_params, multiplier, current_user) do
{:ok, {count, _ammo_groups}} ->
prompt =
dngettext(
"prompts",
"Ammo group created successfully",
"Ammo groups created successfully",
count
)
socket |> put_flash(:info, prompt) |> push_redirect(to: return_to) socket |> put_flash(:info, prompt) |> push_redirect(to: return_to)
{:error, %Changeset{} = changeset} -> {:error, %Changeset{} = changeset} ->
socket |> assign(changeset: changeset) socket |> assign(changeset: changeset)
end end
{:noreply, socket}
end end
end end

View File

@ -10,7 +10,7 @@
phx-target={@myself} phx-target={@myself}
phx-change="validate" phx-change="validate"
phx-submit="save" phx-submit="save"
class="flex flex-col sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center" class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
> >
<%= if @changeset.action && not @changeset.valid? do %> <%= if @changeset.action && not @changeset.valid? do %>
<div class="invalid-feedback col-span-3 text-center"> <div class="invalid-feedback col-span-3 text-center">
@ -18,42 +18,62 @@
</div> </div>
<% end %> <% end %>
<%= label(f, :ammo_type_id, gettext("Ammo type"), class: "mr-4 title text-lg text-primary-600") %> <%= label(f, :ammo_type_id, gettext("Ammo type"), class: "title text-lg text-primary-600") %>
<%= select(f, :ammo_type_id, ammo_type_options(@ammo_types), <%= select(f, :ammo_type_id, ammo_type_options(@ammo_types),
class: "text-center col-span-2 input input-primary" class: "text-center col-span-2 input input-primary"
) %> ) %>
<%= error_tag(f, :ammo_type_id, "col-span-3 text-center") %> <%= error_tag(f, :ammo_type_id, "col-span-3 text-center") %>
<%= label(f, :count, gettext("Count"), class: "mr-4 title text-lg text-primary-600") %> <%= label(f, :count, gettext("Count"), class: "title text-lg text-primary-600") %>
<%= number_input(f, :count, <%= number_input(f, :count,
class: "text-center col-span-2 input input-primary", class: "text-center col-span-2 input input-primary",
min: 1 min: 1
) %> ) %>
<%= error_tag(f, :count, "col-span-3 text-center") %> <%= error_tag(f, :count, "col-span-3 text-center") %>
<%= label(f, :price_paid, gettext("Price paid"), class: "mr-4 title text-lg text-primary-600") %> <%= label(f, :price_paid, gettext("Price paid"), class: "title text-lg text-primary-600") %>
<%= number_input(f, :price_paid, <%= number_input(f, :price_paid,
step: "0.01", step: 0.01,
class: "text-center col-span-2 input input-primary" class: "text-center col-span-2 input input-primary"
) %> ) %>
<%= error_tag(f, :price_paid, "col-span-3 text-center") %> <%= error_tag(f, :price_paid, "col-span-3 text-center") %>
<%= label(f, :notes, gettext("Notes"), class: "mr-4 title text-lg text-primary-600") %> <%= label(f, :notes, gettext("Notes"), class: "title text-lg text-primary-600") %>
<%= textarea(f, :notes, <%= textarea(f, :notes,
class: "text-center col-span-2 input input-primary", class: "text-center col-span-2 input input-primary",
phx_hook: "MaintainAttrs" phx_hook: "MaintainAttrs"
) %> ) %>
<%= error_tag(f, :notes, "col-span-3 text-center") %> <%= error_tag(f, :notes, "col-span-3 text-center") %>
<%= label(f, :container, gettext("Container"), class: "mr-4 title text-lg text-primary-600") %> <%= label(f, :container, gettext("Container"), class: "title text-lg text-primary-600") %>
<%= select(f, :container_id, container_options(@containers), <%= select(f, :container_id, container_options(@containers),
class: "text-center col-span-2 input input-primary" class: "text-center col-span-2 input input-primary"
) %> ) %>
<%= error_tag(f, :container_id, "col-span-3 text-center") %> <%= error_tag(f, :container_id, "col-span-3 text-center") %>
<%= case @action do %>
<% :new -> %>
<hr class="hr col-span-3" />
<%= label(f, :multiplier, gettext("Copies"), class: "title text-lg text-primary-600") %>
<%= number_input(f, :multiplier,
max: @ammo_group_create_limit,
class: "text-center input input-primary",
value: 1,
phx_update: "ignore"
) %>
<%= submit(dgettext("actions", "Create"),
phx_disable_with: dgettext("prompts", "Creating..."),
class: "mx-auto btn btn-primary"
) %>
<%= error_tag(f, :multiplier, "col-span-3 text-center") %>
<% :edit -> %>
<%= submit(dgettext("actions", "Save"), <%= submit(dgettext("actions", "Save"),
phx_disable_with: dgettext("prompts", "Saving..."), phx_disable_with: dgettext("prompts", "Saving..."),
class: "mx-auto col-span-3 btn btn-primary" class: "mx-auto col-span-3 btn btn-primary"
) %> ) %>
<% end %>
</.form> </.form>
</div> </div>

View File

@ -4,7 +4,7 @@ defmodule CanneryWeb.AmmoGroupLive.Index do
""" """
use CanneryWeb, :live_view use CanneryWeb, :live_view
alias Cannery.{Ammo, Ammo.AmmoGroup, Repo} alias Cannery.{Ammo, Ammo.AmmoGroup, Containers, Repo}
alias CanneryWeb.Endpoint alias CanneryWeb.Endpoint
@impl true @impl true
@ -17,9 +17,11 @@ defmodule CanneryWeb.AmmoGroupLive.Index do
{:noreply, apply_action(socket, live_action, params)} {:noreply, apply_action(socket, live_action, params)}
end end
defp apply_action(%{assigns: %{current_user: current_user}} = socket, :add_shot_group, %{ defp apply_action(
"id" => id %{assigns: %{current_user: current_user}} = socket,
}) do :add_shot_group,
%{"id" => id}
) do
socket socket
|> assign(:page_title, gettext("Record shots")) |> assign(:page_title, gettext("Record shots"))
|> assign(:ammo_group, Ammo.get_ammo_group!(id, current_user)) |> assign(:ammo_group, Ammo.get_ammo_group!(id, current_user))
@ -72,6 +74,121 @@ defmodule CanneryWeb.AmmoGroupLive.Index do
defp display_ammo_groups(%{assigns: %{current_user: current_user}} = socket) do defp display_ammo_groups(%{assigns: %{current_user: current_user}} = socket) do
ammo_groups = Ammo.list_ammo_groups(current_user) |> Repo.preload([:ammo_type, :container]) ammo_groups = Ammo.list_ammo_groups(current_user) |> Repo.preload([:ammo_type, :container])
socket |> assign(:ammo_groups, ammo_groups) containers = Containers.list_containers(current_user)
columns = [
%{label: gettext("Ammo type"), key: "ammo_type"},
%{label: gettext("Count"), key: "count"},
%{label: gettext("Price paid"), key: "price_paid"},
%{label: gettext("% left"), key: "remaining"},
%{label: gettext("Range"), key: "range"},
%{label: gettext("Container"), key: "container"},
%{label: nil, key: "actions", sortable: false}
]
rows =
ammo_groups
|> Enum.map(fn ammo_group -> ammo_group |> get_row_data_for_ammo_group(columns) end)
socket
|> assign(ammo_groups: ammo_groups, containers: containers, columns: columns, rows: rows)
end end
@spec get_row_data_for_ammo_group(AmmoGroup.t(), [map()]) :: [map()]
defp get_row_data_for_ammo_group(ammo_group, columns) do
ammo_group = ammo_group |> Repo.preload([:ammo_type, :container])
columns
|> Enum.into(%{}, fn %{key: key} -> {key, get_value_for_key(key, ammo_group)} end)
end
@spec get_value_for_key(String.t(), AmmoGroup.t()) :: any()
defp get_value_for_key("ammo_type", %{ammo_type: ammo_type}) do
{ammo_type.name,
live_patch(ammo_type.name,
to: Routes.ammo_type_show_path(Endpoint, :show, ammo_type),
class: "link"
)}
end
defp get_value_for_key("price_paid", %{price_paid: nil}), do: {"a", nil}
defp get_value_for_key("price_paid", %{price_paid: price_paid}),
do: gettext("$%{amount}", amount: price_paid |> :erlang.float_to_binary(decimals: 2))
defp get_value_for_key("range", %{staged: staged} = ammo_group) do
assigns = %{ammo_group: ammo_group}
{staged,
~H"""
<div class="min-w-20 py-2 px-4 h-full flex flex-col justify-center items-center">
<button
type="button"
class="mx-2 my-1 btn btn-primary"
phx-click="toggle_staged"
phx-value-ammo_group_id={ammo_group.id}
>
<%= if ammo_group.staged, do: gettext("Unstage"), else: gettext("Stage") %>
</button>
<%= live_patch(dgettext("actions", "Record shots"),
to: Routes.ammo_group_index_path(Endpoint, :add_shot_group, ammo_group),
class: "mx-2 my-1 btn btn-primary"
) %>
</div>
"""}
end
defp get_value_for_key("remaining", ammo_group),
do: "#{ammo_group |> Ammo.get_percentage_remaining()}%"
defp get_value_for_key("actions", ammo_group) do
assigns = %{ammo_group: ammo_group}
~H"""
<div class="py-2 px-4 h-full space-x-4 flex justify-center items-center">
<%= live_redirect to: Routes.ammo_group_show_path(Endpoint, :show, ammo_group),
class: "text-primary-600 link",
data: [qa: "view-#{ammo_group.id}"] do %>
<i class="fa-fw fa-lg fas fa-eye"></i>
<% end %>
<%= live_patch to: Routes.ammo_group_index_path(Endpoint, :edit, ammo_group),
class: "text-primary-600 link",
data: [qa: "edit-#{ammo_group.id}"] do %>
<i class="fa-fw fa-lg fas fa-edit"></i>
<% end %>
<%= link to: "#",
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?"),
qa: "delete-#{ammo_group.id}"
] do %>
<i class="fa-fw fa-lg fas fa-trash"></i>
<% end %>
</div>
"""
end
defp get_value_for_key("container", %{container: nil}), do: {nil, nil}
defp get_value_for_key("container", %{container: %{name: container_name}} = ammo_group) do
assigns = %{ammo_group: ammo_group}
{container_name,
~H"""
<div class="min-w-20 py-2 px-4 h-full space-x-4 flex justify-center items-center">
<%= live_patch(@ammo_group.container.name,
to: Routes.ammo_group_index_path(Endpoint, :move, @ammo_group),
class: "btn btn-primary"
) %>
</div>
"""}
end
defp get_value_for_key(key, ammo_group),
do: ammo_group |> Map.get(key |> String.to_existing_atom())
end end

View File

@ -9,126 +9,49 @@
<%= display_emoji("😔") %> <%= display_emoji("😔") %>
</h2> </h2>
<%= if @containers |> Enum.empty?() do %>
<div class="flex justify-center items-center">
<h2 class="m-2 title text-md text-primary-600">
<%= dgettext("prompts", "You'll need to") %>
</h2>
<%= live_patch(dgettext("actions", "add a container first"),
to: Routes.container_index_path(Endpoint, :new),
class: "btn btn-primary"
) %>
</div>
<% else %>
<%= live_patch(dgettext("actions", "Add your first box!"), <%= live_patch(dgettext("actions", "Add your first box!"),
to: Routes.ammo_group_index_path(Endpoint, :new), to: Routes.ammo_group_index_path(Endpoint, :new),
class: "btn btn-primary" class: "btn btn-primary"
) %> ) %>
<% end %>
<% else %>
<%= if @containers |> Enum.empty?() do %>
<div class="flex justify-center items-center">
<h2 class="m-2 title text-md text-primary-600">
<%= dgettext("prompts", "You'll need to") %>
</h2>
<%= live_patch(dgettext("actions", "add a container first"),
to: Routes.container_index_path(Endpoint, :new),
class: "btn btn-primary"
) %>
</div>
<% else %> <% else %>
<%= live_patch(dgettext("actions", "New Ammo group"), <%= live_patch(dgettext("actions", "New Ammo group"),
to: Routes.ammo_group_index_path(Endpoint, :new), to: Routes.ammo_group_index_path(Endpoint, :new),
class: "btn btn-primary" class: "btn btn-primary"
) %> ) %>
<div class="w-full overflow-x-auto border border-gray-600 rounded-lg shadow-lg bg-black">
<table class="min-w-full table-auto text-center bg-white">
<thead class="border-b border-primary-600">
<tr>
<th class="p-2">
<%= gettext("Ammo type") %>
</th>
<th class="p-2">
<%= gettext("Count") %>
</th>
<th class="p-2">
<%= gettext("Price paid") %>
</th>
<th class="p-2">
<%= gettext("Notes") %>
</th>
<th class="p-2">
<%= gettext("Range") %>
</th>
<th class="p-2">
<%= gettext("Container") %>
</th>
<th class="p-2"></th>
</tr>
</thead>
<tbody id="ammo_groups">
<%= for ammo_group <- @ammo_groups do %>
<tr id={"ammo_group-#{ammo_group.id}"}>
<td class="p-2">
<%= live_patch(ammo_group.ammo_type.name,
to: Routes.ammo_type_show_path(Endpoint, :show, ammo_group.ammo_type),
class: "link"
) %>
</td>
<td class="p-2">
<%= ammo_group.count %>
</td>
<td class="p-2">
<%= if ammo_group.price_paid do %>
<%= gettext("$%{amount}",
amount: ammo_group.price_paid |> :erlang.float_to_binary(decimals: 2)
) %>
<% end %>
</td>
<td class="p-2">
<%= ammo_group.notes %>
</td>
<td class="p-2">
<div class="px-4 py-2 space-x-4 flex justify-center items-center">
<button
type="button"
class="btn btn-primary"
phx-click="toggle_staged"
phx-value-ammo_group_id={ammo_group.id}
>
<%= if ammo_group.staged, do: gettext("Unstage"), else: gettext("Stage") %>
</button>
<%= live_patch(dgettext("actions", "Record shots"),
to: Routes.ammo_group_index_path(Endpoint, :add_shot_group, ammo_group),
class: "btn btn-primary"
) %>
</div>
</td>
<td class="p-2">
<%= if ammo_group.container do %>
<%= live_patch(ammo_group.container.name,
to: Routes.ammo_group_index_path(Endpoint, :move, ammo_group),
class: "btn btn-primary"
) %>
<% end %>
</td>
<td class="p-2">
<div class="px-4 py-2 space-x-4 flex justify-center items-center">
<%= live_redirect to: Routes.ammo_group_show_path(Endpoint, :show, ammo_group),
class: "text-primary-600 link",
data: [qa: "view-#{ammo_group.id}"] do %>
<i class="fa-fw fa-lg fas fa-eye"></i>
<% end %> <% end %>
<%= live_patch to: Routes.ammo_group_index_path(Endpoint, :edit, ammo_group), <.live_component
class: "text-primary-600 link", module={CanneryWeb.Components.TableComponent}
data: [qa: "edit-#{ammo_group.id}"] do %> id="ammo_groups_index_table"
<i class="fa-fw fa-lg fas fa-edit"></i> action={@live_action}
<% end %> columns={@columns}
rows={@rows}
<%= link to: "#", />
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?"),
qa: "delete-#{ammo_group.id}"
] do %>
<i class="fa-fw fa-lg fas fa-trash"></i>
<% end %>
</div>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% end %> <% end %>
</div> </div>
@ -143,6 +66,7 @@
ammo_group={@ammo_group} ammo_group={@ammo_group}
return_to={Routes.ammo_group_index_path(Endpoint, :index)} return_to={Routes.ammo_group_index_path(Endpoint, :index)}
current_user={@current_user} current_user={@current_user}
containers={@containers}
/> />
</.modal> </.modal>
<% @live_action == :add_shot_group -> %> <% @live_action == :add_shot_group -> %>
@ -170,4 +94,5 @@
/> />
</.modal> </.modal>
<% true -> %> <% true -> %>
<%= nil %>
<% end %> <% end %>

View File

@ -5,8 +5,9 @@ defmodule CanneryWeb.AmmoGroupLive.Show do
use CanneryWeb, :live_view use CanneryWeb, :live_view
import CanneryWeb.Components.ContainerCard import CanneryWeb.Components.ContainerCard
alias Cannery.{Ammo, Repo} alias Cannery.{ActivityLog, ActivityLog.ShotGroup, Ammo, Ammo.AmmoGroup, Repo}
alias CanneryWeb.Endpoint alias CanneryWeb.Endpoint
alias Phoenix.LiveView.Socket
@impl true @impl true
def mount(_params, session, socket) do def mount(_params, session, socket) do
@ -15,18 +16,35 @@ defmodule CanneryWeb.AmmoGroupLive.Show do
@impl true @impl true
def handle_params( def handle_params(
%{"id" => id}, %{"id" => id, "shot_group_id" => shot_group_id},
_url, _url,
%{assigns: %{live_action: live_action, current_user: current_user}} = socket %{assigns: %{live_action: live_action, current_user: current_user}} = socket
) do ) do
ammo_group = Ammo.get_ammo_group!(id, current_user) |> Repo.preload([:container, :ammo_type]) shot_group = ActivityLog.get_shot_group!(shot_group_id, current_user)
{:noreply, socket |> assign(page_title: page_title(live_action), ammo_group: ammo_group)}
socket =
socket
|> assign(page_title: page_title(live_action), shot_group: shot_group)
|> display_ammo_group(id)
{:noreply, socket}
end end
@impl true
def handle_params(%{"id" => id}, _url, %{assigns: %{live_action: live_action}} = socket) do
{:noreply, socket |> assign(page_title: page_title(live_action)) |> display_ammo_group(id)}
end
defp page_title(:add_shot_group), do: gettext("Record Shots")
defp page_title(:edit_shot_group), do: gettext("Edit Shot Records")
defp page_title(:move), do: gettext("Move Ammo group")
defp page_title(:show), do: gettext("Show Ammo group")
defp page_title(:edit), do: gettext("Edit Ammo group")
@impl true @impl true
def handle_event( def handle_event(
"delete", "delete",
_, _params,
%{assigns: %{ammo_group: ammo_group, current_user: current_user}} = socket %{assigns: %{ammo_group: ammo_group, current_user: current_user}} = socket
) do ) do
ammo_group |> Ammo.delete_ammo_group!(current_user) ammo_group |> Ammo.delete_ammo_group!(current_user)
@ -40,17 +58,90 @@ defmodule CanneryWeb.AmmoGroupLive.Show do
@impl true @impl true
def handle_event( def handle_event(
"toggle_staged", "toggle_staged",
_, _params,
%{assigns: %{ammo_group: ammo_group, current_user: current_user}} = socket %{assigns: %{ammo_group: ammo_group, current_user: current_user}} = socket
) do ) do
{:ok, ammo_group} = {:ok, ammo_group} =
ammo_group |> Ammo.update_ammo_group(%{"staged" => !ammo_group.staged}, current_user) ammo_group |> Ammo.update_ammo_group(%{"staged" => !ammo_group.staged}, current_user)
{:noreply, socket |> assign(ammo_group: ammo_group)} {:noreply, socket |> display_ammo_group(ammo_group)}
end end
defp page_title(:add_shot_group), do: gettext("Add Shot group") @impl true
defp page_title(:move), do: gettext("Move Ammo group") def handle_event(
defp page_title(:show), do: gettext("Show Ammo group") "delete_shot_group",
defp page_title(:edit), do: gettext("Edit Ammo group") %{"id" => id},
%{assigns: %{ammo_group: ammo_group, current_user: current_user}} = socket
) do
{:ok, _} =
ActivityLog.get_shot_group!(id, current_user)
|> ActivityLog.delete_shot_group(current_user)
prompt = dgettext("prompts", "Shot records deleted succesfully")
{:noreply, socket |> put_flash(:info, prompt) |> display_ammo_group(ammo_group)}
end
@spec display_ammo_group(Socket.t(), AmmoGroup.t() | AmmoGroup.id()) :: Socket.t()
defp display_ammo_group(socket, %AmmoGroup{} = ammo_group) do
ammo_group = ammo_group |> Repo.preload([:container, :ammo_type, :shot_groups], force: true)
columns = [
%{label: gettext("Rounds shot"), key: "count"},
%{label: gettext("Notes"), key: "notes"},
%{label: gettext("Date"), key: "date"},
%{label: nil, key: "actions", sortable: false}
]
rows =
ammo_group.shot_groups
|> Enum.map(fn shot_group ->
ammo_group |> get_table_row_for_shot_group(shot_group, columns)
end)
socket |> assign(ammo_group: ammo_group, columns: columns, rows: rows)
end
defp display_ammo_group(%{assigns: %{current_user: current_user}} = socket, id),
do: display_ammo_group(socket, Ammo.get_ammo_group!(id, current_user))
@spec get_table_row_for_shot_group(AmmoGroup.t(), ShotGroup.t(), [map()]) :: [map()]
defp get_table_row_for_shot_group(ammo_group, %{date: date} = shot_group, columns) do
assigns = %{ammo_group: ammo_group, shot_group: shot_group}
columns
|> Enum.into(%{}, fn %{key: key} ->
value =
case key do
"date" ->
{date, date |> display_date()}
"actions" ->
~H"""
<div class="px-4 py-2 space-x-4 flex justify-center items-center">
<%= live_patch to: Routes.ammo_group_show_path(Endpoint, :edit_shot_group, @ammo_group, shot_group),
class: "text-primary-600 link",
data: [qa: "edit-#{shot_group.id}"] do %>
<i class="fa-fw fa-lg fas fa-edit"></i>
<% end %>
<%= link to: "#",
class: "text-primary-600 link",
phx_click: "delete_shot_group",
phx_value_id: shot_group.id,
data: [
confirm: dgettext("prompts", "Are you sure you want to delete this shot record?"),
qa: "delete-#{shot_group.id}"
] do %>
<i class="fa-fw fa-lg fas fa-trash"></i>
<% end %>
</div>
"""
key ->
shot_group |> Map.get(key |> String.to_existing_atom())
end
{key, value}
end)
end
end end

View File

@ -9,6 +9,16 @@
<%= @ammo_group.count %> <%= @ammo_group.count %>
</span> </span>
<span class="rounded-lg title text-lg">
<%= gettext("Original count:") %>
<%= @ammo_group.count + Ammo.get_used_count(@ammo_group) %>
</span>
<span class="rounded-lg title text-lg">
<%= gettext("Percentage left:") %>
<%= "#{@ammo_group |> Ammo.get_percentage_remaining()}%" %>
</span>
<%= if @ammo_group.notes do %> <%= if @ammo_group.notes do %>
<span class="rounded-lg title text-lg"> <span class="rounded-lg title text-lg">
<%= gettext("Notes:") %> <%= gettext("Notes:") %>
@ -18,11 +28,20 @@
<%= if @ammo_group.price_paid do %> <%= if @ammo_group.price_paid do %>
<span class="rounded-lg title text-lg"> <span class="rounded-lg title text-lg">
<%= gettext("Price paid:") %> <%= gettext("Original cost:") %>
<%= gettext("$%{amount}", <%= gettext("$%{amount}",
amount: @ammo_group.price_paid |> :erlang.float_to_binary(decimals: 2) amount: @ammo_group.price_paid |> :erlang.float_to_binary(decimals: 2)
) %> ) %>
</span> </span>
<span class="rounded-lg title text-lg">
<%= gettext("Current value:") %>
<%= gettext("$%{amount}",
amount:
(@ammo_group.price_paid * Ammo.get_percentage_remaining(@ammo_group) / 100)
|> :erlang.float_to_binary(decimals: 2)
) %>
</span>
<% end %> <% end %>
</div> </div>
@ -84,6 +103,21 @@
<%= gettext("This ammo group is not in a container") %> <%= gettext("This ammo group is not in a container") %>
<% end %> <% end %>
</div> </div>
<%= unless @ammo_group.shot_groups |> Enum.empty?() do %>
<hr class="mb-4 w-full" />
<h1 class="mb-4 px-4 py-2 text-center rounded-lg title text-xl">
<%= gettext("Rounds used") %>
</h1>
<.live_component
module={CanneryWeb.Components.TableComponent}
id="ammo_group_shot_groups_table"
columns={@columns}
rows={@rows}
/>
<% end %>
</div> </div>
<%= case @live_action do %> <%= case @live_action do %>
@ -99,6 +133,18 @@
current_user={@current_user} current_user={@current_user}
/> />
</.modal> </.modal>
<% :edit_shot_group -> %>
<.modal return_to={Routes.ammo_group_show_path(Endpoint, :show, @ammo_group)}>
<.live_component
module={CanneryWeb.RangeLive.FormComponent}
id={@shot_group.id}
title={@page_title}
action={@live_action}
shot_group={@shot_group}
return_to={Routes.ammo_group_show_path(Endpoint, :show, @ammo_group)}
current_user={@current_user}
/>
</.modal>
<% :add_shot_group -> %> <% :add_shot_group -> %>
<.modal return_to={Routes.ammo_group_show_path(Endpoint, :show, @ammo_group)}> <.modal return_to={Routes.ammo_group_show_path(Endpoint, :show, @ammo_group)}>
<.live_component <.live_component

View File

@ -9,7 +9,7 @@
phx-target={@myself} phx-target={@myself}
phx-change="validate" phx-change="validate"
phx-submit="save" phx-submit="save"
class="flex flex-col sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center" class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
> >
<%= if @changeset.action && not @changeset.valid? do %> <%= if @changeset.action && not @changeset.valid? do %>
<div class="invalid-feedback col-span-3 text-center"> <div class="invalid-feedback col-span-3 text-center">
@ -17,11 +17,11 @@
</div> </div>
<% end %> <% end %>
<%= label(f, :name, gettext("Name"), class: "mr-4 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") %>
<%= error_tag(f, :name, "col-span-3 text-center") %> <%= error_tag(f, :name, "col-span-3 text-center") %>
<%= label(f, :desc, gettext("Description"), class: "mr-4 title text-lg text-primary-600") %> <%= label(f, :desc, gettext("Description"), class: "title text-lg text-primary-600") %>
<%= textarea(f, :desc, <%= textarea(f, :desc,
class: "text-center col-span-2 input input-primary", class: "text-center col-span-2 input input-primary",
phx_hook: "MaintainAttrs" phx_hook: "MaintainAttrs"
@ -34,50 +34,42 @@
> >
<%= gettext("Example bullet type abbreviations") %> <%= gettext("Example bullet type abbreviations") %>
</a> </a>
<%= label(f, :bullet_type, gettext("Bullet type"), <%= label(f, :bullet_type, gettext("Bullet type"), class: "title text-lg text-primary-600") %>
class: "mr-4 title text-lg text-primary-600"
) %>
<%= text_input(f, :bullet_type, <%= text_input(f, :bullet_type,
class: "text-center col-span-2 input input-primary", class: "text-center col-span-2 input input-primary",
placeholder: gettext("FMJ") placeholder: gettext("FMJ")
) %> ) %>
<%= error_tag(f, :bullet_type, "col-span-3 text-center") %> <%= error_tag(f, :bullet_type, "col-span-3 text-center") %>
<%= label(f, :bullet_core, gettext("Bullet core"), <%= label(f, :bullet_core, gettext("Bullet core"), class: "title text-lg text-primary-600") %>
class: "mr-4 title text-lg text-primary-600"
) %>
<%= text_input(f, :bullet_core, <%= text_input(f, :bullet_core,
class: "text-center col-span-2 input input-primary", class: "text-center col-span-2 input input-primary",
placeholder: gettext("Steel") placeholder: gettext("Steel")
) %> ) %>
<%= error_tag(f, :bullet_core, "col-span-3 text-center") %> <%= error_tag(f, :bullet_core, "col-span-3 text-center") %>
<%= label(f, :cartridge, gettext("Cartridge"), class: "mr-4 title text-lg text-primary-600") %> <%= label(f, :cartridge, gettext("Cartridge"), class: "title text-lg text-primary-600") %>
<%= text_input(f, :cartridge, <%= text_input(f, :cartridge,
class: "text-center col-span-2 input input-primary", class: "text-center col-span-2 input input-primary",
placeholder: "5.56x46mm NATO" placeholder: "5.56x46mm NATO"
) %> ) %>
<%= error_tag(f, :cartridge, "col-span-3 text-center") %> <%= error_tag(f, :cartridge, "col-span-3 text-center") %>
<%= label(f, :caliber, gettext("Caliber"), class: "mr-4 title text-lg text-primary-600") %> <%= 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",
placeholder: ".223" placeholder: ".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"), <%= label(f, :case_material, gettext("Case material"), class: "title text-lg text-primary-600") %>
class: "mr-4 title text-lg text-primary-600"
) %>
<%= text_input(f, :case_material, <%= text_input(f, :case_material,
class: "text-center col-span-2 input input-primary", class: "text-center col-span-2 input input-primary",
placeholder: gettext("Brass") placeholder: gettext("Brass")
) %> ) %>
<%= error_tag(f, :case_material, "col-span-3 text-center") %> <%= error_tag(f, :case_material, "col-span-3 text-center") %>
<%= label(f, :jacket_type, gettext("Jacket type"), <%= label(f, :jacket_type, gettext("Jacket type"), class: "title text-lg text-primary-600") %>
class: "mr-4 title text-lg text-primary-600"
) %>
<%= text_input(f, :jacket_type, <%= text_input(f, :jacket_type,
class: "text-center col-span-2 input input-primary", class: "text-center col-span-2 input input-primary",
placeholder: gettext("Bimetal") placeholder: gettext("Bimetal")
@ -85,7 +77,7 @@
<%= error_tag(f, :case_material, "col-span-3 text-center") %> <%= error_tag(f, :case_material, "col-span-3 text-center") %>
<%= label(f, :muzzle_velocity, gettext("Muzzle velocity"), <%= label(f, :muzzle_velocity, gettext("Muzzle velocity"),
class: "mr-4 title text-lg text-primary-600" class: "title text-lg text-primary-600"
) %> ) %>
<%= number_input(f, :muzzle_velocity, <%= number_input(f, :muzzle_velocity,
step: "1", step: "1",
@ -94,14 +86,12 @@
) %> ) %>
<%= error_tag(f, :muzzle_velocity, "col-span-3 text-center") %> <%= error_tag(f, :muzzle_velocity, "col-span-3 text-center") %>
<%= label(f, :powder_type, gettext("Powder type"), <%= label(f, :powder_type, gettext("Powder type"), class: "title text-lg text-primary-600") %>
class: "mr-4 title text-lg text-primary-600"
) %>
<%= text_input(f, :powder_type, class: "text-center col-span-2 input input-primary") %> <%= text_input(f, :powder_type, class: "text-center col-span-2 input input-primary") %>
<%= error_tag(f, :powder_type, "col-span-3 text-center") %> <%= error_tag(f, :powder_type, "col-span-3 text-center") %>
<%= label(f, :powder_grains_per_charge, gettext("Powder grains per charge"), <%= label(f, :powder_grains_per_charge, gettext("Powder grains per charge"),
class: "mr-4 title text-lg text-primary-600" class: "title text-lg text-primary-600"
) %> ) %>
<%= number_input(f, :powder_grains_per_charge, <%= number_input(f, :powder_grains_per_charge,
step: "1", step: "1",
@ -110,7 +100,7 @@
) %> ) %>
<%= error_tag(f, :powder_grains_per_charge, "col-span-3 text-center") %> <%= error_tag(f, :powder_grains_per_charge, "col-span-3 text-center") %>
<%= label(f, :grains, gettext("Grains"), class: "mr-4 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,
step: "1", step: "1",
class: "text-center col-span-2 input input-primary", class: "text-center col-span-2 input input-primary",
@ -118,54 +108,48 @@
) %> ) %>
<%= error_tag(f, :grains, "col-span-3 text-center") %> <%= error_tag(f, :grains, "col-span-3 text-center") %>
<%= label(f, :pressure, gettext("Pressure"), class: "mr-4 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",
placeholder: "+P" placeholder: "+P"
) %> ) %>
<%= error_tag(f, :pressure, "col-span-3 text-center") %> <%= error_tag(f, :pressure, "col-span-3 text-center") %>
<%= label(f, :primer_type, gettext("Primer type"), <%= label(f, :primer_type, gettext("Primer type"), class: "title text-lg text-primary-600") %>
class: "mr-4 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",
placeholder: "Boxer" placeholder: "Boxer"
) %> ) %>
<%= error_tag(f, :primer_type, "col-span-3 text-center") %> <%= error_tag(f, :primer_type, "col-span-3 text-center") %>
<%= label(f, :firing_type, gettext("Firing type"), <%= label(f, :firing_type, gettext("Firing type"), class: "title text-lg text-primary-600") %>
class: "mr-4 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",
placeholder: "Centerfire" placeholder: "Centerfire"
) %> ) %>
<%= error_tag(f, :firing_type, "col-span-3 text-center") %> <%= error_tag(f, :firing_type, "col-span-3 text-center") %>
<%= label(f, :tracer, gettext("Tracer"), class: "mr-4 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") %>
<%= label(f, :incendiary, gettext("Incendiary"), class: "mr-4 title text-lg text-primary-600") %> <%= label(f, :incendiary, gettext("Incendiary"), class: "title text-lg text-primary-600") %>
<%= checkbox(f, :incendiary, class: "text-center col-span-2 checkbox") %> <%= checkbox(f, :incendiary, class: "text-center col-span-2 checkbox") %>
<%= error_tag(f, :incendiary, "col-span-3 text-center") %> <%= error_tag(f, :incendiary, "col-span-3 text-center") %>
<%= label(f, :blank, gettext("Blank"), class: "mr-4 title text-lg text-primary-600") %> <%= label(f, :blank, gettext("Blank"), class: "title text-lg text-primary-600") %>
<%= checkbox(f, :blank, class: "text-center col-span-2 checkbox") %> <%= checkbox(f, :blank, class: "text-center col-span-2 checkbox") %>
<%= error_tag(f, :blank, "col-span-3 text-center") %> <%= error_tag(f, :blank, "col-span-3 text-center") %>
<%= label(f, :corrosive, gettext("Corrosive"), class: "mr-4 title text-lg text-primary-600") %> <%= label(f, :corrosive, gettext("Corrosive"), class: "title text-lg text-primary-600") %>
<%= 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") %>
<%= label(f, :manufacturer, gettext("Manufacturer"), <%= label(f, :manufacturer, gettext("Manufacturer"), class: "title text-lg text-primary-600") %>
class: "mr-4 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") %>
<%= error_tag(f, :manufacturer, "col-span-3 text-center") %> <%= error_tag(f, :manufacturer, "col-span-3 text-center") %>
<%= label(f, :upc, gettext("UPC"), class: "mr-4 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") %>
<%= error_tag(f, :upc, "col-span-3 text-center") %> <%= error_tag(f, :upc, "col-span-3 text-center") %>

View File

@ -31,7 +31,7 @@ defmodule CanneryWeb.AmmoTypeLive.Index do
end end
defp apply_action(socket, :index, _params) do defp apply_action(socket, :index, _params) do
socket |> assign(:page_title, gettext("Listing Ammo types")) |> assign(:ammo_type, nil) socket |> assign(:page_title, gettext("Ammo types")) |> assign(:ammo_type, nil)
end end
@impl true @impl true
@ -46,34 +46,103 @@ defmodule CanneryWeb.AmmoTypeLive.Index do
defp list_ammo_types(%{assigns: %{current_user: current_user}} = socket) do defp list_ammo_types(%{assigns: %{current_user: current_user}} = socket) do
ammo_types = Ammo.list_ammo_types(current_user) ammo_types = Ammo.list_ammo_types(current_user)
columns_to_display = columns =
[ [
{gettext("Name"), :name, :string}, %{label: gettext("Name"), key: "name", type: :string},
{gettext("Bullet type"), :bullet_type, :string}, %{label: gettext("Bullet type"), key: "bullet_type", type: :string},
{gettext("Bullet core"), :bullet_core, :string}, %{label: gettext("Bullet core"), key: "bullet_core", type: :string},
{gettext("Cartridge"), :cartridge, :string}, %{label: gettext("Cartridge"), key: "cartridge", type: :string},
{gettext("Caliber"), :caliber, :string}, %{label: gettext("Caliber"), key: "caliber", type: :string},
{gettext("Case material"), :case_material, :string}, %{label: gettext("Case material"), key: "case_material", type: :string},
{gettext("Jacket type"), :jacket_type, :string}, %{label: gettext("Jacket type"), key: "jacket_type", type: :string},
{gettext("Muzzle velocity"), :muzzle_velocity, :string}, %{label: gettext("Muzzle velocity"), key: "muzzle_velocity", type: :string},
{gettext("Powder type"), :powder_type, :string}, %{label: gettext("Powder type"), key: "powder_type", type: :string},
{gettext("Powder grains per charge"), :powder_grains_per_charge, :string}, %{
{gettext("Grains"), :grains, :string}, label: gettext("Powder grains per charge"),
{gettext("Pressure"), :pressure, :string}, key: "powder_grains_per_charge",
{gettext("Primer type"), :primer_type, :string}, type: :string
{gettext("Rimfire"), :rimfire, :boolean}, },
{gettext("Tracer"), :tracer, :boolean}, %{label: gettext("Grains"), key: "grains", type: :string},
{gettext("Incendiary"), :incendiary, :boolean}, %{label: gettext("Pressure"), key: "pressure", type: :string},
{gettext("Blank"), :blank, :boolean}, %{label: gettext("Primer type"), key: "primer_type", type: :string},
{gettext("Corrosive"), :corrosive, :boolean}, %{label: gettext("Firing type"), key: "firing_type", type: :string},
{gettext("Manufacturer"), :manufacturer, :string}, %{label: gettext("Tracer"), key: "tracer", type: :boolean},
{gettext("UPC"), :upc, :string} %{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}
] ]
# filter columns to only used ones |> Enum.filter(fn %{key: key, type: type} ->
|> Enum.filter(fn {_label, field, _type} -> # remove columns if all values match defaults
ammo_types |> Enum.any?(fn ammo_type -> not (ammo_type |> Map.get(field) |> is_nil()) end) default_value = if type == :boolean, do: false, else: nil
end)
socket |> assign(ammo_types: ammo_types, columns_to_display: columns_to_display) ammo_types
|> Enum.any?(fn ammo_type ->
not (ammo_type |> Map.get(key |> String.to_existing_atom()) == default_value)
end)
end)
|> Kernel.++([
%{label: gettext("Total # of rounds"), key: "round_count", type: :round_count},
%{label: nil, key: "actions", type: :actions, sortable: false}
])
rows =
ammo_types
|> Enum.map(fn ammo_type -> ammo_type |> get_ammo_type_values(columns, current_user) end)
socket |> assign(columns: columns, rows: rows)
end
defp get_ammo_type_values(ammo_type, columns, current_user) do
assigns = %{ammo_type: ammo_type}
columns
|> Enum.into(%{}, fn %{key: key, type: type} ->
value =
case type do
:boolean ->
ammo_type |> Map.get(key |> String.to_existing_atom()) |> humanize()
:round_count ->
ammo_type |> Ammo.get_round_count_for_ammo_type(current_user)
:actions ->
~H"""
<div class="px-4 py-2 space-x-4 flex justify-center items-center">
<%= live_redirect to: Routes.ammo_type_show_path(Endpoint, :show, ammo_type),
class: "text-primary-600 link",
data: [qa: "view-#{ammo_type.id}"] do %>
<i class="fa-fw fa-lg fas fa-eye"></i>
<% end %>
<%= live_patch to: Routes.ammo_type_index_path(Endpoint, :edit, ammo_type),
class: "text-primary-600 link",
data: [qa: "edit-#{ammo_type.id}"] do %>
<i class="fa-fw fa-lg fas fa-edit"></i>
<% end %>
<%= link to: "#",
class: "text-primary-600 link",
phx_click: "delete",
phx_value_id: ammo_type.id,
data: [
confirm: dgettext("prompts", "Are you sure you want to delete this ammo?"),
qa: "delete-#{ammo_type.id}"
] do %>
<i class="fa-lg fas fa-trash"></i>
<% end %>
</div>
"""
nil ->
nil
_other ->
ammo_type |> Map.get(key |> String.to_existing_atom())
end
{key, value}
end)
end end
end end

View File

@ -3,7 +3,7 @@
<%= gettext("Ammo Types") %> <%= gettext("Ammo Types") %>
</h1> </h1>
<%= if @ammo_types |> Enum.empty?() do %> <%= if @rows |> Enum.empty?() 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("😔") %>
@ -19,64 +19,13 @@
class: "btn btn-primary" class: "btn btn-primary"
) %> ) %>
<div class="w-full overflow-x-auto border border-gray-600 rounded-lg shadow-lg bg-black"> <.live_component
<table class="min-w-full table-auto text-center bg-white"> module={CanneryWeb.Components.TableComponent}
<thead class="border-b border-primary-600"> id="ammo_types_index_table"
<tr> action={@live_action}
<%= for {field_name, _field, _type} <- @columns_to_display do %> columns={@columns}
<th class="p-2"> rows={@rows}
<%= field_name %> />
</th>
<% end %>
<th class="p-2"></th>
</tr>
</thead>
<tbody>
<%= for ammo_type <- @ammo_types do %>
<tr id={"ammo_type-#{ammo_type.id}"}>
<%= for {_label, field, type} <- @columns_to_display do %>
<td class="p-2">
<%= case type do %>
<% :boolean -> %>
<%= ammo_type |> Map.get(field) |> humanize() %>
<% _other -> %>
<%= ammo_type |> Map.get(field) %>
<% end %>
</td>
<% end %>
<td class="p-2">
<div class="px-4 py-2 space-x-4 flex justify-center items-center">
<%= live_redirect to: Routes.ammo_type_show_path(Endpoint, :show, ammo_type),
class: "text-primary-600 link",
data: [qa: "view-#{ammo_type.id}"] do %>
<i class="fa-fw fa-lg fas fa-eye"></i>
<% end %>
<%= live_patch to: Routes.ammo_type_index_path(Endpoint, :edit, ammo_type),
class: "text-primary-600 link",
data: [qa: "edit-#{ammo_type.id}"] do %>
<i class="fa-fw fa-lg fas fa-edit"></i>
<% end %>
<%= link to: "#",
class: "text-primary-600 link",
phx_click: "delete",
phx_value_id: ammo_type.id,
data: [
confirm: dgettext("prompts", "Are you sure you want to delete this ammo?"),
qa: "delete-#{ammo_type.id}"
] do %>
<i class="fa-lg fas fa-trash"></i>
<% end %>
</div>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% end %> <% end %>
</div> </div>

View File

@ -14,7 +14,7 @@ defmodule CanneryWeb.AmmoTypeLive.Show do
end end
@impl true @impl true
def handle_params(%{"id" => id}, _, %{assigns: %{current_user: current_user}} = socket) do def handle_params(%{"id" => id}, _params, %{assigns: %{current_user: current_user}} = socket) do
ammo_type = Ammo.get_ammo_type!(id, current_user) ammo_type = Ammo.get_ammo_type!(id, current_user)
socket = socket =
@ -32,7 +32,7 @@ defmodule CanneryWeb.AmmoTypeLive.Show do
@impl true @impl true
def handle_event( def handle_event(
"delete", "delete",
_, _params,
%{assigns: %{ammo_type: ammo_type, current_user: current_user}} = socket %{assigns: %{ammo_type: ammo_type, current_user: current_user}} = socket
) do ) do
%{name: ammo_type_name} = ammo_type |> Ammo.delete_ammo_type!(current_user) %{name: ammo_type_name} = ammo_type |> Ammo.delete_ammo_type!(current_user)

View File

@ -35,70 +35,74 @@
<hr class="hr" /> <hr class="hr" />
<div class="grid sm:grid-cols-2 text-center justify-center items-center"> <div class="grid sm:grid-cols-2 gap-4 text-center justify-center items-center">
<%= for {field_name, field} <- [ <%= for {field_name, field, type} <- [
{gettext("Bullet type"), :bullet_type}, {gettext("Bullet type"), :bullet_type, :string},
{gettext("Bullet core"), :bullet_core}, {gettext("Bullet core"), :bullet_core, :string},
{gettext("Cartridge"), :cartridge}, {gettext("Cartridge"), :cartridge, :string},
{gettext("Caliber"), :caliber}, {gettext("Caliber"), :caliber, :string},
{gettext("Case material"), :case_material}, {gettext("Case material"), :case_material, :string},
{gettext("Jacket type"), :jacket_type}, {gettext("Jacket type"), :jacket_type, :string},
{gettext("Muzzle velocity"), :muzzle_velocity}, {gettext("Muzzle velocity"), :muzzle_velocity, :string},
{gettext("Powder type"), :powder_type}, {gettext("Powder type"), :powder_type, :string},
{gettext("Powder grains per charge"), :powder_grains_per_charge}, {gettext("Powder grains per charge"), :powder_grains_per_charge, :string},
{gettext("Grains"), :grains}, {gettext("Grains"), :grains, :string},
{gettext("Pressure"), :pressure}, {gettext("Pressure"), :pressure, :string},
{gettext("Primer type"), :primer_type} {gettext("Primer type"), :primer_type, :string},
{gettext("Firing type"), :firing_type, :string},
{gettext("Tracer"), :tracer, :boolean},
{gettext("Incendiary"), :incendiary, :boolean},
{gettext("Blank"), :blank, :boolean},
{gettext("Corrosive"), :corrosive, :boolean},
{gettext("Manufacturer"), :manufacturer, :string},
{gettext("UPC"), :upc, :string}
] do %> ] do %>
<%= if @ammo_type |> Map.get(field) do %> <%= if @ammo_type |> Map.get(field) do %>
<h3 class="mb-2 sm:mr-4 title text-lg"> <h3 class="title text-lg">
<%= field_name %>: <%= field_name %>:
</h3> </h3>
<span class="mb-4 sm:mb-2 text-primary-600"> <span class="text-primary-600">
<%= @ammo_type |> Map.get(field) %> <%= case type do %>
</span> <% :boolean -> %>
<% end %>
<% end %>
<%= for {field_name, field} <- [
{"Rimfire", :rimfire},
{"Tracer", :tracer},
{"Incendiary", :incendiary},
{"Blank", :blank},
{"Corrosive", :corrosive}
] do %>
<h3 class="mb-2 sm:mr-4 title text-lg">
<%= field_name %>:
</h3>
<span class="mb-4 sm:mb-2 text-primary-600">
<%= @ammo_type |> Map.get(field) |> humanize() %> <%= @ammo_type |> Map.get(field) |> humanize() %>
<% _ -> %>
<%= @ammo_type |> Map.get(field) %>
<% end %>
</span> </span>
<% end %> <% end %>
<% end %>
<%= for {field_name, field} <- [{"Manufacturer", :manufacturer}, {"UPC", :upc}] do %> <h3 class="title text-lg">
<%= if @ammo_type |> Map.get(field) do %> <%= gettext("Current # of rounds:") %>
<h3 class="mb-2 sm:mr-4 title text-lg">
<%= field_name %>:
</h3> </h3>
<span class="mb-4 sm:mb-2 text-primary-600"> <span class="text-primary-600">
<%= @ammo_type |> Map.get(field) %> <%= @ammo_type |> Ammo.get_round_count_for_ammo_type(@current_user) %>
</span>
<h3 class="title text-lg">
<%= gettext("Total rounds shot:") %>
</h3>
<span class="text-primary-600">
<%= @ammo_type |> Ammo.get_used_count_for_ammo_type(@current_user) %>
</span> </span>
<% end %>
<% end %>
<%= if @avg_cost_per_round do %> <%= if @avg_cost_per_round do %>
<h3 class="mb-2 sm:mr-4 title text-lg"> <h3 class="title text-lg">
<%= gettext("Average Price paid") %>: <%= gettext("Average Price paid") %>:
</h3> </h3>
<span class="mb-4 sm:mb-2 text-primary-600"> <span class="text-primary-600">
<%= gettext("$%{amount}", <%= gettext("$%{amount}",
amount: @avg_cost_per_round |> :erlang.float_to_binary(decimals: 2) amount: @avg_cost_per_round |> :erlang.float_to_binary(decimals: 2)
) %> ) %>
</span> </span>
<% else %>
<h3 class="mx-8 my-4 title text-lg text-primary-600 col-span-2">
<%= gettext("No cost information") %>
</h3>
<% end %> <% end %>
</div> </div>
@ -106,11 +110,16 @@
<div> <div>
<%= if @ammo_groups |> Enum.empty?() do %> <%= if @ammo_groups |> Enum.empty?() do %>
<h2 class="mx-8 my-4 title text-lg text-primary-600">
<%= gettext("No ammo for this type") %> <%= gettext("No ammo for this type") %>
<%= display_emoji("😔") %>
</h2>
<% else %> <% else %>
<div class="flex flex-wrap justify-center items-center">
<%= for ammo_group <- @ammo_groups do %> <%= for ammo_group <- @ammo_groups do %>
<.ammo_group_card ammo_group={ammo_group} /> <.ammo_group_card ammo_group={ammo_group} />
<% end %> <% end %>
</div>
<% end %> <% end %>
</div> </div>
</div> </div>

View File

@ -1,51 +0,0 @@
defmodule CanneryWeb.ContainerLive.AddTagComponent do
@moduledoc """
Livecomponent that can add a tag to a Container
"""
use CanneryWeb, :live_component
alias Cannery.{Accounts.User, Containers, Containers.Container, Tags, Tags.Tag}
alias Phoenix.LiveView.Socket
@impl true
@spec update(
%{:container => Container.t(), :current_user => User.t(), optional(any) => any},
Socket.t()
) :: {:ok, Socket.t()}
def update(%{container: _container, current_user: current_user} = assigns, socket) do
{:ok, socket |> assign(assigns) |> assign(:tags, Tags.list_tags(current_user))}
end
@impl true
def handle_event(
"save",
%{"tag" => %{"tag_id" => tag_id}},
%{
assigns: %{
tags: tags,
container: container,
current_user: current_user,
return_to: return_to
}
} = socket
) do
socket =
case tags |> Enum.find(fn %{id: id} -> tag_id == id end) do
nil ->
prompt = dgettext("errors", "Tag could not be added")
socket |> put_flash(:error, prompt)
%{name: tag_name} = tag ->
_container_tag = Containers.add_tag!(container, tag, current_user)
prompt = dgettext("prompts", "%{name} added successfully", name: tag_name)
socket |> put_flash(:info, prompt) |> push_redirect(to: return_to)
end
{:noreply, socket}
end
@spec tag_options([Tag.t()]) :: [{String.t(), Tag.id()}]
defp tag_options(tags) do
tags |> Enum.map(fn %{id: id, name: name} -> {name, id} end)
end
end

View File

@ -1,22 +0,0 @@
<div>
<h2 class="mb-8 text-center title text-xl text-primary-600">
<%= @title %>
</h2>
<.form
let={f}
for={:tag}
id="add-tag-to-container-form"
class="flex flex-col sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
phx-target={@myself}
phx-submit="save"
>
<%= select(f, :tag_id, tag_options(@tags), class: "text-center col-span-2 input input-primary") %>
<%= error_tag(f, :tag_id, "col-span-3 text-center") %>
<%= submit(dgettext("actions", "Add"),
class: "mx-auto btn btn-primary",
phx_disable_with: dgettext("prompts", "Adding...")
) %>
</.form>
</div>

View File

@ -0,0 +1,73 @@
defmodule CanneryWeb.ContainerLive.EditTagsComponent do
@moduledoc """
Livecomponent that can add or remove a tag to a Container
"""
use CanneryWeb, :live_component
alias Cannery.{Accounts.User, Containers, Containers.Container, Repo, Tags, Tags.Tag}
alias Phoenix.LiveView.Socket
@impl true
@spec update(
%{:container => Container.t(), :current_user => User.t(), optional(any) => any},
Socket.t()
) :: {:ok, Socket.t()}
def update(%{container: container, current_user: current_user} = assigns, socket) do
tags = Tags.list_tags(current_user)
container = container |> Repo.preload(:tags)
{:ok, socket |> assign(assigns) |> assign(tags: tags, container: container)}
end
@impl true
def handle_event(
"save",
%{"tag" => %{"tag_id" => tag_id}},
%{assigns: %{tags: tags, container: container, current_user: current_user}} = socket
) do
socket =
case tags |> Enum.find(fn %{id: id} -> tag_id == id end) do
nil ->
prompt = dgettext("errors", "Tag could not be added")
socket |> put_flash(:error, prompt)
%{name: tag_name} = tag ->
_container_tag = Containers.add_tag!(container, tag, current_user)
container = container |> Repo.preload(:tags, force: true)
prompt = dgettext("prompts", "%{name} added successfully", name: tag_name)
socket |> put_flash(:info, prompt) |> assign(container: container)
end
{:noreply, socket}
end
@impl true
def handle_event(
"delete",
%{"tag-id" => tag_id},
%{assigns: %{tags: tags, container: container, current_user: current_user}} = socket
) do
socket =
case tags |> Enum.find(fn %{id: id} -> tag_id == id end) do
nil ->
prompt = dgettext("errors", "Tag could not be removed")
socket |> put_flash(:error, prompt)
%{name: tag_name} = tag ->
_container_tag = Containers.remove_tag!(container, tag, current_user)
container = container |> Repo.preload(:tags, force: true)
prompt = dgettext("prompts", "%{name} removed successfully", name: tag_name)
socket |> put_flash(:info, prompt) |> assign(container: container)
end
{:noreply, socket}
end
@spec tag_options([Tag.t()], Container.t()) :: [{String.t(), Tag.id()}]
defp tag_options(tags, %Container{tags: container_tags}) do
container_tags_map = container_tags |> Enum.map(fn %{id: id} -> id end) |> MapSet.new()
tags
|> Enum.reject(fn %{id: id} -> container_tags_map |> MapSet.member?(id) end)
|> Enum.map(fn %{id: id, name: name} -> {name, id} end)
end
end

View File

@ -0,0 +1,58 @@
<div class="flex flex-col justify-center items-center text-center space-y-8">
<h2 class="title text-xl text-primary-600">
<%= @title %>
</h2>
<div class="flex flex-wrap justify-center items-center">
<%= for tag <- @container.tags do %>
<%= link to: "#",
class: "mx-2 my-1 px-4 py-2 rounded-lg title text-xl",
style: "color: #{tag.text_color}; background-color: #{tag.bg_color}",
phx_click: "delete",
phx_value_tag_id: tag.id,
phx_target: @myself,
data: [
confirm:
dgettext(
"prompts",
"Are you sure you want to remove the %{tag_name} tag from %{container_name}?",
tag_name: tag.name,
container_name: @container.name
)
] do %>
<%= tag.name %>
<i class="fa-fw fa-sm fas fa-trash"></i>
<% end %>
<% end %>
<%= if @container.tags |> Enum.empty?() do %>
<h2 class="title text-xl text-primary-600">
<%= gettext("No tags") %>
<%= display_emoji("😔") %>
</h2>
<% end %>
</div>
<%= unless tag_options(@tags, @container) |> Enum.empty?() do %>
<hr class="hr" />
<.form
let={f}
for={:tag}
id="add-tag-to-container-form"
class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
phx-target={@myself}
phx-submit="save"
>
<%= select(f, :tag_id, tag_options(@tags, @container),
class: "text-center col-span-2 input input-primary"
) %>
<%= error_tag(f, :tag_id, "col-span-3 text-center") %>
<%= submit(dgettext("actions", "Add"),
class: "mx-auto btn btn-primary",
phx_disable_with: dgettext("prompts", "Adding...")
) %>
</.form>
<% end %>
</div>

View File

@ -6,7 +6,7 @@
let={f} let={f}
for={@changeset} for={@changeset}
id="container-form" id="container-form"
class="flex flex-col sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center" class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
phx-target={@myself} phx-target={@myself}
phx-change="validate" phx-change="validate"
phx-submit="save" phx-submit="save"

View File

@ -5,24 +5,28 @@ defmodule CanneryWeb.ContainerLive.Index do
use CanneryWeb, :live_view use CanneryWeb, :live_view
import CanneryWeb.Components.ContainerCard import CanneryWeb.Components.ContainerCard
alias Cannery.{Containers, Containers.Container} alias Cannery.{Containers, Containers.Container, Repo}
alias CanneryWeb.Endpoint alias CanneryWeb.Endpoint
alias Ecto.Changeset alias Ecto.Changeset
@impl true @impl true
def mount(_params, session, socket) do def mount(_params, session, socket) do
{:ok, socket |> assign_defaults(session) |> display_containers()} {:ok, socket |> assign_defaults(session)}
end end
@impl true @impl true
def handle_params(params, _url, %{assigns: %{live_action: live_action}} = socket) do def handle_params(params, _url, %{assigns: %{live_action: live_action}} = socket) do
{:noreply, apply_action(socket, live_action, params)} {:noreply, apply_action(socket, live_action, params) |> display_containers()}
end end
defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit, %{"id" => id}) do defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit, %{"id" => id}) do
%{name: container_name} =
container =
Containers.get_container!(id, current_user)
|> Repo.preload([:tags, :ammo_groups], force: true)
socket socket
|> assign(:page_title, gettext("Edit Container")) |> assign(page_title: gettext("Edit %{name}", name: container_name), container: container)
|> assign(:container, Containers.get_container!(id, current_user))
end end
defp apply_action(socket, :new, _params) do defp apply_action(socket, :new, _params) do
@ -30,7 +34,19 @@ defmodule CanneryWeb.ContainerLive.Index do
end end
defp apply_action(socket, :index, _params) do defp apply_action(socket, :index, _params) do
socket |> assign(:page_title, gettext("Listing Containers")) |> assign(:container, nil) socket
|> assign(:page_title, gettext("Containers"))
|> assign(:container, nil)
|> display_containers()
end
defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit_tags, %{"id" => id}) do
%{name: container_name} =
container =
Containers.get_container!(id, current_user) |> Repo.preload([:tags, :ammo_groups])
page_title = gettext("Edit %{name} tags", name: container_name)
socket |> assign(page_title: page_title, container: container)
end end
@impl true @impl true
@ -70,6 +86,9 @@ defmodule CanneryWeb.ContainerLive.Index do
end end
defp display_containers(%{assigns: %{current_user: current_user}} = socket) do defp display_containers(%{assigns: %{current_user: current_user}} = socket) do
socket |> assign(containers: Containers.list_containers(current_user)) containers =
Containers.list_containers(current_user) |> Repo.preload([:tags, :ammo_groups], force: true)
socket |> assign(containers: containers)
end end
end end

View File

@ -20,9 +20,17 @@
) %> ) %>
<% end %> <% end %>
<div class="flex flex-row flex-wrap justify-center items-center"> <div class="max-w-full flex flex-row flex-wrap justify-center items-center">
<%= for container <- @containers do %> <%= for container <- @containers do %>
<.container_card container={container}> <.container_card container={container}>
<:tag_actions>
<div class="mx-4 my-2">
<%= live_patch to: Routes.container_index_path(Endpoint, :edit_tags, container),
class: "text-primary-600 link" do %>
<i class="fa-fw fa-lg fas fa-tags"></i>
<% end %>
</div>
</:tag_actions>
<%= live_patch to: Routes.container_index_path(Endpoint, :edit, container), <%= live_patch to: Routes.container_index_path(Endpoint, :edit, container),
class: "text-primary-600 link", class: "text-primary-600 link",
data: [qa: "edit-#{container.id}"] do %> data: [qa: "edit-#{container.id}"] do %>
@ -58,3 +66,16 @@
/> />
</.modal> </.modal>
<% end %> <% end %>
<%= if @live_action == :edit_tags do %>
<.modal return_to={Routes.container_index_path(Endpoint, :index)}>
<.live_component
module={CanneryWeb.ContainerLive.EditTagsComponent}
id={@container.id}
title={@page_title}
action={@live_action}
container={@container}
current_user={@current_user}
/>
</.modal>
<% end %>

View File

@ -18,11 +18,10 @@ defmodule CanneryWeb.ContainerLive.Show do
@impl true @impl true
def handle_params( def handle_params(
%{"id" => id}, %{"id" => id},
_, _session,
%{assigns: %{current_user: current_user, live_action: live_action}} = socket %{assigns: %{current_user: current_user}} = socket
) do ) do
{:noreply, {:noreply, socket |> render_container(id, current_user)}
socket |> assign(page_title: page_title(live_action)) |> render_container(id, current_user)}
end end
@impl true @impl true
@ -54,7 +53,7 @@ defmodule CanneryWeb.ContainerLive.Show do
@impl true @impl true
def handle_event( def handle_event(
"delete_container", "delete_container",
_, _params,
%{assigns: %{container: container, current_user: current_user}} = socket %{assigns: %{container: container, current_user: current_user}} = socket
) do ) do
socket = socket =
@ -85,16 +84,20 @@ defmodule CanneryWeb.ContainerLive.Show do
{:noreply, socket} {:noreply, socket}
end end
defp page_title(:show), do: gettext("Show Container")
defp page_title(:edit), do: gettext("Edit Container")
defp page_title(:add_tag), do: gettext("Add Tag to Container")
@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(socket, id, current_user) do defp render_container(%{assigns: %{live_action: live_action}} = socket, id, current_user) do
%{name: container_name} =
container = container =
Containers.get_container!(id, current_user) Containers.get_container!(id, current_user)
|> Repo.preload([:ammo_groups, :tags], force: true) |> Repo.preload([:ammo_groups, :tags], force: true)
socket |> assign(container: container) page_title =
case live_action do
:show -> gettext("Show %{name}", name: container_name)
:edit -> gettext("Edit %{name}", name: container_name)
:edit_tags -> gettext("Edit %{name} tags", name: container_name)
end
socket |> assign(container: container, page_title: page_title)
end end
end end

View File

@ -51,47 +51,40 @@
</h2> </h2>
<%= live_patch(dgettext("actions", "Why not add one?"), <%= live_patch(dgettext("actions", "Why not add one?"),
to: Routes.container_show_path(Endpoint, :add_tag, @container), to: Routes.container_show_path(Endpoint, :edit_tags, @container),
class: "btn btn-primary" class: "btn btn-primary"
) %> ) %>
</div> </div>
<% else %> <% else %>
<h2 class="mb-4 title text-xl text-primary-600"> <div class="flex flex-wrap justify-center items-center">
<%= gettext("Tags") %>
</h2>
<%= for tag <- @container.tags do %> <%= for tag <- @container.tags do %>
<.tag_card tag={tag}> <.simple_tag_card tag={tag} />
<%= link to: "#",
class: "text-primary-600 link",
phx_click: "delete_tag",
phx_value_tag_id: tag.id,
data: [
confirm:
dgettext(
"prompts",
"Are you sure you want to remove the %{tag_name} tag from %{container_name}?",
tag_name: tag.name,
container_name: @container.name
)
] do %>
<i class="fa-fw fa-lg fas fa-trash"></i>
<% end %> <% end %>
</.tag_card>
<div class="mx-4 my-2">
<%= live_patch to: Routes.container_show_path(Endpoint, :edit_tags, @container),
class: "text-primary-600 link" do %>
<i class="fa-fw fa-lg fas fa-tags"></i>
<% end %> <% end %>
</div>
</div>
<% end %> <% end %>
<hr class="mb-4 hr" /> <hr class="mb-4 hr" />
<p> <div>
<%= if @container.ammo_groups |> Enum.empty?() do %> <%= if @container.ammo_groups |> Enum.empty?() do %>
<h2 class="mx-8 my-4 title text-lg text-primary-600">
<%= gettext("No ammo groups in this container") %> <%= gettext("No ammo groups in this container") %>
</h2>
<% else %> <% else %>
<div class="flex flex-wrap justify-center items-center">
<%= for ammo_group <- @container.ammo_groups do %> <%= for ammo_group <- @container.ammo_groups do %>
<.ammo_group_card ammo_group={ammo_group} /> <.ammo_group_card ammo_group={ammo_group} />
<% end %> <% end %>
</div>
<% end %> <% end %>
</p> </div>
</div> </div>
<%= if @live_action in [:edit] do %> <%= if @live_action in [:edit] do %>
@ -108,10 +101,10 @@
</.modal> </.modal>
<% end %> <% end %>
<%= if @live_action == :add_tag do %> <%= if @live_action == :edit_tags do %>
<.modal return_to={Routes.container_show_path(Endpoint, :show, @container)}> <.modal return_to={Routes.container_show_path(Endpoint, :show, @container)}>
<.live_component <.live_component
module={CanneryWeb.ContainerLive.AddTagComponent} module={CanneryWeb.ContainerLive.EditTagsComponent}
id={@container.id} id={@container.id}
title={@page_title} title={@page_title}
action={@live_action} action={@live_action}

View File

@ -9,7 +9,13 @@ defmodule CanneryWeb.HomeLive do
@impl true @impl true
def mount(_params, session, socket) do def mount(_params, session, socket) do
admins = Accounts.list_users_by_role(:admin) admins = Accounts.list_users_by_role(:admin)
{:ok, socket |> assign_defaults(session) |> assign(query: "", results: %{}, admins: admins)}
socket =
socket
|> assign_defaults(session)
|> assign(page_title: "Home", query: "", results: %{}, admins: admins)
{:ok, socket}
end end
@impl true @impl true
@ -23,7 +29,7 @@ defmodule CanneryWeb.HomeLive do
%{^query => vsn} -> %{^query => vsn} ->
{:noreply, redirect(socket, external: "https://hexdocs.pm/#{query}/#{vsn}")} {:noreply, redirect(socket, external: "https://hexdocs.pm/#{query}/#{vsn}")}
_ -> _no_query ->
{:noreply, {:noreply,
socket socket
|> put_flash(:error, "No dependencies found matching \"#{query}\"") |> put_flash(:error, "No dependencies found matching \"#{query}\"")
@ -121,11 +127,15 @@ defmodule CanneryWeb.HomeLive do
</p> </p>
</li> </li>
<li class="flex flex-row justify-center space-x-2"> <li class="flex flex-row justify-center items-center space-x-2">
<b>Version:</b> <b>Version:</b>
<p> <%= link class: "flex flex-row justify-center items-center space-x-2 hover:underline",
0.1.0 to: "https://gitea.bubbletea.dev/shibao/cannery/src/branch/stable/CHANGELOG.md",
</p> target: "_blank",
rel: "noopener noreferrer" do %>
<p>0.4.1</p>
<i class="fas fa-md fa-info-circle"></i>
<% end %>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -6,7 +6,7 @@
let={f} let={f}
for={@changeset} for={@changeset}
id="invite-form" id="invite-form"
class="flex flex-col sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center" class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
phx-target={@myself} phx-target={@myself}
phx-change="validate" phx-change="validate"
phx-submit="save" phx-submit="save"

View File

@ -40,7 +40,7 @@ defmodule CanneryWeb.InviteLive.Index do
end end
defp apply_action(socket, :index, _params) do defp apply_action(socket, :index, _params) do
socket |> assign(page_title: gettext("Listing Invites"), invite: nil) socket |> assign(page_title: gettext("Invites"), invite: nil)
end end
@impl true @impl true
@ -119,7 +119,7 @@ defmodule CanneryWeb.InviteLive.Index do
end end
@impl true @impl true
def handle_event("copy_to_clipboard", _, socket) do def handle_event("copy_to_clipboard", _params, socket) do
prompt = dgettext("prompts", "Copied to clipboard") prompt = dgettext("prompts", "Copied to clipboard")
{:noreply, socket |> put_flash(:info, prompt)} {:noreply, socket |> put_flash(:info, prompt)}
end end

View File

@ -38,32 +38,45 @@ defmodule CanneryWeb.LiveHelpers do
""" """
def modal(assigns) do def modal(assigns) do
~H""" ~H"""
<%= live_patch to: @return_to,
id: "modal-bg",
class:
"fade-in fixed z-10 left-0 top-0
w-full h-full overflow-hidden
p-8 flex flex-col justify-center items-center cursor-auto",
style: "background-color: rgba(0,0,0,0.4);",
phx_remove: hide_modal()
do %>
<span class="hidden"></span>
<% end %>
<div <div
id="modal" id="modal"
class="fade-in fixed z-10 left-0 top-0 class="fixed z-10 left-0 top-0 pointer-events-none
w-full h-full overflow-hidden w-full h-full overflow-hidden
p-8 flex flex-col justify-center items-center" p-4 sm:p-8 flex flex-col justify-center items-center"
style="opacity: 1 !important; background-color: rgba(0,0,0,0.4);"
phx-remove={hide_modal()}
> >
<div <div
id="modal-content" id="modal-content"
class="fade-in-scale w-full max-w-3xl max-h-128 relative overflow-y-auto class="fade-in-scale w-full max-w-3xl relative
pointer-events-auto overflow-hidden
px-8 py-4 sm:py-8 flex flex-col justify-center items-center
flex flex-col justify-start items-center flex flex-col justify-start items-center
bg-white border-2 rounded-lg" bg-white border-2 rounded-lg"
phx-click-away={hide_modal()}
phx-window-keydown={hide_modal()}
phx-key="escape"
> >
<%= live_patch to: @return_to, <%= live_patch to: @return_to,
id: "close", id: "close",
class: class:
"absolute top-8 right-10 text-gray-500 hover:text-gray-800 transition-all duration-500 ease-in-out", "absolute top-8 right-10
phx_click: hide_modal() do %> text-gray-500 hover:text-gray-800
transition-all duration-500 ease-in-out",
phx_remove: hide_modal() do %>
<i class="fa-fw fa-lg fas fa-times"></i> <i class="fa-fw fa-lg fas fa-times"></i>
<% end %> <% end %>
<div class="w-full p-8 flex flex-col space-y-4 justify-start items-center"> <div
class="overflow-x-hidden overflow-y-auto w-full p-8 flex flex-col space-y-4 justify-start items-center"
>
<%= render_slot(@inner_block) %> <%= render_slot(@inner_block) %>
</div> </div>
</div> </div>
@ -74,6 +87,7 @@ defmodule CanneryWeb.LiveHelpers do
def hide_modal(js \\ %JS{}) do def hide_modal(js \\ %JS{}) do
js js
|> JS.hide(to: "#modal", transition: "fade-out") |> JS.hide(to: "#modal", transition: "fade-out")
|> JS.hide(to: "#modal-bg", transition: "fade-out")
|> JS.hide(to: "#modal-content", transition: "fade-out-scale") |> JS.hide(to: "#modal-content", transition: "fade-out-scale")
end end
end end

View File

@ -7,7 +7,7 @@
let={f} let={f}
for={@changeset} for={@changeset}
id="shot-group-form" id="shot-group-form"
class="flex flex-col sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center" class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
phx-target={@myself} phx-target={@myself}
phx-change="validate" phx-change="validate"
phx-submit="save" phx-submit="save"

View File

@ -25,7 +25,7 @@ defmodule CanneryWeb.RangeLive.Index do
%{"id" => id} %{"id" => id}
) do ) do
socket socket
|> assign(:page_title, gettext("Record shots")) |> assign(:page_title, gettext("Record Shots"))
|> assign(:ammo_group, Ammo.get_ammo_group!(id, current_user)) |> assign(:ammo_group, Ammo.get_ammo_group!(id, current_user))
end end
@ -77,6 +77,69 @@ defmodule CanneryWeb.RangeLive.Index do
ActivityLog.list_shot_groups(current_user) |> Repo.preload(ammo_group: :ammo_type) ActivityLog.list_shot_groups(current_user) |> Repo.preload(ammo_group: :ammo_type)
ammo_groups = Ammo.list_staged_ammo_groups(current_user) ammo_groups = Ammo.list_staged_ammo_groups(current_user)
socket |> assign(shot_groups: shot_groups, ammo_groups: ammo_groups)
columns = [
%{label: gettext("Ammo"), key: "name"},
%{label: gettext("Rounds shot"), key: "count"},
%{label: gettext("Notes"), key: "notes"},
%{label: gettext("Date"), key: "date"},
%{label: nil, key: "actions", sortable: false}
]
rows =
shot_groups
|> Enum.map(fn shot_group -> shot_group |> get_row_data_for_shot_group(columns) end)
socket
|> assign(ammo_groups: ammo_groups, columns: columns, rows: rows, shot_groups: shot_groups)
end
@spec get_row_data_for_shot_group(ShotGroup.t(), [map()]) :: [map()]
defp get_row_data_for_shot_group(%{date: date} = shot_group, columns) do
shot_group = shot_group |> Repo.preload(ammo_group: :ammo_type)
assigns = %{shot_group: shot_group}
columns
|> Enum.into(%{}, fn %{key: key} ->
value =
case key do
"name" ->
{shot_group.ammo_group.ammo_type.name,
live_patch(shot_group.ammo_group.ammo_type.name,
to: Routes.ammo_group_show_path(Endpoint, :show, shot_group.ammo_group),
class: "link"
)}
"date" ->
date |> display_date()
"actions" ->
~H"""
<div class="px-4 py-2 space-x-4 flex justify-center items-center">
<%= live_patch to: Routes.range_index_path(Endpoint, :edit, shot_group),
class: "text-primary-600 link",
data: [qa: "edit-#{shot_group.id}"] do %>
<i class="fa-fw fa-lg fas fa-edit"></i>
<% end %>
<%= link to: "#",
class: "text-primary-600 link",
phx_click: "delete",
phx_value_id: shot_group.id,
data: [
confirm: dgettext("prompts", "Are you sure you want to delete this shot record?"),
qa: "delete-#{shot_group.id}"
] do %>
<i class="fa-fw fa-lg fas fa-trash"></i>
<% end %>
</div>
"""
key ->
shot_group |> Map.get(key |> String.to_existing_atom())
end
{key, value}
end)
end end
end end

View File

@ -53,70 +53,12 @@
<%= gettext("Shot log") %> <%= gettext("Shot log") %>
</h1> </h1>
<div class="w-full overflow-x-auto border border-gray-600 rounded-lg shadow-lg bg-black"> <.live_component
<table class="min-w-full table-auto text-center bg-white"> module={CanneryWeb.Components.TableComponent}
<thead class="border-b border-primary-600"> id="shot_groups_index_table"
<tr> columns={@columns}
<th class="p-2"> rows={@rows}
<%= gettext("Ammo") %> />
</th>
<th class="p-2">
<%= gettext("Rounds shot") %>
</th>
<th class="p-2">
<%= gettext("Notes") %>
</th>
<th class="p-2">
<%= gettext("Date") %>
</th>
<th class="p-2"></th>
</tr>
</thead>
<tbody id="shot_groups">
<%= for shot_group <- @shot_groups do %>
<tr id={"shot_group-#{shot_group.id}"}>
<td class="p-2">
<%= live_patch(shot_group.ammo_group.ammo_type.name,
to: Routes.ammo_group_show_path(Endpoint, :show, shot_group.ammo_group),
class: "link"
) %>
</td>
<td class="p-2">
<%= shot_group.count %>
</td>
<td class="p-2">
<%= shot_group.notes %>
</td>
<td class="p-2">
<%= shot_group.date |> display_date() %>
</td>
<td class="p-2 w-full h-full space-x-2 flex justify-center items-center">
<div class="px-4 py-2 space-x-4 flex justify-center items-center">
<%= live_patch to: Routes.range_index_path(Endpoint, :edit, shot_group),
class: "text-primary-600 link",
data: [qa: "edit-#{shot_group.id}"] do %>
<i class="fa-fw fa-lg fas fa-edit"></i>
<% end %>
<%= link to: "#",
class: "text-primary-600 link",
phx_click: "delete",
phx_value_id: shot_group.id,
data: [
confirm: dgettext("prompts", "Are you sure you want to delete this shot record?"),
qa: "delete-#{shot_group.id}"
] do %>
<i class="fa-fw fa-lg fas fa-trash"></i>
<% end %>
</div>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% end %> <% end %>
</div> </div>

View File

@ -36,7 +36,7 @@ defmodule CanneryWeb.TagLive.FormComponent do
let={f} let={f}
for={@changeset} for={@changeset}
id="tag-form" id="tag-form"
class="flex flex-col sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center" class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
phx-target={@myself} phx-target={@myself}
phx-change="validate" phx-change="validate"
phx-submit="save" phx-submit="save"

View File

@ -15,7 +15,7 @@ defmodule CanneryWeb.TagLive.Index do
@impl true @impl true
def handle_params(params, _url, %{assigns: %{live_action: live_action}} = socket) do def handle_params(params, _url, %{assigns: %{live_action: live_action}} = socket) do
{:noreply, apply_action(socket, live_action, params)} {:noreply, apply_action(socket, live_action, params) |> display_tags}
end end
defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit, %{"id" => id}) do defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit, %{"id" => id}) do
@ -31,7 +31,7 @@ defmodule CanneryWeb.TagLive.Index do
end end
defp apply_action(socket, :index, _params) do defp apply_action(socket, :index, _params) do
socket |> assign(:page_title, gettext("Listing Tags")) |> assign(:tag, nil) socket |> assign(:page_title, gettext("Tags")) |> assign(:tag, nil)
end end
@impl true @impl true

View File

@ -64,10 +64,11 @@ defmodule CanneryWeb.Router do
live "/containers", ContainerLive.Index, :index live "/containers", ContainerLive.Index, :index
live "/containers/new", ContainerLive.Index, :new live "/containers/new", ContainerLive.Index, :new
live "/containers/:id/edit", ContainerLive.Index, :edit live "/containers/:id/edit", ContainerLive.Index, :edit
live "/containers/:id/edit_tags", ContainerLive.Index, :edit_tags
live "/containers/:id", ContainerLive.Show, :show live "/containers/:id", ContainerLive.Show, :show
live "/containers/:id/show/edit", ContainerLive.Show, :edit live "/containers/:id/show/edit", ContainerLive.Show, :edit
live "/containers/:id/show/add_tag", ContainerLive.Show, :add_tag live "/containers/:id/show/edit_tags", ContainerLive.Show, :edit_tags
live "/ammo_groups", AmmoGroupLive.Index, :index live "/ammo_groups", AmmoGroupLive.Index, :index
live "/ammo_groups/new", AmmoGroupLive.Index, :new live "/ammo_groups/new", AmmoGroupLive.Index, :new
@ -79,6 +80,7 @@ defmodule CanneryWeb.Router do
live "/ammo_groups/:id/show/edit", AmmoGroupLive.Show, :edit live "/ammo_groups/:id/show/edit", AmmoGroupLive.Show, :edit
live "/ammo_groups/:id/show/add_shot_group", AmmoGroupLive.Show, :add_shot_group live "/ammo_groups/:id/show/add_shot_group", AmmoGroupLive.Show, :add_shot_group
live "/ammo_groups/:id/show/move", AmmoGroupLive.Show, :move live "/ammo_groups/:id/show/move", AmmoGroupLive.Show, :move
live "/ammo_groups/:id/show/:shot_group_id/edit", AmmoGroupLive.Show, :edit_shot_group
live "/range", RangeLive.Index, :index live "/range", RangeLive.Index, :index
live "/range/:id/edit", RangeLive.Index, :edit live "/range/:id/edit", RangeLive.Index, :edit

View File

@ -29,7 +29,7 @@
<div <div
id="loading" id="loading"
class="absolute opacity-0 top-0 left-0 w-screen h-screen bg-white z-50 class="fixed opacity-0 top-0 left-0 w-screen h-screen bg-white z-50
flex flex-col justify-center items-center space-y-4 flex flex-col justify-center items-center space-y-4
transition-opacity ease-in-out duration-500" transition-opacity ease-in-out duration-500"
> >
@ -42,7 +42,7 @@
<div <div
id="disconnect" id="disconnect"
class="absolute opacity-0 top-0 left-0 w-screen h-screen bg-white z-50 class="fixed opacity-0 top-0 left-0 w-screen h-screen bg-white z-50
flex flex-col justify-center items-center space-y-4 flex flex-col justify-center items-center space-y-4
transition-opacity ease-in-out duration-500" transition-opacity ease-in-out duration-500"
> >

View File

@ -1,11 +1,12 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" class="m-0 p-0 w-full h-full"> <html lang="en" class="m-0 p-0 w-full h-full bg-white">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<%= csrf_meta_tag() %> <%= csrf_meta_tag() %>
<%= live_title_tag(assigns[:page_title] || "Cannery", suffix: "") %> <%= if(assigns |> Map.has_key?(:page_title), do: @page_title, else: "Cannery")
|> live_title_tag(suffix: " | Cannery") %>
<link phx-track-static rel="stylesheet" href={Routes.static_path(@conn, "/css/app.css")} /> <link phx-track-static rel="stylesheet" href={Routes.static_path(@conn, "/css/app.css")} />
<script <script
defer defer
@ -16,7 +17,7 @@
</script> </script>
</head> </head>
<body class="m-0 p-0 w-full h-full"> <body class="m-0 p-0 w-full h-full bg-white">
<%= @inner_content %> <%= @inner_content %>
</body> </body>
</html> </html>

View File

@ -7,7 +7,7 @@
Routes.user_confirmation_path(@conn, :create), Routes.user_confirmation_path(@conn, :create),
[ [
class: class:
"flex flex-col sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center" "flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
], ],
fn f -> %> fn f -> %>
<%= label(f, :email, class: "title text-lg text-primary-600") %> <%= label(f, :email, class: "title text-lg text-primary-600") %>

View File

@ -7,7 +7,7 @@
Routes.user_registration_path(@conn, :create), Routes.user_registration_path(@conn, :create),
[ [
class: class:
"flex flex-col sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center" "flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
], ],
fn f -> %> fn f -> %>
<%= if @changeset.action && not @changeset.valid? do %> <%= if @changeset.action && not @changeset.valid? do %>

View File

@ -7,7 +7,7 @@
Routes.user_reset_password_path(@conn, :update, @token), Routes.user_reset_password_path(@conn, :update, @token),
[ [
class: class:
"flex flex-col sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center" "flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
], ],
fn f -> %> fn f -> %>
<%= if @changeset.action && not @changeset.valid? do %> <%= if @changeset.action && not @changeset.valid? do %>

View File

@ -7,7 +7,7 @@
Routes.user_reset_password_path(@conn, :create), Routes.user_reset_password_path(@conn, :create),
[ [
class: class:
"flex flex-col sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center" "flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
], ],
fn f -> %> fn f -> %>
<%= label(f, :email, class: "title text-lg text-primary-600") %> <%= label(f, :email, class: "title text-lg text-primary-600") %>

View File

@ -8,7 +8,7 @@
[ [
as: :user, as: :user,
class: class:
"flex flex-col sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center" "flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
], ],
fn f -> %> fn f -> %>
<%= if @error_message do %> <%= if @error_message do %>

View File

@ -9,7 +9,7 @@
Routes.user_settings_path(@conn, :update), Routes.user_settings_path(@conn, :update),
[ [
class: class:
"pb-4 text-center flex flex-col sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center" "flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
], ],
fn f -> %> fn f -> %>
<h3 class="title text-primary-600 text-lg col-span-3"> <h3 class="title text-primary-600 text-lg col-span-3">
@ -53,7 +53,7 @@
Routes.user_settings_path(@conn, :update), Routes.user_settings_path(@conn, :update),
[ [
class: class:
"pb-4 text-center flex flex-col sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center" "flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
], ],
fn f -> %> fn f -> %>
<h3 class="title text-primary-600 text-lg col-span-3"> <h3 class="title text-primary-600 text-lg col-span-3">

View File

@ -2,7 +2,6 @@ defmodule CanneryWeb.EmailView do
@moduledoc """ @moduledoc """
A view for email-related helper functions A view for email-related helper functions
""" """
alias CanneryWeb.{Endpoint, HomeLive}
use CanneryWeb, :view use CanneryWeb, :view
alias CanneryWeb.{Endpoint, HomeLive}
end end

View File

@ -8,7 +8,7 @@ defmodule CanneryWeb.ErrorView do
case error_path do case error_path do
"404.html" -> dgettext("errors", "Not found") "404.html" -> dgettext("errors", "Not found")
"401.html" -> dgettext("errors", "Unauthorized") "401.html" -> dgettext("errors", "Unauthorized")
_ -> dgettext("errors", "Internal Server Error") _other_template -> dgettext("errors", "Internal Server Error")
end end
render("error.html", %{error_string: error_string}) render("error.html", %{error_string: error_string})

View File

@ -7,6 +7,8 @@ defmodule CanneryWeb.ViewHelpers do
import Phoenix.LiveView.Helpers import Phoenix.LiveView.Helpers
@id_length 16
@doc """ @doc """
Returns a <time> element that renders the naivedatetime in the user's local Returns a <time> element that renders the naivedatetime in the user's local
timezone with Alpine.js timezone with Alpine.js
@ -16,11 +18,12 @@ defmodule CanneryWeb.ViewHelpers do
def display_datetime(datetime) do def display_datetime(datetime) do
assigns = %{ assigns = %{
id: :crypto.strong_rand_bytes(@id_length) |> Base.url_encode64(),
datetime: datetime |> DateTime.from_naive!("Etc/UTC") |> DateTime.to_iso8601(:extended) datetime: datetime |> DateTime.from_naive!("Etc/UTC") |> DateTime.to_iso8601(:extended)
} }
~H""" ~H"""
<time datetime={@datetime} x-data={"{ <time id={@id} datetime={@datetime} x-data={"{
date: date:
Intl.DateTimeFormat([], {dateStyle: 'short', timeStyle: 'long'}) Intl.DateTimeFormat([], {dateStyle: 'short', timeStyle: 'long'})
.format(new Date(\"#{@datetime}\")) .format(new Date(\"#{@datetime}\"))
@ -38,10 +41,13 @@ defmodule CanneryWeb.ViewHelpers do
def display_date(nil), do: "" def display_date(nil), do: ""
def display_date(date) do def display_date(date) do
assigns = %{date: date |> Date.to_iso8601(:extended)} assigns = %{
id: :crypto.strong_rand_bytes(@id_length) |> Base.url_encode64(),
date: date |> Date.to_iso8601(:extended)
}
~H""" ~H"""
<time datetime={@date} x-data={"{ <time id={@id} datetime={@date} x-data={"{
date: date:
Intl.DateTimeFormat([], {timeZone: 'Etc/UTC', dateStyle: 'short'}).format(new Date(\"#{@date}\")) Intl.DateTimeFormat([], {timeZone: 'Etc/UTC', dateStyle: 'short'}).format(new Date(\"#{@date}\"))
}"} x-text="date"> }"} x-text="date">

View File

@ -4,7 +4,7 @@ defmodule Cannery.MixProject do
def project do def project do
[ [
app: :cannery, app: :cannery,
version: "0.1.0", version: "0.4.1",
elixir: "~> 1.12", elixir: "~> 1.12",
elixirc_paths: elixirc_paths(Mix.env()), elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:gettext] ++ Mix.compilers(), compilers: [:gettext] ++ Mix.compilers(),

View File

@ -11,12 +11,12 @@ msgid ""
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.ex:42 #: lib/cannery_web/live/ammo_group_live/index.ex:44
msgid "Add Ammo" msgid "Add Ammo"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.html.heex:12 #: lib/cannery_web/live/ammo_group_live/index.html.heex:24
msgid "Add your first box!" msgid "Add your first box!"
msgstr "" msgstr ""
@ -81,7 +81,7 @@ msgid "Make your first tag!"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.html.heex:17 #: lib/cannery_web/live/ammo_group_live/index.html.heex:42
msgid "New Ammo group" msgid "New Ammo group"
msgstr "" msgstr ""
@ -124,9 +124,9 @@ msgid "Reset password"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/add_shot_group_component.html.heex:42 #: lib/cannery_web/components/add_shot_group_component.html.heex:46
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:54 #: lib/cannery_web/live/ammo_group_live/form_component.html.heex:73
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:172 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:156
#: lib/cannery_web/live/container_live/form_component.html.heex:50 #: lib/cannery_web/live/container_live/form_component.html.heex:50
#: lib/cannery_web/live/invite_live/form_component.html.heex:28 #: lib/cannery_web/live/invite_live/form_component.html.heex:28
#: lib/cannery_web/live/range_live/form_component.html.heex:40 #: lib/cannery_web/live/range_live/form_component.html.heex:40
@ -145,7 +145,7 @@ msgid "Why not add one?"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/add_tag_component.html.heex:17 #: lib/cannery_web/live/container_live/edit_tags_component.html.heex:52
msgid "Add" msgid "Add"
msgstr "" msgstr ""
@ -160,29 +160,29 @@ msgid "Why not get some ready to shoot?"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.html.heex:85 #: lib/cannery_web/live/ammo_group_live/index.ex:134
#: lib/cannery_web/live/ammo_group_live/show.html.heex:67 #: lib/cannery_web/live/ammo_group_live/show.html.heex:86
#: lib/cannery_web/live/range_live/index.html.heex:36 #: lib/cannery_web/live/range_live/index.html.heex:36
msgid "Record shots" msgid "Record shots"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.html.heex:31 #: lib/cannery_web/live/ammo_group_live/show.html.heex:50
msgid "Ammo Details" msgid "Ammo Details"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/move_ammo_group_component.html.heex:12 #: lib/cannery_web/components/move_ammo_group_component.ex:89
msgid "Add another container!" msgid "Add another container!"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.html.heex:61 #: lib/cannery_web/live/ammo_group_live/show.html.heex:80
msgid "Move containers" msgid "Move containers"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/move_ammo_group_component.html.heex:60 #: lib/cannery_web/components/move_ammo_group_component.ex:127
msgid "Select" msgid "Select"
msgstr "" msgstr ""
@ -190,3 +190,14 @@ msgstr ""
#: lib/cannery_web/live/invite_live/index.html.heex:33 #: lib/cannery_web/live/invite_live/index.html.heex:33
msgid "Copy to clipboard" msgid "Copy to clipboard"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.html.heex:18
#: lib/cannery_web/live/ammo_group_live/index.html.heex:36
msgid "add a container first"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:66
msgid "Create"
msgstr ""

View File

@ -11,12 +11,12 @@ msgid ""
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:57 #: lib/cannery_web/live/home_live.ex:63
msgid "%{name} lets you easily keep an eye on your ammo levels before and after range day" msgid "%{name} lets you easily keep an eye on your ammo levels before and after range day"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:79 #: lib/cannery_web/live/home_live.ex:85
msgid "Access from any internet-capable device" msgid "Access from any internet-capable device"
msgstr "" msgstr ""
@ -26,20 +26,20 @@ msgid "Admins"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:93 #: lib/cannery_web/live/home_live.ex:99
msgid "Admins:" msgid "Admins:"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/topbar.ex:52 #: lib/cannery_web/components/topbar.ex:52
#: lib/cannery_web/live/ammo_group_live/index.html.heex:3 #: lib/cannery_web/live/ammo_group_live/index.html.heex:3
#: lib/cannery_web/live/range_live/index.html.heex:61 #: lib/cannery_web/live/range_live/index.ex:82
msgid "Ammo" msgid "Ammo"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:21 #: lib/cannery_web/live/ammo_group_live/form_component.html.heex:21
#: lib/cannery_web/live/ammo_group_live/index.html.heex:27 #: lib/cannery_web/live/ammo_group_live/index.ex:80
msgid "Ammo type" msgid "Ammo type"
msgstr "" msgstr ""
@ -54,18 +54,19 @@ msgid "Background color"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:154 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:140
#: lib/cannery_web/live/ammo_type_live/index.ex:67 #: lib/cannery_web/live/ammo_type_live/index.ex:71
#: lib/cannery_web/live/ammo_type_live/show.html.heex:55
msgid "Blank" msgid "Blank"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:74 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:68
msgid "Brass" msgid "Brass"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:46 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:44
#: lib/cannery_web/live/ammo_type_live/index.ex:53 #: lib/cannery_web/live/ammo_type_live/index.ex:53
#: lib/cannery_web/live/ammo_type_live/show.html.heex:41 #: lib/cannery_web/live/ammo_type_live/show.html.heex:41
msgid "Bullet core" msgid "Bullet core"
@ -79,48 +80,50 @@ msgid "Bullet type"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:62 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:58
#: lib/cannery_web/live/ammo_type_live/index.ex:55 #: lib/cannery_web/live/ammo_type_live/index.ex:55
#: lib/cannery_web/live/ammo_type_live/show.html.heex:43 #: lib/cannery_web/live/ammo_type_live/show.html.heex:43
msgid "Caliber" msgid "Caliber"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:55 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:51
#: lib/cannery_web/live/ammo_type_live/index.ex:54 #: lib/cannery_web/live/ammo_type_live/index.ex:54
#: lib/cannery_web/live/ammo_type_live/show.html.heex:42 #: lib/cannery_web/live/ammo_type_live/show.html.heex:42
msgid "Cartridge" msgid "Cartridge"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:69 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:65
#: lib/cannery_web/live/ammo_type_live/index.ex:56 #: lib/cannery_web/live/ammo_type_live/index.ex:56
#: lib/cannery_web/live/ammo_type_live/show.html.heex:44 #: lib/cannery_web/live/ammo_type_live/show.html.heex:44
msgid "Case material" msgid "Case material"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/move_ammo_group_component.html.heex:22 #: lib/cannery_web/components/move_ammo_group_component.ex:67
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:48 #: lib/cannery_web/live/ammo_group_live/form_component.html.heex:48
#: lib/cannery_web/live/ammo_group_live/index.html.heex:42 #: lib/cannery_web/live/ammo_group_live/index.ex:85
msgid "Container" msgid "Container"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/topbar.ex:46 #: lib/cannery_web/components/topbar.ex:46
#: lib/cannery_web/live/container_live/index.ex:38
#: lib/cannery_web/live/container_live/index.html.heex:3 #: lib/cannery_web/live/container_live/index.html.heex:3
msgid "Containers" msgid "Containers"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:158 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:144
#: lib/cannery_web/live/ammo_type_live/index.ex:68 #: lib/cannery_web/live/ammo_type_live/index.ex:72
#: lib/cannery_web/live/ammo_type_live/show.html.heex:56
msgid "Corrosive" msgid "Corrosive"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:27 #: lib/cannery_web/live/ammo_group_live/form_component.html.heex:27
#: lib/cannery_web/live/ammo_group_live/index.html.heex:30 #: lib/cannery_web/live/ammo_group_live/index.ex:81
msgid "Count" msgid "Count"
msgstr "" msgstr ""
@ -137,7 +140,7 @@ msgid "Description"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/container_card.ex:27 #: lib/cannery_web/components/container_card.ex:31
#: lib/cannery_web/live/container_live/show.html.heex:8 #: lib/cannery_web/live/container_live/show.html.heex:8
msgid "Description:" msgid "Description:"
msgstr "" msgstr ""
@ -148,13 +151,13 @@ msgid "Disable"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:54 #: lib/cannery_web/live/home_live.ex:60
msgid "Easy to Use:" msgid "Easy to Use:"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.ex:36 #: lib/cannery_web/live/ammo_group_live/index.ex:38
#: lib/cannery_web/live/ammo_group_live/show.ex:55 #: lib/cannery_web/live/ammo_group_live/show.ex:42
msgid "Edit Ammo group" msgid "Edit Ammo group"
msgstr "" msgstr ""
@ -164,12 +167,6 @@ msgstr ""
msgid "Edit Ammo type" msgid "Edit Ammo type"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/index.ex:24
#: lib/cannery_web/live/container_live/show.ex:89
msgid "Edit Container"
msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/invite_live/index.ex:35 #: lib/cannery_web/live/invite_live/index.ex:35
msgid "Edit Invite" msgid "Edit Invite"
@ -191,25 +188,26 @@ msgid "Example bullet type abbreviations"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:42 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:40
msgid "FMJ" msgid "FMJ"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:113 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:103
#: lib/cannery_web/live/ammo_type_live/index.ex:61 #: lib/cannery_web/live/ammo_type_live/index.ex:65
#: lib/cannery_web/live/ammo_type_live/show.html.heex:49 #: lib/cannery_web/live/ammo_type_live/show.html.heex:49
msgid "Grains" msgid "Grains"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:150 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:136
#: lib/cannery_web/live/ammo_type_live/index.ex:66 #: lib/cannery_web/live/ammo_type_live/index.ex:70
#: lib/cannery_web/live/ammo_type_live/show.html.heex:54
msgid "Incendiary" msgid "Incendiary"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:88 #: lib/cannery_web/live/home_live.ex:94
msgid "Instance Information" msgid "Instance Information"
msgstr "" msgstr ""
@ -219,12 +217,13 @@ msgid "Invite Disabled"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:119 #: lib/cannery_web/live/home_live.ex:125
msgid "Invite Only" msgid "Invite Only"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/topbar.ex:71 #: lib/cannery_web/components/topbar.ex:71
#: lib/cannery_web/live/invite_live/index.ex:43
#: lib/cannery_web/live/invite_live/index.html.heex:3 #: lib/cannery_web/live/invite_live/index.html.heex:3
msgid "Invites" msgid "Invites"
msgstr "" msgstr ""
@ -235,33 +234,13 @@ msgid "Keep me logged in for 60 days"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/index.ex:34 #: lib/cannery_web/components/move_ammo_group_component.ex:69
msgid "Listing Ammo types"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/index.ex:33
msgid "Listing Containers"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/invite_live/index.ex:43
msgid "Listing Invites"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/tag_live/index.ex:34
msgid "Listing Tags"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/components/move_ammo_group_component.html.heex:30
#: lib/cannery_web/live/container_live/form_component.html.heex:42 #: lib/cannery_web/live/container_live/form_component.html.heex:42
msgid "Location" msgid "Location"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/container_card.ex:39 #: lib/cannery_web/components/container_card.ex:43
#: lib/cannery_web/live/container_live/show.html.heex:20 #: lib/cannery_web/live/container_live/show.html.heex:20
msgid "Location:" msgid "Location:"
msgstr "" msgstr ""
@ -277,8 +256,9 @@ msgid "Manage"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:162 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:148
#: lib/cannery_web/live/ammo_type_live/index.ex:69 #: lib/cannery_web/live/ammo_type_live/index.ex:73
#: lib/cannery_web/live/ammo_type_live/show.html.heex:57
msgid "Manufacturer" msgid "Manufacturer"
msgstr "" msgstr ""
@ -307,7 +287,7 @@ msgid "New Ammo type"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/index.ex:29 #: lib/cannery_web/live/container_live/index.ex:33
msgid "New Container" msgid "New Container"
msgstr "" msgstr ""
@ -332,12 +312,12 @@ msgid "No Ammo Types"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/show.html.heex:109 #: lib/cannery_web/live/ammo_type_live/show.html.heex:114
msgid "No ammo for this type" msgid "No ammo for this type"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/show.html.heex:88 #: lib/cannery_web/live/container_live/show.html.heex:78
msgid "No ammo groups in this container" msgid "No ammo groups in this container"
msgstr "" msgstr ""
@ -352,6 +332,7 @@ msgid "No invites"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/edit_tags_component.html.heex:30
#: lib/cannery_web/live/tag_live/index.html.heex:10 #: lib/cannery_web/live/tag_live/index.html.heex:10
msgid "No tags" msgid "No tags"
msgstr "" msgstr ""
@ -359,15 +340,15 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/add_shot_group_component.html.heex:30 #: lib/cannery_web/components/add_shot_group_component.html.heex:30
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:41 #: lib/cannery_web/live/ammo_group_live/form_component.html.heex:41
#: lib/cannery_web/live/ammo_group_live/index.html.heex:36 #: lib/cannery_web/live/ammo_group_live/show.ex:90
#: lib/cannery_web/live/range_live/form_component.html.heex:29 #: lib/cannery_web/live/range_live/form_component.html.heex:29
#: lib/cannery_web/live/range_live/index.html.heex:67 #: lib/cannery_web/live/range_live/index.ex:84
msgid "Notes" msgid "Notes"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/ammo_group_card.ex:35 #: lib/cannery_web/components/ammo_group_card.ex:35
#: lib/cannery_web/live/ammo_group_live/show.html.heex:14 #: lib/cannery_web/live/ammo_group_live/show.html.heex:24
msgid "Notes:" msgid "Notes:"
msgstr "" msgstr ""
@ -377,48 +358,42 @@ msgid "On the bookshelf"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:121 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:111
#: lib/cannery_web/live/ammo_type_live/index.ex:62 #: lib/cannery_web/live/ammo_type_live/index.ex:66
#: lib/cannery_web/live/ammo_type_live/show.html.heex:50 #: lib/cannery_web/live/ammo_type_live/show.html.heex:50
msgid "Pressure" msgid "Pressure"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:34 #: lib/cannery_web/live/ammo_group_live/form_component.html.heex:34
#: lib/cannery_web/live/ammo_group_live/index.html.heex:33 #: lib/cannery_web/live/ammo_group_live/index.ex:82
msgid "Price paid" msgid "Price paid"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/ammo_group_card.ex:42 #: lib/cannery_web/components/ammo_group_card.ex:42
#: lib/cannery_web/live/ammo_group_live/show.html.heex:21
msgid "Price paid:" msgid "Price paid:"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:128 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:118
#: lib/cannery_web/live/ammo_type_live/index.ex:63 #: lib/cannery_web/live/ammo_type_live/index.ex:67
#: lib/cannery_web/live/ammo_type_live/show.html.heex:51 #: lib/cannery_web/live/ammo_type_live/show.html.heex:51
msgid "Primer type" msgid "Primer type"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:118 #: lib/cannery_web/live/home_live.ex:124
msgid "Public Signups" msgid "Public Signups"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/index.ex:64 #: lib/cannery_web/live/home_live.ex:72
msgid "Rimfire"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:66
msgid "Secure:" msgid "Secure:"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:69 #: lib/cannery_web/live/home_live.ex:75
msgid "Self-host your own instance, or use an instance from someone you trust." msgid "Self-host your own instance, or use an instance from someone you trust."
msgstr "" msgstr ""
@ -428,12 +403,13 @@ msgid "Set Unlimited"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_settings_controller.ex:10
#: lib/cannery_web/templates/user_settings/edit.html.heex:3 #: lib/cannery_web/templates/user_settings/edit.html.heex:3
msgid "Settings" msgid "Settings"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.ex:54 #: lib/cannery_web/live/ammo_group_live/show.ex:41
msgid "Show Ammo group" msgid "Show Ammo group"
msgstr "" msgstr ""
@ -443,28 +419,23 @@ msgid "Show Ammo type"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/show.ex:88 #: lib/cannery_web/live/home_live.ex:82
msgid "Show Container"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:76
msgid "Simple:" msgid "Simple:"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:51 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:47
msgid "Steel" msgid "Steel"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.html.heex:79 #: lib/cannery_web/live/ammo_group_live/show.html.heex:98
msgid "Stored in" msgid "Stored in"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/topbar.ex:40 #: lib/cannery_web/components/topbar.ex:40
#: lib/cannery_web/live/container_live/show.html.heex:60 #: lib/cannery_web/live/tag_live/index.ex:34
#: lib/cannery_web/live/tag_live/index.html.heex:3 #: lib/cannery_web/live/tag_live/index.html.heex:3
msgid "Tags" msgid "Tags"
msgstr "" msgstr ""
@ -480,29 +451,30 @@ msgid "Text color"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:45 #: lib/cannery_web/live/home_live.ex:51
msgid "The self-hosted firearm tracker website" msgid "The self-hosted firearm tracker website"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.html.heex:84 #: lib/cannery_web/live/ammo_group_live/show.html.heex:103
msgid "This ammo group is not in a container" msgid "This ammo group is not in a container"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:146 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:132
#: lib/cannery_web/live/ammo_type_live/index.ex:65 #: lib/cannery_web/live/ammo_type_live/index.ex:69
#: lib/cannery_web/live/ammo_type_live/show.html.heex:53
msgid "Tracer" msgid "Tracer"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/move_ammo_group_component.html.heex:26 #: lib/cannery_web/components/move_ammo_group_component.ex:68
#: lib/cannery_web/live/container_live/form_component.html.heex:35 #: lib/cannery_web/live/container_live/form_component.html.heex:35
msgid "Type" msgid "Type"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/container_card.ex:33 #: lib/cannery_web/components/container_card.ex:37
#: lib/cannery_web/live/container_live/show.html.heex:14 #: lib/cannery_web/live/container_live/show.html.heex:14
msgid "Type:" msgid "Type:"
msgstr "" msgstr ""
@ -523,20 +495,15 @@ msgid "Uses left"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:41 #: lib/cannery_web/live/home_live.ex:47
msgid "Welcome to %{name}" msgid "Welcome to %{name}"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:70 #: lib/cannery_web/live/home_live.ex:76
msgid "Your data stays with you, period" msgid "Your data stays with you, period"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/show.ex:90
msgid "Add Tag to Container"
msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/show.html.heex:49 #: lib/cannery_web/live/container_live/show.html.heex:49
msgid "No tags for this container" msgid "No tags for this container"
@ -544,7 +511,7 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/topbar.ex:64 #: lib/cannery_web/components/topbar.ex:64
#: lib/cannery_web/live/ammo_group_live/index.html.heex:39 #: lib/cannery_web/live/ammo_group_live/index.ex:84
msgid "Range" msgid "Range"
msgstr "" msgstr ""
@ -554,7 +521,8 @@ msgid "Range day"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/range_live/index.html.heex:70 #: lib/cannery_web/live/ammo_group_live/show.ex:91
#: lib/cannery_web/live/range_live/index.ex:85
msgid "Date" msgid "Date"
msgstr "" msgstr ""
@ -569,26 +537,20 @@ msgid "No ammo staged"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.html.heex:58 #: lib/cannery_web/live/ammo_group_live/show.html.heex:77
#: lib/cannery_web/live/range_live/index.html.heex:33 #: lib/cannery_web/live/range_live/index.html.heex:33
msgid "Stage for range" msgid "Stage for range"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.html.heex:57 #: lib/cannery_web/live/ammo_group_live/show.html.heex:76
#: lib/cannery_web/live/range_live/index.html.heex:32 #: lib/cannery_web/live/range_live/index.html.heex:32
msgid "Unstage from range" msgid "Unstage from range"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.ex:52
msgid "Add Shot group"
msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/add_shot_group_component.html.heex:3 #: lib/cannery_web/components/add_shot_group_component.html.heex:3
#: lib/cannery_web/live/ammo_group_live/index.ex:24 #: lib/cannery_web/live/ammo_group_live/index.ex:26
#: lib/cannery_web/live/range_live/index.ex:28
msgid "Record shots" msgid "Record shots"
msgstr "" msgstr ""
@ -598,7 +560,7 @@ msgid "Ammo Types"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.ex:47 #: lib/cannery_web/live/ammo_group_live/index.ex:49
msgid "Ammo groups" msgid "Ammo groups"
msgstr "" msgstr ""
@ -609,6 +571,7 @@ msgid "Date (UTC)"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.ex:39
#: lib/cannery_web/live/range_live/index.ex:34 #: lib/cannery_web/live/range_live/index.ex:34
msgid "Edit Shot Records" msgid "Edit Shot Records"
msgstr "" msgstr ""
@ -629,7 +592,8 @@ msgid "Rounds left"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/range_live/index.html.heex:64 #: lib/cannery_web/live/ammo_group_live/show.ex:89
#: lib/cannery_web/live/range_live/index.ex:83
msgid "Rounds shot" msgid "Rounds shot"
msgstr "" msgstr ""
@ -639,18 +603,18 @@ msgid "Shot Records"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.ex:30 #: lib/cannery_web/live/ammo_group_live/index.ex:32
#: lib/cannery_web/live/ammo_group_live/show.ex:53 #: lib/cannery_web/live/ammo_group_live/show.ex:40
msgid "Move Ammo group" msgid "Move Ammo group"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/move_ammo_group_component.html.heex:3 #: lib/cannery_web/components/move_ammo_group_component.ex:80
msgid "Move ammo" msgid "Move ammo"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/move_ammo_group_component.html.heex:8 #: lib/cannery_web/components/move_ammo_group_component.ex:85
msgid "No other containers" msgid "No other containers"
msgstr "" msgstr ""
@ -661,48 +625,50 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/ammo_group_card.ex:43 #: lib/cannery_web/components/ammo_group_card.ex:43
#: lib/cannery_web/live/ammo_group_live/index.html.heex:64 #: lib/cannery_web/live/ammo_group_live/index.ex:117
#: lib/cannery_web/live/ammo_group_live/show.html.heex:22 #: lib/cannery_web/live/ammo_group_live/show.html.heex:32
#: lib/cannery_web/live/ammo_group_live/show.html.heex:39
#: lib/cannery_web/live/ammo_type_live/show.html.heex:98 #: lib/cannery_web/live/ammo_type_live/show.html.heex:98
msgid "$%{amount}" msgid "$%{amount}"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:83 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:75
msgid "Bimetal" msgid "Bimetal"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:78 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:72
#: lib/cannery_web/live/ammo_type_live/index.ex:57 #: lib/cannery_web/live/ammo_type_live/index.ex:57
#: lib/cannery_web/live/ammo_type_live/show.html.heex:45 #: lib/cannery_web/live/ammo_type_live/show.html.heex:45
msgid "Jacket type" msgid "Jacket type"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:87 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:79
#: lib/cannery_web/live/ammo_type_live/index.ex:58 #: lib/cannery_web/live/ammo_type_live/index.ex:58
#: lib/cannery_web/live/ammo_type_live/show.html.heex:46 #: lib/cannery_web/live/ammo_type_live/show.html.heex:46
msgid "Muzzle velocity" msgid "Muzzle velocity"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:103 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:93
#: lib/cannery_web/live/ammo_type_live/index.ex:60 #: lib/cannery_web/live/ammo_type_live/index.ex:61
#: lib/cannery_web/live/ammo_type_live/show.html.heex:48 #: lib/cannery_web/live/ammo_type_live/show.html.heex:48
msgid "Powder grains per charge" msgid "Powder grains per charge"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:97 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:89
#: lib/cannery_web/live/ammo_type_live/index.ex:59 #: lib/cannery_web/live/ammo_type_live/index.ex:59
#: lib/cannery_web/live/ammo_type_live/show.html.heex:47 #: lib/cannery_web/live/ammo_type_live/show.html.heex:47
msgid "Powder type" msgid "Powder type"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:168 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:152
#: lib/cannery_web/live/ammo_type_live/index.ex:70 #: lib/cannery_web/live/ammo_type_live/index.ex:74
#: lib/cannery_web/live/ammo_type_live/show.html.heex:58
msgid "UPC" msgid "UPC"
msgstr "" msgstr ""
@ -723,17 +689,19 @@ msgid "New password"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.html.heex:82 #: lib/cannery_web/live/ammo_group_live/index.ex:131
msgid "Stage" msgid "Stage"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.html.heex:82 #: lib/cannery_web/live/ammo_group_live/index.ex:131
msgid "Unstage" msgid "Unstage"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:137 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:125
#: lib/cannery_web/live/ammo_type_live/index.ex:68
#: lib/cannery_web/live/ammo_type_live/show.html.heex:52
msgid "Firing type" msgid "Firing type"
msgstr "" msgstr ""
@ -746,3 +714,116 @@ msgstr ""
#: lib/cannery_web/templates/layout/live.html.heex:37 #: lib/cannery_web/templates/layout/live.html.heex:37
msgid "Loading..." msgid "Loading..."
msgstr "" msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/index.ex:29
#: lib/cannery_web/live/container_live/show.ex:97
msgid "Edit %{name}"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/index.ex:48
#: lib/cannery_web/live/container_live/show.ex:98
msgid "Edit %{name} tags"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/components/container_card.ex:50
msgid "Rounds:"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/show.ex:96
msgid "Show %{name}"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/show.html.heex:104
msgid "No cost information"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.ex:83
msgid "% left"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.html.heex:38
msgid "Current value:"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.html.heex:31
msgid "Original cost:"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.html.heex:13
msgid "Original count:"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.html.heex:18
msgid "Percentage left:"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.html.heex:111
msgid "Rounds used"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/show.html.heex:77
msgid "Current # of rounds:"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/index.ex:86
msgid "Total # of rounds"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/show.html.heex:85
msgid "Total rounds shot:"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_confirmation_controller.ex:8
msgid "Confirm your account"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_reset_password_controller.ex:9
msgid "Forgot your password?"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_session_controller.ex:8
msgid "Log in"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_registration_controller.ex:35
msgid "Register"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_reset_password_controller.ex:36
msgid "Reset your password"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.ex:38
#: lib/cannery_web/live/range_live/index.ex:28
msgid "Record Shots"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:58
msgid "Copies"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/index.ex:34
msgid "Ammo types"
msgstr ""

View File

@ -12,12 +12,12 @@ msgstr ""
"Plural-Forms: nplurals=2\n" "Plural-Forms: nplurals=2\n"
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.ex:42 #: lib/cannery_web/live/ammo_group_live/index.ex:44
msgid "Add Ammo" msgid "Add Ammo"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.html.heex:12 #: lib/cannery_web/live/ammo_group_live/index.html.heex:24
msgid "Add your first box!" msgid "Add your first box!"
msgstr "" msgstr ""
@ -32,14 +32,14 @@ msgid "Add your first type!"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/templates/user_settings/edit.html.heex:17 #: lib/cannery_web/templates/user_settings/edit.html.heex:16
#: lib/cannery_web/templates/user_settings/edit.html.heex:46 #: lib/cannery_web/templates/user_settings/edit.html.heex:45
msgid "Change email" msgid "Change email"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/templates/user_settings/edit.html.heex:61 #: lib/cannery_web/templates/user_settings/edit.html.heex:60
#: lib/cannery_web/templates/user_settings/edit.html.heex:102 #: lib/cannery_web/templates/user_settings/edit.html.heex:101
msgid "Change password" msgid "Change password"
msgstr "" msgstr ""
@ -49,14 +49,14 @@ msgid "Create Invite"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/templates/user_settings/edit.html.heex:109 #: lib/cannery_web/templates/user_settings/edit.html.heex:108
msgid "Delete User" msgid "Delete User"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/templates/user_registration/new.html.heex:44 #: lib/cannery_web/templates/user_registration/new.html.heex:43
#: lib/cannery_web/templates/user_reset_password/new.html.heex:3 #: lib/cannery_web/templates/user_reset_password/new.html.heex:3
#: lib/cannery_web/templates/user_session/new.html.heex:46 #: lib/cannery_web/templates/user_session/new.html.heex:45
msgid "Forgot your password?" msgid "Forgot your password?"
msgstr "" msgstr ""
@ -67,12 +67,12 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/topbar.ex:108 #: lib/cannery_web/components/topbar.ex:108
#: lib/cannery_web/templates/user_confirmation/new.html.heex:31 #: lib/cannery_web/templates/user_confirmation/new.html.heex:30
#: lib/cannery_web/templates/user_registration/new.html.heex:40 #: lib/cannery_web/templates/user_registration/new.html.heex:39
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:49 #: lib/cannery_web/templates/user_reset_password/edit.html.heex:48
#: lib/cannery_web/templates/user_reset_password/new.html.heex:31 #: lib/cannery_web/templates/user_reset_password/new.html.heex:30
#: lib/cannery_web/templates/user_session/new.html.heex:3 #: lib/cannery_web/templates/user_session/new.html.heex:3
#: lib/cannery_web/templates/user_session/new.html.heex:34 #: lib/cannery_web/templates/user_session/new.html.heex:33
msgid "Log in" msgid "Log in"
msgstr "" msgstr ""
@ -82,7 +82,7 @@ msgid "Make your first tag!"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.html.heex:17 #: lib/cannery_web/live/ammo_group_live/index.html.heex:42
msgid "New Ammo group" msgid "New Ammo group"
msgstr "" msgstr ""
@ -103,31 +103,31 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/topbar.ex:101 #: lib/cannery_web/components/topbar.ex:101
#: lib/cannery_web/templates/user_confirmation/new.html.heex:26 #: lib/cannery_web/templates/user_confirmation/new.html.heex:25
#: lib/cannery_web/templates/user_registration/new.html.heex:3 #: lib/cannery_web/templates/user_registration/new.html.heex:3
#: lib/cannery_web/templates/user_registration/new.html.heex:34 #: lib/cannery_web/templates/user_registration/new.html.heex:33
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:44 #: lib/cannery_web/templates/user_reset_password/edit.html.heex:43
#: lib/cannery_web/templates/user_reset_password/new.html.heex:26 #: lib/cannery_web/templates/user_reset_password/new.html.heex:25
#: lib/cannery_web/templates/user_session/new.html.heex:41 #: lib/cannery_web/templates/user_session/new.html.heex:40
msgid "Register" msgid "Register"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/templates/user_confirmation/new.html.heex:3 #: lib/cannery_web/templates/user_confirmation/new.html.heex:3
#: lib/cannery_web/templates/user_confirmation/new.html.heex:17 #: lib/cannery_web/templates/user_confirmation/new.html.heex:16
msgid "Resend confirmation instructions" msgid "Resend confirmation instructions"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:3 #: lib/cannery_web/templates/user_reset_password/edit.html.heex:3
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:35 #: lib/cannery_web/templates/user_reset_password/edit.html.heex:34
msgid "Reset password" msgid "Reset password"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/add_shot_group_component.html.heex:42 #: lib/cannery_web/components/add_shot_group_component.html.heex:46
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:54 #: lib/cannery_web/live/ammo_group_live/form_component.html.heex:73
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:175 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:156
#: lib/cannery_web/live/container_live/form_component.html.heex:50 #: lib/cannery_web/live/container_live/form_component.html.heex:50
#: lib/cannery_web/live/invite_live/form_component.html.heex:28 #: lib/cannery_web/live/invite_live/form_component.html.heex:28
#: lib/cannery_web/live/range_live/form_component.html.heex:40 #: lib/cannery_web/live/range_live/form_component.html.heex:40
@ -136,7 +136,7 @@ msgid "Save"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/templates/user_reset_password/new.html.heex:17 #: lib/cannery_web/templates/user_reset_password/new.html.heex:16
msgid "Send instructions to reset password" msgid "Send instructions to reset password"
msgstr "" msgstr ""
@ -146,7 +146,7 @@ msgid "Why not add one?"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/add_tag_component.html.heex:17 #: lib/cannery_web/live/container_live/edit_tags_component.html.heex:52
msgid "Add" msgid "Add"
msgstr "" msgstr ""
@ -161,29 +161,29 @@ msgid "Why not get some ready to shoot?"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.html.heex:85 #: lib/cannery_web/live/ammo_group_live/index.ex:133
#: lib/cannery_web/live/ammo_group_live/show.html.heex:67 #: lib/cannery_web/live/ammo_group_live/show.html.heex:86
#: lib/cannery_web/live/range_live/index.html.heex:36 #: lib/cannery_web/live/range_live/index.html.heex:36
msgid "Record shots" msgid "Record shots"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.html.heex:31 #: lib/cannery_web/live/ammo_group_live/show.html.heex:50
msgid "Ammo Details" msgid "Ammo Details"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/move_ammo_group_component.html.heex:12 #: lib/cannery_web/components/move_ammo_group_component.ex:89
msgid "Add another container!" msgid "Add another container!"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.html.heex:61 #: lib/cannery_web/live/ammo_group_live/show.html.heex:80
msgid "Move containers" msgid "Move containers"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/move_ammo_group_component.html.heex:60 #: lib/cannery_web/components/move_ammo_group_component.ex:127
msgid "Select" msgid "Select"
msgstr "" msgstr ""
@ -191,3 +191,14 @@ msgstr ""
#: lib/cannery_web/live/invite_live/index.html.heex:33 #: lib/cannery_web/live/invite_live/index.html.heex:33
msgid "Copy to clipboard" msgid "Copy to clipboard"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.html.heex:18
#: lib/cannery_web/live/ammo_group_live/index.html.heex:36
msgid "add a container first"
msgstr ""
#, elixir-autogen, elixir-format, fuzzy
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:66
msgid "Create"
msgstr ""

View File

@ -12,12 +12,12 @@ msgstr ""
"Plural-Forms: nplurals=2\n" "Plural-Forms: nplurals=2\n"
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:57 #: lib/cannery_web/live/home_live.ex:63
msgid "%{name} lets you easily keep an eye on your ammo levels before and after range day" msgid "%{name} lets you easily keep an eye on your ammo levels before and after range day"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:79 #: lib/cannery_web/live/home_live.ex:85
msgid "Access from any internet-capable device" msgid "Access from any internet-capable device"
msgstr "" msgstr ""
@ -27,20 +27,20 @@ msgid "Admins"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:93 #: lib/cannery_web/live/home_live.ex:99
msgid "Admins:" msgid "Admins:"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/topbar.ex:52 #: lib/cannery_web/components/topbar.ex:52
#: lib/cannery_web/live/ammo_group_live/index.html.heex:3 #: lib/cannery_web/live/ammo_group_live/index.html.heex:3
#: lib/cannery_web/live/range_live/index.html.heex:61 #: lib/cannery_web/live/range_live/index.ex:82
msgid "Ammo" msgid "Ammo"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:21 #: lib/cannery_web/live/ammo_group_live/form_component.html.heex:21
#: lib/cannery_web/live/ammo_group_live/index.html.heex:27 #: lib/cannery_web/live/ammo_group_live/index.ex:80
msgid "Ammo type" msgid "Ammo type"
msgstr "" msgstr ""
@ -55,19 +55,19 @@ msgid "Background color"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:157 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:140
#: lib/cannery_web/live/ammo_type_live/index.ex:67 #: lib/cannery_web/live/ammo_type_live/index.ex:71
#: lib/cannery_web/live/ammo_type_live/show.html.heex:55
msgid "Blank" msgid "Blank"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:74 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:68
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:102
msgid "Brass" msgid "Brass"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:46 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:44
#: lib/cannery_web/live/ammo_type_live/index.ex:53 #: lib/cannery_web/live/ammo_type_live/index.ex:53
#: lib/cannery_web/live/ammo_type_live/show.html.heex:41 #: lib/cannery_web/live/ammo_type_live/show.html.heex:41
msgid "Bullet core" msgid "Bullet core"
@ -81,48 +81,50 @@ msgid "Bullet type"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:62 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:58
#: lib/cannery_web/live/ammo_type_live/index.ex:55 #: lib/cannery_web/live/ammo_type_live/index.ex:55
#: lib/cannery_web/live/ammo_type_live/show.html.heex:43 #: lib/cannery_web/live/ammo_type_live/show.html.heex:43
msgid "Caliber" msgid "Caliber"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:55 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:51
#: lib/cannery_web/live/ammo_type_live/index.ex:54 #: lib/cannery_web/live/ammo_type_live/index.ex:54
#: lib/cannery_web/live/ammo_type_live/show.html.heex:42 #: lib/cannery_web/live/ammo_type_live/show.html.heex:42
msgid "Cartridge" msgid "Cartridge"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:69 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:65
#: lib/cannery_web/live/ammo_type_live/index.ex:56 #: lib/cannery_web/live/ammo_type_live/index.ex:56
#: lib/cannery_web/live/ammo_type_live/show.html.heex:44 #: lib/cannery_web/live/ammo_type_live/show.html.heex:44
msgid "Case material" msgid "Case material"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/move_ammo_group_component.html.heex:22 #: lib/cannery_web/components/move_ammo_group_component.ex:67
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:48 #: lib/cannery_web/live/ammo_group_live/form_component.html.heex:48
#: lib/cannery_web/live/ammo_group_live/index.html.heex:42 #: lib/cannery_web/live/ammo_group_live/index.ex:85
msgid "Container" msgid "Container"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/topbar.ex:46 #: lib/cannery_web/components/topbar.ex:46
#: lib/cannery_web/live/container_live/index.ex:38
#: lib/cannery_web/live/container_live/index.html.heex:3 #: lib/cannery_web/live/container_live/index.html.heex:3
msgid "Containers" msgid "Containers"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:161 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:144
#: lib/cannery_web/live/ammo_type_live/index.ex:68 #: lib/cannery_web/live/ammo_type_live/index.ex:72
#: lib/cannery_web/live/ammo_type_live/show.html.heex:56
msgid "Corrosive" msgid "Corrosive"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:27 #: lib/cannery_web/live/ammo_group_live/form_component.html.heex:27
#: lib/cannery_web/live/ammo_group_live/index.html.heex:30 #: lib/cannery_web/live/ammo_group_live/index.ex:81
msgid "Count" msgid "Count"
msgstr "" msgstr ""
@ -139,7 +141,7 @@ msgid "Description"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/container_card.ex:27 #: lib/cannery_web/components/container_card.ex:31
#: lib/cannery_web/live/container_live/show.html.heex:8 #: lib/cannery_web/live/container_live/show.html.heex:8
msgid "Description:" msgid "Description:"
msgstr "" msgstr ""
@ -150,13 +152,13 @@ msgid "Disable"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:54 #: lib/cannery_web/live/home_live.ex:60
msgid "Easy to Use:" msgid "Easy to Use:"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.ex:36 #: lib/cannery_web/live/ammo_group_live/index.ex:38
#: lib/cannery_web/live/ammo_group_live/show.ex:55 #: lib/cannery_web/live/ammo_group_live/show.ex:42
msgid "Edit Ammo group" msgid "Edit Ammo group"
msgstr "" msgstr ""
@ -166,12 +168,6 @@ msgstr ""
msgid "Edit Ammo type" msgid "Edit Ammo type"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/index.ex:24
#: lib/cannery_web/live/container_live/show.ex:89
msgid "Edit Container"
msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/invite_live/index.ex:35 #: lib/cannery_web/live/invite_live/index.ex:35
msgid "Edit Invite" msgid "Edit Invite"
@ -193,25 +189,26 @@ msgid "Example bullet type abbreviations"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:42 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:40
msgid "FMJ" msgid "FMJ"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:116 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:103
#: lib/cannery_web/live/ammo_type_live/index.ex:61 #: lib/cannery_web/live/ammo_type_live/index.ex:65
#: lib/cannery_web/live/ammo_type_live/show.html.heex:49 #: lib/cannery_web/live/ammo_type_live/show.html.heex:49
msgid "Grains" msgid "Grains"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:153 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:136
#: lib/cannery_web/live/ammo_type_live/index.ex:66 #: lib/cannery_web/live/ammo_type_live/index.ex:70
#: lib/cannery_web/live/ammo_type_live/show.html.heex:54
msgid "Incendiary" msgid "Incendiary"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:88 #: lib/cannery_web/live/home_live.ex:94
msgid "Instance Information" msgid "Instance Information"
msgstr "" msgstr ""
@ -221,49 +218,30 @@ msgid "Invite Disabled"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:119 #: lib/cannery_web/live/home_live.ex:125
msgid "Invite Only" msgid "Invite Only"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/topbar.ex:71 #: lib/cannery_web/components/topbar.ex:71
#: lib/cannery_web/live/invite_live/index.ex:43
#: lib/cannery_web/live/invite_live/index.html.heex:3 #: lib/cannery_web/live/invite_live/index.html.heex:3
msgid "Invites" msgid "Invites"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/templates/user_session/new.html.heex:29 #: lib/cannery_web/templates/user_session/new.html.heex:28
msgid "Keep me logged in for 60 days" msgid "Keep me logged in for 60 days"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/index.ex:34 #: lib/cannery_web/components/move_ammo_group_component.ex:69
msgid "Listing Ammo types"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/index.ex:33
msgid "Listing Containers"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/invite_live/index.ex:43
msgid "Listing Invites"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/tag_live/index.ex:34
msgid "Listing Tags"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/components/move_ammo_group_component.html.heex:30
#: lib/cannery_web/live/container_live/form_component.html.heex:42 #: lib/cannery_web/live/container_live/form_component.html.heex:42
msgid "Location" msgid "Location"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/container_card.ex:39 #: lib/cannery_web/components/container_card.ex:43
#: lib/cannery_web/live/container_live/show.html.heex:20 #: lib/cannery_web/live/container_live/show.html.heex:20
msgid "Location:" msgid "Location:"
msgstr "" msgstr ""
@ -279,8 +257,9 @@ msgid "Manage"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:165 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:148
#: lib/cannery_web/live/ammo_type_live/index.ex:69 #: lib/cannery_web/live/ammo_type_live/index.ex:73
#: lib/cannery_web/live/ammo_type_live/show.html.heex:57
msgid "Manufacturer" msgid "Manufacturer"
msgstr "" msgstr ""
@ -309,7 +288,7 @@ msgid "New Ammo type"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/index.ex:29 #: lib/cannery_web/live/container_live/index.ex:33
msgid "New Container" msgid "New Container"
msgstr "" msgstr ""
@ -334,12 +313,12 @@ msgid "No Ammo Types"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/show.html.heex:109 #: lib/cannery_web/live/ammo_type_live/show.html.heex:114
msgid "No ammo for this type" msgid "No ammo for this type"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/show.html.heex:88 #: lib/cannery_web/live/container_live/show.html.heex:78
msgid "No ammo groups in this container" msgid "No ammo groups in this container"
msgstr "" msgstr ""
@ -354,6 +333,7 @@ msgid "No invites"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/edit_tags_component.html.heex:30
#: lib/cannery_web/live/tag_live/index.html.heex:10 #: lib/cannery_web/live/tag_live/index.html.heex:10
msgid "No tags" msgid "No tags"
msgstr "" msgstr ""
@ -361,15 +341,15 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/add_shot_group_component.html.heex:30 #: lib/cannery_web/components/add_shot_group_component.html.heex:30
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:41 #: lib/cannery_web/live/ammo_group_live/form_component.html.heex:41
#: lib/cannery_web/live/ammo_group_live/index.html.heex:36 #: lib/cannery_web/live/ammo_group_live/show.ex:90
#: lib/cannery_web/live/range_live/form_component.html.heex:29 #: lib/cannery_web/live/range_live/form_component.html.heex:29
#: lib/cannery_web/live/range_live/index.html.heex:67 #: lib/cannery_web/live/range_live/index.ex:84
msgid "Notes" msgid "Notes"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/ammo_group_card.ex:35 #: lib/cannery_web/components/ammo_group_card.ex:35
#: lib/cannery_web/live/ammo_group_live/show.html.heex:14 #: lib/cannery_web/live/ammo_group_live/show.html.heex:24
msgid "Notes:" msgid "Notes:"
msgstr "" msgstr ""
@ -379,48 +359,42 @@ msgid "On the bookshelf"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:124 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:111
#: lib/cannery_web/live/ammo_type_live/index.ex:62 #: lib/cannery_web/live/ammo_type_live/index.ex:66
#: lib/cannery_web/live/ammo_type_live/show.html.heex:50 #: lib/cannery_web/live/ammo_type_live/show.html.heex:50
msgid "Pressure" msgid "Pressure"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:34 #: lib/cannery_web/live/ammo_group_live/form_component.html.heex:34
#: lib/cannery_web/live/ammo_group_live/index.html.heex:33 #: lib/cannery_web/live/ammo_group_live/index.ex:82
msgid "Price paid" msgid "Price paid"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/ammo_group_card.ex:42 #: lib/cannery_web/components/ammo_group_card.ex:42
#: lib/cannery_web/live/ammo_group_live/show.html.heex:21
msgid "Price paid:" msgid "Price paid:"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:131 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:118
#: lib/cannery_web/live/ammo_type_live/index.ex:63 #: lib/cannery_web/live/ammo_type_live/index.ex:67
#: lib/cannery_web/live/ammo_type_live/show.html.heex:51 #: lib/cannery_web/live/ammo_type_live/show.html.heex:51
msgid "Primer type" msgid "Primer type"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:118 #: lib/cannery_web/live/home_live.ex:124
msgid "Public Signups" msgid "Public Signups"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/index.ex:64 #: lib/cannery_web/live/home_live.ex:72
msgid "Rimfire"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:66
msgid "Secure:" msgid "Secure:"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:69 #: lib/cannery_web/live/home_live.ex:75
msgid "Self-host your own instance, or use an instance from someone you trust." msgid "Self-host your own instance, or use an instance from someone you trust."
msgstr "" msgstr ""
@ -430,12 +404,13 @@ msgid "Set Unlimited"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_settings_controller.ex:10
#: lib/cannery_web/templates/user_settings/edit.html.heex:3 #: lib/cannery_web/templates/user_settings/edit.html.heex:3
msgid "Settings" msgid "Settings"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.ex:54 #: lib/cannery_web/live/ammo_group_live/show.ex:41
msgid "Show Ammo group" msgid "Show Ammo group"
msgstr "" msgstr ""
@ -445,28 +420,23 @@ msgid "Show Ammo type"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/show.ex:88 #: lib/cannery_web/live/home_live.ex:82
msgid "Show Container"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:76
msgid "Simple:" msgid "Simple:"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:51 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:47
msgid "Steel" msgid "Steel"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.html.heex:79 #: lib/cannery_web/live/ammo_group_live/show.html.heex:98
msgid "Stored in" msgid "Stored in"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/topbar.ex:40 #: lib/cannery_web/components/topbar.ex:40
#: lib/cannery_web/live/container_live/show.html.heex:60 #: lib/cannery_web/live/tag_live/index.ex:34
#: lib/cannery_web/live/tag_live/index.html.heex:3 #: lib/cannery_web/live/tag_live/index.html.heex:3
msgid "Tags" msgid "Tags"
msgstr "" msgstr ""
@ -482,29 +452,30 @@ msgid "Text color"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:45 #: lib/cannery_web/live/home_live.ex:51
msgid "The self-hosted firearm tracker website" msgid "The self-hosted firearm tracker website"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.html.heex:84 #: lib/cannery_web/live/ammo_group_live/show.html.heex:103
msgid "This ammo group is not in a container" msgid "This ammo group is not in a container"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:149 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:132
#: lib/cannery_web/live/ammo_type_live/index.ex:65 #: lib/cannery_web/live/ammo_type_live/index.ex:69
#: lib/cannery_web/live/ammo_type_live/show.html.heex:53
msgid "Tracer" msgid "Tracer"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/move_ammo_group_component.html.heex:26 #: lib/cannery_web/components/move_ammo_group_component.ex:68
#: lib/cannery_web/live/container_live/form_component.html.heex:35 #: lib/cannery_web/live/container_live/form_component.html.heex:35
msgid "Type" msgid "Type"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/container_card.ex:33 #: lib/cannery_web/components/container_card.ex:37
#: lib/cannery_web/live/container_live/show.html.heex:14 #: lib/cannery_web/live/container_live/show.html.heex:14
msgid "Type:" msgid "Type:"
msgstr "" msgstr ""
@ -525,20 +496,15 @@ msgid "Uses left"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:41 #: lib/cannery_web/live/home_live.ex:47
msgid "Welcome to %{name}" msgid "Welcome to %{name}"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:70 #: lib/cannery_web/live/home_live.ex:76
msgid "Your data stays with you, period" msgid "Your data stays with you, period"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/show.ex:90
msgid "Add Tag to Container"
msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/show.html.heex:49 #: lib/cannery_web/live/container_live/show.html.heex:49
msgid "No tags for this container" msgid "No tags for this container"
@ -546,7 +512,7 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/topbar.ex:64 #: lib/cannery_web/components/topbar.ex:64
#: lib/cannery_web/live/ammo_group_live/index.html.heex:39 #: lib/cannery_web/live/ammo_group_live/index.ex:84
msgid "Range" msgid "Range"
msgstr "" msgstr ""
@ -556,7 +522,8 @@ msgid "Range day"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/range_live/index.html.heex:70 #: lib/cannery_web/live/ammo_group_live/show.ex:91
#: lib/cannery_web/live/range_live/index.ex:85
msgid "Date" msgid "Date"
msgstr "" msgstr ""
@ -571,26 +538,20 @@ msgid "No ammo staged"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.html.heex:58 #: lib/cannery_web/live/ammo_group_live/show.html.heex:77
#: lib/cannery_web/live/range_live/index.html.heex:33 #: lib/cannery_web/live/range_live/index.html.heex:33
msgid "Stage for range" msgid "Stage for range"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.html.heex:57 #: lib/cannery_web/live/ammo_group_live/show.html.heex:76
#: lib/cannery_web/live/range_live/index.html.heex:32 #: lib/cannery_web/live/range_live/index.html.heex:32
msgid "Unstage from range" msgid "Unstage from range"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.ex:52
msgid "Add Shot group"
msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/add_shot_group_component.html.heex:3 #: lib/cannery_web/components/add_shot_group_component.html.heex:3
#: lib/cannery_web/live/ammo_group_live/index.ex:24 #: lib/cannery_web/live/ammo_group_live/index.ex:26
#: lib/cannery_web/live/range_live/index.ex:28
msgid "Record shots" msgid "Record shots"
msgstr "" msgstr ""
@ -600,7 +561,7 @@ msgid "Ammo Types"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.ex:47 #: lib/cannery_web/live/ammo_group_live/index.ex:49
msgid "Ammo groups" msgid "Ammo groups"
msgstr "" msgstr ""
@ -611,6 +572,7 @@ msgid "Date (UTC)"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.ex:39
#: lib/cannery_web/live/range_live/index.ex:34 #: lib/cannery_web/live/range_live/index.ex:34
msgid "Edit Shot Records" msgid "Edit Shot Records"
msgstr "" msgstr ""
@ -631,7 +593,8 @@ msgid "Rounds left"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/range_live/index.html.heex:64 #: lib/cannery_web/live/ammo_group_live/show.ex:89
#: lib/cannery_web/live/range_live/index.ex:83
msgid "Rounds shot" msgid "Rounds shot"
msgstr "" msgstr ""
@ -641,18 +604,18 @@ msgid "Shot Records"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.ex:30 #: lib/cannery_web/live/ammo_group_live/index.ex:32
#: lib/cannery_web/live/ammo_group_live/show.ex:53 #: lib/cannery_web/live/ammo_group_live/show.ex:40
msgid "Move Ammo group" msgid "Move Ammo group"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/move_ammo_group_component.html.heex:3 #: lib/cannery_web/components/move_ammo_group_component.ex:80
msgid "Move ammo" msgid "Move ammo"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/move_ammo_group_component.html.heex:8 #: lib/cannery_web/components/move_ammo_group_component.ex:85
msgid "No other containers" msgid "No other containers"
msgstr "" msgstr ""
@ -663,79 +626,83 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/ammo_group_card.ex:43 #: lib/cannery_web/components/ammo_group_card.ex:43
#: lib/cannery_web/live/ammo_group_live/index.html.heex:64 #: lib/cannery_web/live/ammo_group_live/index.ex:117
#: lib/cannery_web/live/ammo_group_live/show.html.heex:22 #: lib/cannery_web/live/ammo_group_live/show.html.heex:32
#: lib/cannery_web/live/ammo_group_live/show.html.heex:39
#: lib/cannery_web/live/ammo_type_live/show.html.heex:98 #: lib/cannery_web/live/ammo_type_live/show.html.heex:98
msgid "$%{amount}" msgid "$%{amount}"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:83 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:75
msgid "Bimetal" msgid "Bimetal"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:78 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:72
#: lib/cannery_web/live/ammo_type_live/index.ex:57 #: lib/cannery_web/live/ammo_type_live/index.ex:57
#: lib/cannery_web/live/ammo_type_live/show.html.heex:45 #: lib/cannery_web/live/ammo_type_live/show.html.heex:45
msgid "Jacket type" msgid "Jacket type"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:87 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:79
#: lib/cannery_web/live/ammo_type_live/index.ex:58 #: lib/cannery_web/live/ammo_type_live/index.ex:58
#: lib/cannery_web/live/ammo_type_live/show.html.heex:46 #: lib/cannery_web/live/ammo_type_live/show.html.heex:46
msgid "Muzzle velocity" msgid "Muzzle velocity"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:106 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:93
#: lib/cannery_web/live/ammo_type_live/index.ex:60 #: lib/cannery_web/live/ammo_type_live/index.ex:61
#: lib/cannery_web/live/ammo_type_live/show.html.heex:48 #: lib/cannery_web/live/ammo_type_live/show.html.heex:48
msgid "Powder grains per charge" msgid "Powder grains per charge"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:97 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:89
#: lib/cannery_web/live/ammo_type_live/index.ex:59 #: lib/cannery_web/live/ammo_type_live/index.ex:59
#: lib/cannery_web/live/ammo_type_live/show.html.heex:47 #: lib/cannery_web/live/ammo_type_live/show.html.heex:47
msgid "Powder type" msgid "Powder type"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:171 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:152
#: lib/cannery_web/live/ammo_type_live/index.ex:70 #: lib/cannery_web/live/ammo_type_live/index.ex:74
#: lib/cannery_web/live/ammo_type_live/show.html.heex:58
msgid "UPC" msgid "UPC"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/templates/user_settings/edit.html.heex:81 #: lib/cannery_web/templates/user_settings/edit.html.heex:80
msgid "Confirm new password" msgid "Confirm new password"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/templates/user_settings/edit.html.heex:34 #: lib/cannery_web/templates/user_settings/edit.html.heex:33
#: lib/cannery_web/templates/user_settings/edit.html.heex:90 #: lib/cannery_web/templates/user_settings/edit.html.heex:89
msgid "Current password" msgid "Current password"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/templates/user_settings/edit.html.heex:74 #: lib/cannery_web/templates/user_settings/edit.html.heex:73
msgid "New password" msgid "New password"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.html.heex:82 #: lib/cannery_web/live/ammo_group_live/index.ex:130
msgid "Stage" msgid "Stage"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.html.heex:82 #: lib/cannery_web/live/ammo_group_live/index.ex:130
msgid "Unstage" msgid "Unstage"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:140 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:125
#: lib/cannery_web/live/ammo_type_live/index.ex:68
#: lib/cannery_web/live/ammo_type_live/show.html.heex:52
msgid "Firing type" msgid "Firing type"
msgstr "" msgstr ""
@ -748,3 +715,116 @@ msgstr ""
#: lib/cannery_web/templates/layout/live.html.heex:37 #: lib/cannery_web/templates/layout/live.html.heex:37
msgid "Loading..." msgid "Loading..."
msgstr "" msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/index.ex:29
#: lib/cannery_web/live/container_live/show.ex:97
msgid "Edit %{name}"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/index.ex:48
#: lib/cannery_web/live/container_live/show.ex:98
msgid "Edit %{name} tags"
msgstr ""
#, elixir-autogen, elixir-format, fuzzy
#: lib/cannery_web/components/container_card.ex:50
msgid "Rounds:"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/show.ex:96
msgid "Show %{name}"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/show.html.heex:104
msgid "No cost information"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.ex:83
msgid "% left"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.html.heex:38
msgid "Current value:"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.html.heex:31
msgid "Original cost:"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.html.heex:13
msgid "Original count:"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.html.heex:18
msgid "Percentage left:"
msgstr ""
#, elixir-autogen, elixir-format, fuzzy
#: lib/cannery_web/live/ammo_group_live/show.html.heex:111
msgid "Rounds used"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/show.html.heex:77
msgid "Current # of rounds:"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/index.ex:86
msgid "Total # of rounds"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/show.html.heex:85
msgid "Total rounds shot:"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_confirmation_controller.ex:8
msgid "Confirm your account"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_reset_password_controller.ex:9
msgid "Forgot your password?"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_session_controller.ex:8
msgid "Log in"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_registration_controller.ex:35
msgid "Register"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_reset_password_controller.ex:36
msgid "Reset your password"
msgstr ""
#, elixir-autogen, elixir-format, fuzzy
#: lib/cannery_web/live/ammo_group_live/show.ex:38
#: lib/cannery_web/live/range_live/index.ex:28
msgid "Record Shots"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:58
msgid "Copies"
msgstr ""
#, elixir-autogen, elixir-format, fuzzy
#: lib/cannery_web/live/ammo_type_live/index.ex:34
msgid "Ammo types"
msgstr ""

View File

@ -11,18 +11,18 @@ msgstr ""
"Language: en\n" "Language: en\n"
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery/containers.ex:105 #: lib/cannery/containers.ex:122
msgid "Container must be empty before deleting" msgid "Container must be empty before deleting"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/index.ex:55 #: lib/cannery_web/live/container_live/index.ex:71
#: lib/cannery_web/live/container_live/show.ex:74 #: lib/cannery_web/live/container_live/show.ex:73
msgid "Could not delete %{name}: %{error}" msgid "Could not delete %{name}: %{error}"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/index.ex:43 #: lib/cannery_web/live/container_live/index.ex:59
msgid "Could not find that container" msgid "Could not find that container"
msgstr "" msgstr ""
@ -57,27 +57,27 @@ msgid "Not found"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/templates/user_registration/new.html.heex:17 #: lib/cannery_web/templates/user_registration/new.html.heex:16
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:17 #: lib/cannery_web/templates/user_reset_password/edit.html.heex:16
#: lib/cannery_web/templates/user_settings/edit.html.heex:23 #: lib/cannery_web/templates/user_settings/edit.html.heex:22
#: lib/cannery_web/templates/user_settings/edit.html.heex:67 #: lib/cannery_web/templates/user_settings/edit.html.heex:66
msgid "Oops, something went wrong! Please check the errors below." msgid "Oops, something went wrong! Please check the errors below."
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_reset_password_controller.ex:60 #: lib/cannery_web/controllers/user_reset_password_controller.ex:63
msgid "Reset password link is invalid or it has expired." msgid "Reset password link is invalid or it has expired."
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_registration_controller.ex:25 #: lib/cannery_web/controllers/user_registration_controller.ex:25
#: lib/cannery_web/controllers/user_registration_controller.ex:53 #: lib/cannery_web/controllers/user_registration_controller.ex:56
msgid "Sorry, public registration is disabled" msgid "Sorry, public registration is disabled"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_registration_controller.ex:15 #: lib/cannery_web/controllers/user_registration_controller.ex:15
#: lib/cannery_web/controllers/user_registration_controller.ex:43 #: lib/cannery_web/controllers/user_registration_controller.ex:46
msgid "Sorry, this invite was not found or expired" msgid "Sorry, this invite was not found or expired"
msgstr "" msgstr ""
@ -92,7 +92,7 @@ msgid "Unauthorized"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_confirmation_controller.ex:53 #: lib/cannery_web/controllers/user_confirmation_controller.ex:54
msgid "User confirmation link is invalid or it has expired." msgid "User confirmation link is invalid or it has expired."
msgstr "" msgstr ""
@ -133,7 +133,7 @@ msgid "Tag not found"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/add_tag_component.ex:35 #: lib/cannery_web/live/container_live/edit_tags_component.ex:30
msgid "Tag could not be added" msgid "Tag could not be added"
msgstr "" msgstr ""
@ -153,3 +153,18 @@ msgstr ""
#: lib/cannery_web/controllers/user_auth.ex:161 #: lib/cannery_web/controllers/user_auth.ex:161
msgid "You must confirm your account and log in to access this page." msgid "You must confirm your account and log in to access this page."
msgstr "" msgstr ""
#, elixir-autogen, elixir-format, fuzzy
#: lib/cannery_web/live/container_live/edit_tags_component.ex:52
msgid "Tag could not be removed"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/form_component.ex:113
msgid "Could not parse number of copies"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/form_component.ex:98
msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}"
msgstr ""

View File

@ -39,8 +39,8 @@ msgid "%{name} enabled succesfully"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/index.ex:48 #: lib/cannery_web/live/container_live/index.ex:64
#: lib/cannery_web/live/container_live/show.ex:64 #: lib/cannery_web/live/container_live/show.ex:63
msgid "%{name} has been deleted" msgid "%{name} has been deleted"
msgstr "" msgstr ""
@ -63,18 +63,13 @@ msgid "A link to confirm your email change has been sent to the new address."
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/form_component.ex:87 #: lib/cannery_web/live/ammo_group_live/index.ex:56
msgid "Ammo group created successfully" #: lib/cannery_web/live/ammo_group_live/show.ex:52
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.ex:54
#: lib/cannery_web/live/ammo_group_live/show.ex:34
msgid "Ammo group deleted succesfully" msgid "Ammo group deleted succesfully"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/form_component.ex:69 #: lib/cannery_web/live/ammo_group_live/form_component.ex:75
msgid "Ammo group updated successfully" msgid "Ammo group updated successfully"
msgstr "" msgstr ""
@ -86,7 +81,7 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/show.html.heex:29 #: lib/cannery_web/live/ammo_type_live/show.html.heex:29
#: lib/cannery_web/live/container_live/index.html.heex:38 #: lib/cannery_web/live/container_live/index.html.heex:46
#: lib/cannery_web/live/container_live/show.html.heex:37 #: lib/cannery_web/live/container_live/show.html.heex:37
#: lib/cannery_web/live/tag_live/index.html.heex:38 #: lib/cannery_web/live/tag_live/index.html.heex:38
msgid "Are you sure you want to delete %{name}?" msgid "Are you sure you want to delete %{name}?"
@ -98,14 +93,14 @@ msgid "Are you sure you want to delete the invite for %{name}?"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.html.heex:120 #: lib/cannery_web/live/ammo_group_live/index.ex:165
#: lib/cannery_web/live/ammo_group_live/show.html.heex:47 #: lib/cannery_web/live/ammo_group_live/show.html.heex:66
#: lib/cannery_web/live/ammo_type_live/index.html.heex:68 #: lib/cannery_web/live/ammo_type_live/index.ex:130
msgid "Are you sure you want to delete this ammo?" msgid "Are you sure you want to delete this ammo?"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/templates/user_settings/edit.html.heex:113 #: lib/cannery_web/templates/user_settings/edit.html.heex:112
msgid "Are you sure you want to delete your account?" msgid "Are you sure you want to delete your account?"
msgstr "" msgstr ""
@ -125,7 +120,7 @@ msgid "Email changed successfully."
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_confirmation_controller.ex:22 #: lib/cannery_web/controllers/user_confirmation_controller.ex:23
msgid "If your email is in our system and it has not been confirmed yet, you will receive an email with instructions shortly." msgid "If your email is in our system and it has not been confirmed yet, you will receive an email with instructions shortly."
msgstr "" msgstr ""
@ -140,7 +135,7 @@ msgid "Logged out successfully."
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_reset_password_controller.ex:43 #: lib/cannery_web/controllers/user_reset_password_controller.ex:46
msgid "Password reset successfully." msgid "Password reset successfully."
msgstr "" msgstr ""
@ -150,19 +145,19 @@ msgid "Password updated successfully."
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_registration_controller.ex:71 #: lib/cannery_web/controllers/user_registration_controller.ex:74
msgid "Please check your email to verify your account" msgid "Please check your email to verify your account"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:97 #: lib/cannery_web/live/home_live.ex:103
msgid "Register to setup %{name}" msgid "Register to setup %{name}"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/add_shot_group_component.html.heex:44 #: lib/cannery_web/components/add_shot_group_component.html.heex:48
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:55 #: lib/cannery_web/live/ammo_group_live/form_component.html.heex:74
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:176 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:157
#: lib/cannery_web/live/container_live/form_component.html.heex:52 #: lib/cannery_web/live/container_live/form_component.html.heex:52
#: lib/cannery_web/live/invite_live/form_component.html.heex:30 #: lib/cannery_web/live/invite_live/form_component.html.heex:30
#: lib/cannery_web/live/range_live/form_component.html.heex:42 #: lib/cannery_web/live/range_live/form_component.html.heex:42
@ -176,22 +171,22 @@ msgid "Your account has been deleted"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/show.html.heex:71 #: lib/cannery_web/live/container_live/edit_tags_component.html.heex:16
msgid "Are you sure you want to remove the %{tag_name} tag from %{container_name}?" msgid "Are you sure you want to remove the %{tag_name} tag from %{container_name}?"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/add_tag_component.ex:40 #: lib/cannery_web/live/container_live/edit_tags_component.ex:36
msgid "%{name} added successfully" msgid "%{name} added successfully"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/show.ex:40 #: lib/cannery_web/live/container_live/show.ex:39
msgid "%{tag_name} has been removed from %{container_name}" msgid "%{tag_name} has been removed from %{container_name}"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/add_tag_component.html.heex:19 #: lib/cannery_web/live/container_live/edit_tags_component.html.heex:54
msgid "Adding..." msgid "Adding..."
msgstr "" msgstr ""
@ -211,11 +206,13 @@ msgid "Ammo group unstaged succesfully"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/range_live/index.html.heex:108 #: lib/cannery_web/live/ammo_group_live/show.ex:132
#: lib/cannery_web/live/range_live/index.ex:130
msgid "Are you sure you want to delete this shot record?" msgid "Are you sure you want to delete this shot record?"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.ex:80
#: lib/cannery_web/live/range_live/index.ex:56 #: lib/cannery_web/live/range_live/index.ex:56
msgid "Shot records deleted succesfully" msgid "Shot records deleted succesfully"
msgstr "" msgstr ""
@ -226,12 +223,12 @@ msgid "Shot records updated successfully"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_confirmation_controller.ex:37 #: lib/cannery_web/controllers/user_confirmation_controller.ex:38
msgid "%{email} confirmed successfully." msgid "%{email} confirmed successfully."
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/move_ammo_group_component.ex:47 #: lib/cannery_web/components/move_ammo_group_component.ex:53
msgid "Ammo moved to %{name} successfully" msgid "Ammo moved to %{name} successfully"
msgstr "" msgstr ""
@ -239,3 +236,26 @@ msgstr ""
#: lib/cannery_web/live/invite_live/index.ex:123 #: lib/cannery_web/live/invite_live/index.ex:123
msgid "Copied to clipboard" msgid "Copied to clipboard"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format, fuzzy
#: lib/cannery_web/live/container_live/edit_tags_component.ex:58
msgid "%{name} removed successfully"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.html.heex:15
#: lib/cannery_web/live/ammo_group_live/index.html.heex:33
msgid "You'll need to"
msgstr ""
#, elixir-autogen, elixir-format, fuzzy
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:67
msgid "Creating..."
msgstr ""
#, elixir-autogen, elixir-format, fuzzy
#: lib/cannery_web/live/ammo_group_live/form_component.ex:134
msgid "Ammo group created successfully"
msgid_plural "Ammo groups created successfully"
msgstr[0] ""
msgstr[1] ""

View File

@ -11,18 +11,18 @@ msgid ""
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery/containers.ex:105 #: lib/cannery/containers.ex:122
msgid "Container must be empty before deleting" msgid "Container must be empty before deleting"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/index.ex:55 #: lib/cannery_web/live/container_live/index.ex:71
#: lib/cannery_web/live/container_live/show.ex:74 #: lib/cannery_web/live/container_live/show.ex:73
msgid "Could not delete %{name}: %{error}" msgid "Could not delete %{name}: %{error}"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/index.ex:43 #: lib/cannery_web/live/container_live/index.ex:59
msgid "Could not find that container" msgid "Could not find that container"
msgstr "" msgstr ""
@ -65,19 +65,19 @@ msgid "Oops, something went wrong! Please check the errors below."
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_reset_password_controller.ex:60 #: lib/cannery_web/controllers/user_reset_password_controller.ex:63
msgid "Reset password link is invalid or it has expired." msgid "Reset password link is invalid or it has expired."
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_registration_controller.ex:25 #: lib/cannery_web/controllers/user_registration_controller.ex:25
#: lib/cannery_web/controllers/user_registration_controller.ex:53 #: lib/cannery_web/controllers/user_registration_controller.ex:56
msgid "Sorry, public registration is disabled" msgid "Sorry, public registration is disabled"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_registration_controller.ex:15 #: lib/cannery_web/controllers/user_registration_controller.ex:15
#: lib/cannery_web/controllers/user_registration_controller.ex:43 #: lib/cannery_web/controllers/user_registration_controller.ex:46
msgid "Sorry, this invite was not found or expired" msgid "Sorry, this invite was not found or expired"
msgstr "" msgstr ""
@ -92,7 +92,7 @@ msgid "Unauthorized"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_confirmation_controller.ex:53 #: lib/cannery_web/controllers/user_confirmation_controller.ex:54
msgid "User confirmation link is invalid or it has expired." msgid "User confirmation link is invalid or it has expired."
msgstr "" msgstr ""
@ -132,7 +132,7 @@ msgid "Tag not found"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/add_tag_component.ex:35 #: lib/cannery_web/live/container_live/edit_tags_component.ex:30
msgid "Tag could not be added" msgid "Tag could not be added"
msgstr "" msgstr ""
@ -152,3 +152,18 @@ msgstr ""
#: lib/cannery_web/controllers/user_auth.ex:161 #: lib/cannery_web/controllers/user_auth.ex:161
msgid "You must confirm your account and log in to access this page." msgid "You must confirm your account and log in to access this page."
msgstr "" msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/edit_tags_component.ex:52
msgid "Tag could not be removed"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/form_component.ex:126
msgid "Could not parse number of copies"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/form_component.ex:111
msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}"
msgstr ""

View File

@ -38,8 +38,8 @@ msgid "%{name} enabled succesfully"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/index.ex:48 #: lib/cannery_web/live/container_live/index.ex:64
#: lib/cannery_web/live/container_live/show.ex:64 #: lib/cannery_web/live/container_live/show.ex:63
msgid "%{name} has been deleted" msgid "%{name} has been deleted"
msgstr "" msgstr ""
@ -62,18 +62,13 @@ msgid "A link to confirm your email change has been sent to the new address."
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/form_component.ex:87 #: lib/cannery_web/live/ammo_group_live/index.ex:56
msgid "Ammo group created successfully" #: lib/cannery_web/live/ammo_group_live/show.ex:52
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.ex:54
#: lib/cannery_web/live/ammo_group_live/show.ex:34
msgid "Ammo group deleted succesfully" msgid "Ammo group deleted succesfully"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/form_component.ex:69 #: lib/cannery_web/live/ammo_group_live/form_component.ex:88
msgid "Ammo group updated successfully" msgid "Ammo group updated successfully"
msgstr "" msgstr ""
@ -85,7 +80,7 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_type_live/show.html.heex:29 #: lib/cannery_web/live/ammo_type_live/show.html.heex:29
#: lib/cannery_web/live/container_live/index.html.heex:38 #: lib/cannery_web/live/container_live/index.html.heex:46
#: lib/cannery_web/live/container_live/show.html.heex:37 #: lib/cannery_web/live/container_live/show.html.heex:37
#: lib/cannery_web/live/tag_live/index.html.heex:38 #: lib/cannery_web/live/tag_live/index.html.heex:38
msgid "Are you sure you want to delete %{name}?" msgid "Are you sure you want to delete %{name}?"
@ -97,9 +92,9 @@ msgid "Are you sure you want to delete the invite for %{name}?"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.html.heex:120 #: lib/cannery_web/live/ammo_group_live/index.ex:167
#: lib/cannery_web/live/ammo_group_live/show.html.heex:47 #: lib/cannery_web/live/ammo_group_live/show.html.heex:66
#: lib/cannery_web/live/ammo_type_live/index.html.heex:68 #: lib/cannery_web/live/ammo_type_live/index.ex:130
msgid "Are you sure you want to delete this ammo?" msgid "Are you sure you want to delete this ammo?"
msgstr "" msgstr ""
@ -124,7 +119,7 @@ msgid "Email changed successfully."
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_confirmation_controller.ex:22 #: lib/cannery_web/controllers/user_confirmation_controller.ex:23
msgid "If your email is in our system and it has not been confirmed yet, you will receive an email with instructions shortly." msgid "If your email is in our system and it has not been confirmed yet, you will receive an email with instructions shortly."
msgstr "" msgstr ""
@ -139,7 +134,7 @@ msgid "Logged out successfully."
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_reset_password_controller.ex:43 #: lib/cannery_web/controllers/user_reset_password_controller.ex:46
msgid "Password reset successfully." msgid "Password reset successfully."
msgstr "" msgstr ""
@ -149,19 +144,19 @@ msgid "Password updated successfully."
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_registration_controller.ex:71 #: lib/cannery_web/controllers/user_registration_controller.ex:74
msgid "Please check your email to verify your account" msgid "Please check your email to verify your account"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/home_live.ex:97 #: lib/cannery_web/live/home_live.ex:103
msgid "Register to setup %{name}" msgid "Register to setup %{name}"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/add_shot_group_component.html.heex:44 #: lib/cannery_web/components/add_shot_group_component.html.heex:48
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:55 #: lib/cannery_web/live/ammo_group_live/form_component.html.heex:74
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:173 #: lib/cannery_web/live/ammo_type_live/form_component.html.heex:157
#: lib/cannery_web/live/container_live/form_component.html.heex:52 #: lib/cannery_web/live/container_live/form_component.html.heex:52
#: lib/cannery_web/live/invite_live/form_component.html.heex:30 #: lib/cannery_web/live/invite_live/form_component.html.heex:30
#: lib/cannery_web/live/range_live/form_component.html.heex:42 #: lib/cannery_web/live/range_live/form_component.html.heex:42
@ -175,22 +170,22 @@ msgid "Your account has been deleted"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/show.html.heex:71 #: lib/cannery_web/live/container_live/edit_tags_component.html.heex:16
msgid "Are you sure you want to remove the %{tag_name} tag from %{container_name}?" msgid "Are you sure you want to remove the %{tag_name} tag from %{container_name}?"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/add_tag_component.ex:40 #: lib/cannery_web/live/container_live/edit_tags_component.ex:36
msgid "%{name} added successfully" msgid "%{name} added successfully"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/show.ex:40 #: lib/cannery_web/live/container_live/show.ex:39
msgid "%{tag_name} has been removed from %{container_name}" msgid "%{tag_name} has been removed from %{container_name}"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/add_tag_component.html.heex:19 #: lib/cannery_web/live/container_live/edit_tags_component.html.heex:54
msgid "Adding..." msgid "Adding..."
msgstr "" msgstr ""
@ -210,11 +205,13 @@ msgid "Ammo group unstaged succesfully"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/range_live/index.html.heex:108 #: lib/cannery_web/live/ammo_group_live/show.ex:132
#: lib/cannery_web/live/range_live/index.ex:130
msgid "Are you sure you want to delete this shot record?" msgid "Are you sure you want to delete this shot record?"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/show.ex:80
#: lib/cannery_web/live/range_live/index.ex:56 #: lib/cannery_web/live/range_live/index.ex:56
msgid "Shot records deleted succesfully" msgid "Shot records deleted succesfully"
msgstr "" msgstr ""
@ -225,12 +222,12 @@ msgid "Shot records updated successfully"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/controllers/user_confirmation_controller.ex:37 #: lib/cannery_web/controllers/user_confirmation_controller.ex:38
msgid "%{email} confirmed successfully." msgid "%{email} confirmed successfully."
msgstr "" msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/cannery_web/components/move_ammo_group_component.ex:47 #: lib/cannery_web/components/move_ammo_group_component.ex:53
msgid "Ammo moved to %{name} successfully" msgid "Ammo moved to %{name} successfully"
msgstr "" msgstr ""
@ -238,3 +235,26 @@ msgstr ""
#: lib/cannery_web/live/invite_live/index.ex:123 #: lib/cannery_web/live/invite_live/index.ex:123
msgid "Copied to clipboard" msgid "Copied to clipboard"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/container_live/edit_tags_component.ex:58
msgid "%{name} removed successfully"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/index.html.heex:15
#: lib/cannery_web/live/ammo_group_live/index.html.heex:33
msgid "You'll need to"
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:67
msgid "Creating..."
msgstr ""
#, elixir-autogen, elixir-format
#: lib/cannery_web/live/ammo_group_live/form_component.ex:147
msgid "Ammo group created successfully"
msgid_plural "Ammo groups created successfully"
msgstr[0] ""
msgstr[1] ""

View File

@ -304,9 +304,9 @@ defmodule Cannery.AccountsTest do
end end
test "deletes all tokens for the given user", %{user: user} do test "deletes all tokens for the given user", %{user: user} do
_ = Accounts.generate_user_session_token(user) _token = Accounts.generate_user_session_token(user)
{:ok, _} = {: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"
}) })
@ -501,8 +501,8 @@ defmodule Cannery.AccountsTest do
end end
test "deletes all tokens for the given user", %{user: user} do test "deletes all tokens for the given user", %{user: user} do
_ = Accounts.generate_user_session_token(user) _token = Accounts.generate_user_session_token(user)
{:ok, _} = 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

@ -20,8 +20,8 @@ defmodule Cannery.ActivityLogTest do
container = container_fixture(current_user) container = container_fixture(current_user)
ammo_type = ammo_type_fixture(current_user) ammo_type = ammo_type_fixture(current_user)
%{id: ammo_group_id} = {1, [%{id: ammo_group_id} = ammo_group]} =
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"}

View File

@ -108,7 +108,7 @@ defmodule Cannery.AmmoTest do
current_user = user_fixture() current_user = user_fixture()
ammo_type = ammo_type_fixture(current_user) ammo_type = ammo_type_fixture(current_user)
container = container_fixture(current_user) container = container_fixture(current_user)
ammo_group = ammo_group_fixture(ammo_type, container, current_user) {1, [ammo_group]} = ammo_group_fixture(ammo_type, container, current_user)
[ [
ammo_type: ammo_type, ammo_type: ammo_type,
@ -120,36 +120,37 @@ defmodule Cannery.AmmoTest do
test "list_ammo_groups/0 returns all ammo_groups", test "list_ammo_groups/0 returns all ammo_groups",
%{ammo_group: ammo_group, current_user: current_user} do %{ammo_group: ammo_group, current_user: current_user} do
assert Ammo.list_ammo_groups(current_user) == [ammo_group] assert Ammo.list_ammo_groups(current_user) == [ammo_group] |> Repo.preload(:shot_groups)
end end
test "get_ammo_group!/1 returns the ammo_group with given id", test "get_ammo_group!/1 returns the ammo_group with given id",
%{ammo_group: ammo_group, current_user: current_user} do %{ammo_group: ammo_group, current_user: current_user} do
assert Ammo.get_ammo_group!(ammo_group.id, current_user) == ammo_group assert Ammo.get_ammo_group!(ammo_group.id, current_user) ==
ammo_group |> Repo.preload(:shot_groups)
end end
test "create_ammo_group/1 with valid data creates a ammo_group", test "create_ammo_groups/3 with valid data creates a ammo_group",
%{ %{
ammo_type: ammo_type, ammo_type: ammo_type,
container: container, container: container,
current_user: current_user current_user: current_user
} do } do
assert {:ok, %AmmoGroup{} = ammo_group} = assert {:ok, {1, [%AmmoGroup{} = ammo_group]}} =
@valid_attrs @valid_attrs
|> Map.merge(%{"ammo_type_id" => ammo_type.id, "container_id" => container.id}) |> Map.merge(%{"ammo_type_id" => ammo_type.id, "container_id" => container.id})
|> Ammo.create_ammo_group(current_user) |> Ammo.create_ammo_groups(1, current_user)
assert ammo_group.count == 42 assert ammo_group.count == 42
assert ammo_group.notes == "some notes" assert ammo_group.notes == "some notes"
assert ammo_group.price_paid == 120.5 assert ammo_group.price_paid == 120.5
end end
test "create_ammo_group/1 with invalid data returns error changeset", test "create_ammo_groups/3 with invalid data returns error changeset",
%{ammo_type: ammo_type, container: container, current_user: current_user} do %{ammo_type: ammo_type, container: container, current_user: current_user} do
assert {:error, %Changeset{}} = assert {:error, %Changeset{}} =
@invalid_attrs @invalid_attrs
|> Map.merge(%{"ammo_type_id" => ammo_type.id, "container_id" => container.id}) |> Map.merge(%{"ammo_type_id" => ammo_type.id, "container_id" => container.id})
|> Ammo.create_ammo_group(current_user) |> Ammo.create_ammo_groups(1, current_user)
end end
test "update_ammo_group/2 with valid data updates the ammo_group", test "update_ammo_group/2 with valid data updates the ammo_group",
@ -167,7 +168,8 @@ defmodule Cannery.AmmoTest do
assert {:error, %Changeset{}} = assert {:error, %Changeset{}} =
Ammo.update_ammo_group(ammo_group, @invalid_attrs, current_user) Ammo.update_ammo_group(ammo_group, @invalid_attrs, current_user)
assert ammo_group == Ammo.get_ammo_group!(ammo_group.id, current_user) assert ammo_group |> Repo.preload(:shot_groups) ==
Ammo.get_ammo_group!(ammo_group.id, current_user)
end end
test "delete_ammo_group/1 deletes the ammo_group", test "delete_ammo_group/1 deletes the ammo_group",

View File

@ -33,12 +33,14 @@ defmodule Cannery.ContainersTest do
test "list_containers/1 returns all containers", test "list_containers/1 returns all containers",
%{current_user: current_user, container: container} do %{current_user: current_user, container: container} do
assert Containers.list_containers(current_user) == [container] assert Containers.list_containers(current_user) ==
[container |> Repo.preload([:ammo_groups, :tags])]
end end
test "get_container!/1 returns the container with given id", test "get_container!/1 returns the container with given id",
%{current_user: current_user, container: container} do %{current_user: current_user, container: container} do
assert Containers.get_container!(container.id, current_user) == container assert Containers.get_container!(container.id, current_user) ==
container |> Repo.preload([:ammo_groups, :tags])
end end
test "create_container/1 with valid data creates a container", %{current_user: current_user} do test "create_container/1 with valid data creates a container", %{current_user: current_user} do
@ -73,7 +75,8 @@ defmodule Cannery.ContainersTest do
assert {:error, %Changeset{}} = assert {:error, %Changeset{}} =
Containers.update_container(container, current_user, @invalid_attrs) Containers.update_container(container, current_user, @invalid_attrs)
assert container == Containers.get_container!(container.id, current_user) assert container |> Repo.preload([:ammo_groups, :tags]) ==
Containers.get_container!(container.id, current_user)
end end
test "delete_container/1 deletes the container", test "delete_container/1 deletes the container",

View File

@ -116,7 +116,7 @@ defmodule CanneryWeb.UserAuthTest do
end end
test "does not authenticate if data is missing", %{conn: conn, current_user: current_user} do test "does not authenticate if data is missing", %{conn: conn, current_user: current_user} do
_ = Accounts.generate_user_session_token(current_user) _token = Accounts.generate_user_session_token(current_user)
conn = UserAuth.fetch_current_user(conn, []) conn = UserAuth.fetch_current_user(conn, [])
refute get_session(conn, :user_token) refute get_session(conn, :user_token)
refute conn.assigns.current_user refute conn.assigns.current_user

View File

@ -5,8 +5,7 @@ defmodule CanneryWeb.UserConfirmationControllerTest do
use CanneryWeb.ConnCase, async: true use CanneryWeb.ConnCase, async: true
import CanneryWeb.Gettext import CanneryWeb.Gettext
alias Cannery.Accounts alias Cannery.{Accounts, Repo}
alias Cannery.Repo
@moduletag :user_confirmation_controller_test @moduletag :user_confirmation_controller_test

View File

@ -6,18 +6,26 @@ defmodule CanneryWeb.AmmoGroupLiveTest do
use CanneryWeb.ConnCase use CanneryWeb.ConnCase
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
import CanneryWeb.Gettext import CanneryWeb.Gettext
alias Cannery.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"} @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 %{"count" => 5, "notes" => "some updated notes"}
@update_attrs %{count: 43, notes: "some updated notes", price_paid: 456.7} @create_attrs %{"count" => 42, "notes" => "some notes", "price_paid" => 120.5}
@update_attrs %{"count" => 43, "notes" => "some updated notes", "price_paid" => 456.7}
@ammo_group_create_limit 10_000
# @invalid_attrs %{count: -1, notes: nil, price_paid: nil} # @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)
%{ammo_group: ammo_group_fixture(ammo_type, container, current_user)} {1, [ammo_group]} = ammo_group_fixture(ammo_type, container, current_user)
shot_group =
%{"count" => 5, "date" => ~N[2022-02-13 03:17:00], "notes" => "some notes"}
|> shot_group_fixture(current_user, ammo_group)
%{ammo_group: ammo_group, shot_group: shot_group}
end end
describe "Index" do describe "Index" do
@ -31,7 +39,7 @@ defmodule CanneryWeb.AmmoGroupLiveTest do
assert html =~ ammo_group.ammo_type.name assert html =~ ammo_group.ammo_type.name
end end
test "saves 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", "New Ammo group")) |> render_click() =~ assert index_live |> element("a", dgettext("actions", "New Ammo group")) |> render_click() =~
@ -43,14 +51,76 @@ defmodule CanneryWeb.AmmoGroupLiveTest do
# |> form("#ammo_group-form", ammo_group: @invalid_attrs) # |> form("#ammo_group-form", ammo_group: @invalid_attrs)
# |> render_change() =~ dgettext("errors", "can't be blank") # |> render_change() =~ dgettext("errors", "can't be blank")
{:ok, _, html} = {:ok, _view, html} =
index_live index_live
|> form("#ammo_group-form", ammo_group: @create_attrs) |> form("#ammo_group-form", ammo_group: @create_attrs)
|> 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 group created successfully") assert html =~ dgettext("prompts", "Ammo group created successfully")
assert html =~ "some notes" assert html =~ "42"
end
test "saves multiple new ammo_groups", %{conn: conn, current_user: current_user} do
multiplier = 25
{:ok, index_live, _html} = live(conn, Routes.ammo_group_index_path(conn, :index))
assert index_live |> element("a", dgettext("actions", "New Ammo group")) |> render_click() =~
gettext("New Ammo group")
assert_patch(index_live, Routes.ammo_group_index_path(conn, :new))
# assert index_live
# |> form("#ammo_group-form", ammo_group: @invalid_attrs)
# |> render_change() =~ dgettext("errors", "can't be blank")
{:ok, _view, html} =
index_live
|> form("#ammo_group-form",
ammo_group: @create_attrs |> Map.put("multiplier", to_string(multiplier))
)
|> render_submit()
|> follow_redirect(conn, Routes.ammo_group_index_path(conn, :index))
assert html =~ dgettext("prompts", "Ammo groups created successfully")
assert Ammo.list_ammo_groups(current_user) |> Enum.count() == multiplier + 1
end
test "does not save invalid number of new ammo_groups", %{conn: conn} do
{:ok, index_live, _html} = live(conn, Routes.ammo_group_index_path(conn, :index))
assert index_live |> element("a", dgettext("actions", "New Ammo group")) |> render_click() =~
gettext("New Ammo group")
assert_patch(index_live, Routes.ammo_group_index_path(conn, :new))
# assert index_live
# |> form("#ammo_group-form", ammo_group: @invalid_attrs)
# |> render_change() =~ dgettext("errors", "can't be blank")
assert index_live
|> form("#ammo_group-form", ammo_group: @create_attrs |> Map.put("multiplier", "0"))
|> render_submit() =~
dgettext(
"errors",
"Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}",
multiplier: 0,
max: @ammo_group_create_limit
)
assert index_live
|> form("#ammo_group-form",
ammo_group:
@create_attrs |> Map.put("multiplier", to_string(@ammo_group_create_limit + 1))
)
|> render_submit() =~
dgettext(
"errors",
"Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}",
multiplier: @ammo_group_create_limit + 1,
max: @ammo_group_create_limit
)
end 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
@ -65,7 +135,7 @@ defmodule CanneryWeb.AmmoGroupLiveTest do
# |> form("#shot_group-form", shot_group: @invalid_attrs) # |> form("#shot_group-form", shot_group: @invalid_attrs)
# |> render_change() =~ dgettext("errors", "is invalid") # |> render_change() =~ dgettext("errors", "is invalid")
{:ok, _, html} = {:ok, _view, html} =
index_live index_live
|> form("#shot-group-form", shot_group: @shot_group_create_attrs) |> form("#shot-group-form", shot_group: @shot_group_create_attrs)
|> render_submit() |> render_submit()
@ -88,14 +158,14 @@ defmodule CanneryWeb.AmmoGroupLiveTest do
# |> form("#ammo_group-form", ammo_group: @invalid_attrs) # |> form("#ammo_group-form", ammo_group: @invalid_attrs)
# |> render_change() =~ dgettext("errors", "can't be blank") # |> render_change() =~ dgettext("errors", "can't be blank")
{:ok, _, html} = {:ok, _view, html} =
index_live index_live
|> form("#ammo_group-form", ammo_group: @update_attrs) |> form("#ammo_group-form", ammo_group: @update_attrs)
|> 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 group updated successfully") assert html =~ dgettext("prompts", "Ammo group updated successfully")
assert html =~ "some updated notes" assert html =~ "43"
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
@ -134,7 +204,7 @@ defmodule CanneryWeb.AmmoGroupLiveTest do
# |> form("#ammo_group-form", ammo_group: @invalid_attrs) # |> form("#ammo_group-form", ammo_group: @invalid_attrs)
# |> render_change() =~ dgettext("errors", "can't be blank") # |> render_change() =~ dgettext("errors", "can't be blank")
{:ok, _, html} = {:ok, _view, html} =
show_live show_live
|> form("#ammo_group-form", ammo_group: @update_attrs) |> form("#ammo_group-form", ammo_group: @update_attrs)
|> render_submit() |> render_submit()
@ -156,7 +226,7 @@ defmodule CanneryWeb.AmmoGroupLiveTest do
# |> form("#shot_group-form", shot_group: @invalid_attrs) # |> form("#shot_group-form", shot_group: @invalid_attrs)
# |> render_change() =~ dgettext("errors", "is invalid") # |> render_change() =~ dgettext("errors", "is invalid")
{:ok, _, html} = {:ok, _view, html} =
index_live index_live
|> form("#shot-group-form", shot_group: @shot_group_create_attrs) |> form("#shot-group-form", shot_group: @shot_group_create_attrs)
|> render_submit() |> render_submit()
@ -164,5 +234,40 @@ defmodule CanneryWeb.AmmoGroupLiveTest do
assert html =~ dgettext("prompts", "Shots recorded successfully") assert html =~ dgettext("prompts", "Shots recorded successfully")
end end
test "updates shot_group in listing",
%{conn: conn, ammo_group: ammo_group, shot_group: shot_group} do
{:ok, index_live, _html} = live(conn, Routes.ammo_group_show_path(conn, :edit, ammo_group))
assert index_live |> element("[data-qa=\"edit-#{shot_group.id}\"]") |> render_click() =~
gettext("Edit Shot Records")
assert_patch(
index_live,
Routes.ammo_group_show_path(conn, :edit_shot_group, ammo_group, shot_group)
)
# assert index_live
# |> form("#shot_group-form", shot_group: @invalid_attrs)
# |> render_change() =~ dgettext("errors", "is invalid")
{:ok, _view, html} =
index_live
|> form("#shot-group-form", shot_group: @shot_group_update_attrs)
|> render_submit()
|> follow_redirect(conn, Routes.ammo_group_show_path(conn, :show, ammo_group))
assert html =~ dgettext("actions", "Shot records updated successfully")
assert html =~ "some updated notes"
end
test "deletes shot_group in listing",
%{conn: conn, ammo_group: ammo_group, shot_group: shot_group} do
{:ok, index_live, _html} =
live(conn, Routes.ammo_group_show_path(conn, :edit_shot_group, ammo_group, shot_group))
assert index_live |> element("[data-qa=\"delete-#{shot_group.id}\"]") |> render_click()
refute has_element?(index_live, "#shot_group-#{shot_group.id}")
end
end end
end end

View File

@ -62,7 +62,7 @@ defmodule CanneryWeb.AmmoTypeLiveTest do
# |> form("#ammo_type-form", ammo_type: @invalid_attrs) # |> form("#ammo_type-form", ammo_type: @invalid_attrs)
# |> render_change() =~ dgettext("errors", "can't be blank") # |> render_change() =~ dgettext("errors", "can't be blank")
{:ok, _, html} = {:ok, _view, html} =
index_live index_live
|> form("#ammo_type-form", ammo_type: @create_attrs) |> form("#ammo_type-form", ammo_type: @create_attrs)
|> render_submit() |> render_submit()
@ -86,7 +86,7 @@ defmodule CanneryWeb.AmmoTypeLiveTest do
# |> form("#ammo_type-form", ammo_type: @invalid_attrs) # |> form("#ammo_type-form", ammo_type: @invalid_attrs)
# |> render_change() =~ dgettext("errors", "can't be blank") # |> render_change() =~ dgettext("errors", "can't be blank")
{:ok, _, html} = {:ok, _view, html} =
index_live index_live
|> form("#ammo_type-form", ammo_type: @update_attrs) |> form("#ammo_type-form", ammo_type: @update_attrs)
|> render_submit() |> render_submit()
@ -128,7 +128,7 @@ defmodule CanneryWeb.AmmoTypeLiveTest do
# |> form("#ammo_type-form", ammo_type: @invalid_attrs) # |> form("#ammo_type-form", ammo_type: @invalid_attrs)
# |> render_change() =~ dgettext("errors", "can't be blank") # |> render_change() =~ dgettext("errors", "can't be blank")
{:ok, _, html} = {:ok, _view, html} =
show_live show_live
|> form("#ammo_type-form", ammo_type: @update_attrs) |> form("#ammo_type-form", ammo_type: @update_attrs)
|> render_submit() |> render_submit()

View File

@ -52,7 +52,7 @@ defmodule CanneryWeb.ContainerLiveTest do
# |> form("#container-form", container: @invalid_attrs) # |> form("#container-form", container: @invalid_attrs)
# |> render_change() =~ dgettext("errors", "can't be blank") # |> render_change() =~ dgettext("errors", "can't be blank")
{:ok, _, html} = {:ok, _view, html} =
index_live index_live
|> form("#container-form", container: @create_attrs) |> form("#container-form", container: @create_attrs)
|> render_submit() |> render_submit()
@ -70,7 +70,7 @@ 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("[data-qa=\"edit-#{container.id}\"]") |> render_click() =~ assert index_live |> element("[data-qa=\"edit-#{container.id}\"]") |> render_click() =~
gettext("Edit Container") gettext("Edit %{name}", name: container.name)
assert_patch(index_live, Routes.container_index_path(conn, :edit, container)) assert_patch(index_live, Routes.container_index_path(conn, :edit, container))
@ -78,7 +78,7 @@ defmodule CanneryWeb.ContainerLiveTest do
# |> form("#container-form", container: @invalid_attrs) # |> form("#container-form", container: @invalid_attrs)
# |> render_change() =~ dgettext("errors", "can't be blank") # |> render_change() =~ dgettext("errors", "can't be blank")
{:ok, _, html} = {:ok, _view, html} =
index_live index_live
|> form("#container-form", container: @update_attrs) |> form("#container-form", container: @update_attrs)
|> render_submit() |> render_submit()
@ -103,7 +103,7 @@ defmodule CanneryWeb.ContainerLiveTest do
test "displays container", %{conn: conn, container: container} do test "displays container", %{conn: conn, container: container} 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 =~ gettext("Show Container") assert html =~ gettext("Show %{name}", name: container.name)
assert html =~ container.location assert html =~ container.location
end end
@ -115,7 +115,7 @@ 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("[data-qa=\"edit\"]") |> render_click() =~ assert show_live |> element("[data-qa=\"edit\"]") |> render_click() =~
gettext("Edit Container") gettext("Edit %{name}", name: container.name)
assert_patch(show_live, Routes.container_show_path(conn, :edit, container)) assert_patch(show_live, Routes.container_show_path(conn, :edit, container))
@ -123,7 +123,7 @@ defmodule CanneryWeb.ContainerLiveTest do
# |> form("#container-form", container: @invalid_attrs) # |> form("#container-form", container: @invalid_attrs)
# |> render_change() =~ dgettext("errors", "can't be blank") # |> render_change() =~ dgettext("errors", "can't be blank")
{:ok, _, html} = {:ok, _view, html} =
show_live show_live
|> form("#container-form", container: @update_attrs) |> form("#container-form", container: @update_attrs)
|> render_submit() |> render_submit()

View File

@ -40,7 +40,7 @@ defmodule CanneryWeb.InviteLiveTest do
# |> form("#invite-form", invite: @invalid_attrs) # |> form("#invite-form", invite: @invalid_attrs)
# |> render_change() =~ dgettext("errors", "can't be blank") # |> render_change() =~ dgettext("errors", "can't be blank")
{:ok, _, html} = {:ok, _view, html} =
index_live index_live
|> form("#invite-form", invite: @create_attrs) |> form("#invite-form", invite: @create_attrs)
|> render_submit() |> render_submit()
@ -63,7 +63,7 @@ defmodule CanneryWeb.InviteLiveTest do
# |> form("#invite-form", invite: @invalid_attrs) # |> form("#invite-form", invite: @invalid_attrs)
# |> render_change() =~ dgettext("errors", "can't be blank") # |> render_change() =~ dgettext("errors", "can't be blank")
{:ok, _, html} = {:ok, _view, html} =
index_live index_live
|> form("#invite-form", invite: @update_attrs) |> form("#invite-form", invite: @update_attrs)
|> render_submit() |> render_submit()

View File

@ -16,7 +16,9 @@ defmodule CanneryWeb.RangeLiveTest do
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)
ammo_group = ammo_group_fixture(%{"staged" => true}, ammo_type, container, current_user)
{1, [ammo_group]} =
ammo_group_fixture(%{"staged" => true}, ammo_type, container, current_user)
shot_group = 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"}
@ -47,7 +49,7 @@ defmodule CanneryWeb.RangeLiveTest do
# |> form("#shot_group-form", shot_group: @invalid_attrs) # |> form("#shot_group-form", shot_group: @invalid_attrs)
# |> render_change() =~ dgettext("errors", "is invalid") # |> render_change() =~ dgettext("errors", "is invalid")
{:ok, _, html} = {:ok, _view, html} =
index_live index_live
|> form("#shot-group-form", shot_group: @create_attrs) |> form("#shot-group-form", shot_group: @create_attrs)
|> render_submit() |> render_submit()
@ -69,7 +71,7 @@ defmodule CanneryWeb.RangeLiveTest do
# |> form("#shot_group-form", shot_group: @invalid_attrs) # |> form("#shot_group-form", shot_group: @invalid_attrs)
# |> render_change() =~ dgettext("errors", "is invalid") # |> render_change() =~ dgettext("errors", "is invalid")
{:ok, _, html} = {:ok, _view, html} =
index_live index_live
|> form("#shot-group-form", shot_group: @update_attrs) |> form("#shot-group-form", shot_group: @update_attrs)
|> render_submit() |> render_submit()

View File

@ -53,7 +53,7 @@ defmodule CanneryWeb.TagLiveTest do
# |> form("#tag-form", tag: @invalid_attrs) # |> form("#tag-form", tag: @invalid_attrs)
# |> render_change() =~ dgettext("errors", "can't be blank") # |> render_change() =~ dgettext("errors", "can't be blank")
{:ok, _, html} = {:ok, _view, html} =
index_live index_live
|> form("#tag-form", tag: @create_attrs) |> form("#tag-form", tag: @create_attrs)
|> render_submit() |> render_submit()
@ -75,7 +75,7 @@ defmodule CanneryWeb.TagLiveTest do
# |> form("#tag-form", tag: @invalid_attrs) # |> form("#tag-form", tag: @invalid_attrs)
# |> render_change() =~ dgettext("errors", "can't be blank") # |> render_change() =~ dgettext("errors", "can't be blank")
{:ok, _, html} = {:ok, _view, html} =
index_live index_live
|> form("#tag-form", tag: @update_attrs) |> form("#tag-form", tag: @update_attrs)
|> render_submit() |> render_submit()

View File

@ -5,12 +5,11 @@ defmodule CanneryWeb.ErrorViewTest do
use CanneryWeb.ConnCase, async: true use CanneryWeb.ConnCase, async: true
import CanneryWeb.Gettext import CanneryWeb.Gettext
@moduletag :error_view_test
# 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
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", []) =~
dgettext("errors", "Not found") dgettext("errors", "Not found")

View File

@ -25,6 +25,7 @@ defmodule CanneryWeb.ConnCase do
# Import conveniences for testing with connections # Import conveniences for testing with connections
import Plug.Conn import Plug.Conn
import Phoenix.ConnTest import Phoenix.ConnTest
# credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
import Cannery.Fixtures import Cannery.Fixtures
import CanneryWeb.ConnCase import CanneryWeb.ConnCase

View File

@ -22,10 +22,8 @@ defmodule Cannery.DataCase do
alias Cannery.Repo alias Cannery.Repo
import Ecto import Ecto
import Ecto.Changeset import Ecto.{Changeset, Query}
import Ecto.Query import Cannery.{DataCase, Fixtures}
import Cannery.DataCase
import Cannery.Fixtures
end end
end end
@ -45,7 +43,7 @@ defmodule Cannery.DataCase do
""" """
def errors_on(changeset) do def errors_on(changeset) do
Ecto.Changeset.traverse_errors(changeset, fn {message, opts} -> Ecto.Changeset.traverse_errors(changeset, fn {message, opts} ->
Regex.replace(~r"%{(\w+)}", message, fn _, key -> Regex.replace(~r"%{(\w+)}", message, fn _content, key ->
opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string() opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string()
end) end)
end) end)

View File

@ -111,10 +111,20 @@ defmodule Cannery.Fixtures do
@doc """ @doc """
Generate a AmmoGroup Generate a AmmoGroup
""" """
@spec ammo_group_fixture(AmmoType.t(), Container.t(), User.t()) :: AmmoGroup.t() @spec ammo_group_fixture(AmmoType.t(), Container.t(), User.t()) ::
@spec ammo_group_fixture(attrs :: map(), AmmoType.t(), Container.t(), User.t()) :: AmmoGroup.t() {count :: non_neg_integer(), [AmmoGroup.t()]}
@spec ammo_group_fixture(attrs :: map(), AmmoType.t(), Container.t(), User.t()) ::
{count :: non_neg_integer(), [AmmoGroup.t()]}
@spec ammo_group_fixture(
attrs :: map(),
multiplier :: non_neg_integer(),
AmmoType.t(),
Container.t(),
User.t()
) :: {count :: non_neg_integer(), [AmmoGroup.t()]}
def ammo_group_fixture( def ammo_group_fixture(
attrs \\ %{}, attrs \\ %{},
multiplier \\ 1,
%AmmoType{id: ammo_type_id}, %AmmoType{id: ammo_type_id},
%Container{id: container_id}, %Container{id: container_id},
%User{} = user %User{} = user
@ -125,7 +135,7 @@ defmodule Cannery.Fixtures do
"container_id" => container_id, "container_id" => container_id,
"count" => 20 "count" => 20
}) })
|> Ammo.create_ammo_group(user) |> Ammo.create_ammo_groups(multiplier, user)
|> unwrap_ok_tuple() |> unwrap_ok_tuple()
end end