Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
b32edd581d | |||
2e372ca2ab | |||
fd0bac3bbf | |||
f83fbc5d99 | |||
daab051026 | |||
440dc5061b | |||
c0d2c69144 | |||
7a7359fa66 | |||
9e8fd00d65 | |||
f5f72b53e6 | |||
a54cf8b87d | |||
0b7146ba32 | |||
c0441957b6 | |||
7fa9933a9b | |||
f4c7f22460 | |||
a01d97e360 | |||
a53b352cf7 | |||
ce07cc2569 | |||
3acecb9a93 | |||
ab8561fcf0 | |||
8163b906a2 |
16
CHANGELOG.md
16
CHANGELOG.md
@ -1,3 +1,19 @@
|
|||||||
|
# v0.8.5
|
||||||
|
- Add link in readme to github mirror
|
||||||
|
- Fix tables unable to sort on empty dates
|
||||||
|
- Only show historical ammo type information when displaying "Show used"
|
||||||
|
- Fix even more accessibility issues
|
||||||
|
|
||||||
|
# v0.8.4
|
||||||
|
- Improve accessibility
|
||||||
|
- Code quality improvements
|
||||||
|
- Fix dead link of example bullet abbreviations
|
||||||
|
- Fix inaccurate error message when updating shot records
|
||||||
|
- Fix tables not sorting dates correctly
|
||||||
|
- Fix dates displaying incorrectly
|
||||||
|
- Fix container table not displaying all fields
|
||||||
|
- Fix textareas resizing when typing in them
|
||||||
|
|
||||||
# v0.8.3
|
# v0.8.3
|
||||||
- Improve some styles
|
- Improve some styles
|
||||||
- Improve server log
|
- Improve server log
|
||||||
|
@ -92,6 +92,15 @@ Cannery is licensed under AGPLv3 or later. A copy of the latest version of the
|
|||||||
license can be found at
|
license can be found at
|
||||||
[LICENSE.md](https://gitea.bubbletea.dev/shibao/cannery/src/branch/stable/LICENSE.md).
|
[LICENSE.md](https://gitea.bubbletea.dev/shibao/cannery/src/branch/stable/LICENSE.md).
|
||||||
|
|
||||||
|
# Links
|
||||||
|
|
||||||
|
- [Gitea](https://gitea.bubbletea.dev/shibao/cannery): Main repo, feature
|
||||||
|
requests and bug reports
|
||||||
|
- [Github](https://github.com/shibaobun/cannery): Source code mirror, please
|
||||||
|
don't open pull requests to this repository
|
||||||
|
- [Weblate](https://weblate.bubbletea.dev/engage/cannery): Contribute to
|
||||||
|
translations!
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[![Build
|
[![Build
|
||||||
|
@ -27,23 +27,15 @@ import { LiveSocket } from 'phoenix_live_view'
|
|||||||
import topbar from 'topbar'
|
import topbar from 'topbar'
|
||||||
import MaintainAttrs from './maintain_attrs'
|
import MaintainAttrs from './maintain_attrs'
|
||||||
import ShotLogChart from './shot_log_chart'
|
import ShotLogChart from './shot_log_chart'
|
||||||
import Alpine from 'alpinejs'
|
import Date from './date'
|
||||||
|
import DateTime from './datetime'
|
||||||
|
|
||||||
const csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute('content')
|
const csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute('content')
|
||||||
const liveSocket = new LiveSocket('/live', Socket, {
|
const liveSocket = new LiveSocket('/live', Socket, {
|
||||||
dom: {
|
|
||||||
onBeforeElUpdated (from, to) {
|
|
||||||
if (from._x_dataStack) { window.Alpine.clone(from, to) }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
params: { _csrf_token: csrfToken },
|
params: { _csrf_token: csrfToken },
|
||||||
hooks: { MaintainAttrs, ShotLogChart }
|
hooks: { Date, DateTime, MaintainAttrs, ShotLogChart }
|
||||||
})
|
})
|
||||||
|
|
||||||
// alpine.js
|
|
||||||
window.Alpine = Alpine
|
|
||||||
Alpine.start()
|
|
||||||
|
|
||||||
// Show progress bar on live navigation and form submits
|
// Show progress bar on live navigation and form submits
|
||||||
topbar.config({ barColors: { 0: '#29d' }, shadowColor: 'rgba(0, 0, 0, .3)' })
|
topbar.config({ barColors: { 0: '#29d' }, shadowColor: 'rgba(0, 0, 0, .3)' })
|
||||||
window.addEventListener('phx:page-loading-start', info => topbar.show())
|
window.addEventListener('phx:page-loading-start', info => topbar.show())
|
||||||
|
11
assets/js/date.js
Normal file
11
assets/js/date.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export default {
|
||||||
|
displayDate (el) {
|
||||||
|
const date =
|
||||||
|
Intl.DateTimeFormat([], { timeZone: 'Etc/UTC', dateStyle: 'short' })
|
||||||
|
.format(new Date(el.dateTime))
|
||||||
|
|
||||||
|
el.innerText = date
|
||||||
|
},
|
||||||
|
mounted () { this.displayDate(this.el) },
|
||||||
|
updated () { this.displayDate(this.el) }
|
||||||
|
}
|
11
assets/js/datetime.js
Normal file
11
assets/js/datetime.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export default {
|
||||||
|
displayDateTime (el) {
|
||||||
|
const date =
|
||||||
|
Intl.DateTimeFormat([], { dateStyle: 'short', timeStyle: 'long' })
|
||||||
|
.format(new Date(el.dateTime))
|
||||||
|
|
||||||
|
el.innerText = date
|
||||||
|
},
|
||||||
|
mounted () { this.displayDateTime(this.el) },
|
||||||
|
updated () { this.displayDateTime(this.el) }
|
||||||
|
}
|
9280
assets/package-lock.json
generated
9280
assets/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "v18.9.1",
|
"node": "v18.9.1",
|
||||||
"npm": "8.10.0"
|
"npm": "8.19.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"deploy": "NODE_ENV=production webpack --mode production",
|
"deploy": "NODE_ENV=production webpack --mode production",
|
||||||
@ -13,37 +13,37 @@
|
|||||||
"test": "standard"
|
"test": "standard"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^6.1.1",
|
"@fortawesome/fontawesome-free": "^6.3.0",
|
||||||
"alpinejs": "^3.10.2",
|
"chart.js": "^4.2.1",
|
||||||
"chart.js": "^3.9.1",
|
"chartjs-adapter-date-fns": "^3.0.0",
|
||||||
"chartjs-adapter-date-fns": "^2.0.0",
|
|
||||||
"date-fns": "^2.29.3",
|
"date-fns": "^2.29.3",
|
||||||
"phoenix": "file:../deps/phoenix",
|
"phoenix": "file:../deps/phoenix",
|
||||||
"phoenix_html": "file:../deps/phoenix_html",
|
"phoenix_html": "file:../deps/phoenix_html",
|
||||||
"phoenix_live_view": "file:../deps/phoenix_live_view",
|
"phoenix_live_view": "file:../deps/phoenix_live_view",
|
||||||
"topbar": "^1.0.1"
|
"topbar": "^2.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.17.10",
|
"@babel/core": "^7.21.3",
|
||||||
"@babel/preset-env": "^7.17.10",
|
"@babel/preset-env": "^7.20.2",
|
||||||
"autoprefixer": "^10.4.7",
|
"autoprefixer": "^10.4.14",
|
||||||
"babel-loader": "^8.2.5",
|
"babel-loader": "^9.1.2",
|
||||||
"copy-webpack-plugin": "^10.2.4",
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
"css-loader": "^6.7.1",
|
"css-loader": "^6.7.3",
|
||||||
"css-minimizer-webpack-plugin": "^3.4.1",
|
"css-minimizer-webpack-plugin": "^4.2.2",
|
||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"mini-css-extract-plugin": "^2.6.0",
|
"mini-css-extract-plugin": "^2.7.5",
|
||||||
"postcss": "^8.4.13",
|
"npm-check-updates": "^16.7.12",
|
||||||
"postcss-import": "^14.1.0",
|
"postcss": "^8.4.21",
|
||||||
"postcss-loader": "^6.2.1",
|
"postcss-import": "^15.1.0",
|
||||||
"postcss-preset-env": "^7.5.0",
|
"postcss-loader": "^7.1.0",
|
||||||
"sass": "^1.56.0",
|
"postcss-preset-env": "^8.0.1",
|
||||||
"sass-loader": "^12.6.0",
|
"sass": "^1.59.3",
|
||||||
|
"sass-loader": "^13.2.1",
|
||||||
"standard": "^17.0.0",
|
"standard": "^17.0.0",
|
||||||
"tailwindcss": "^3.0.24",
|
"tailwindcss": "^3.2.7",
|
||||||
"terser-webpack-plugin": "^5.3.1",
|
"terser-webpack-plugin": "^5.3.7",
|
||||||
"webpack": "^5.72.0",
|
"webpack": "^5.76.2",
|
||||||
"webpack-cli": "^4.9.2",
|
"webpack-cli": "^5.0.1",
|
||||||
"webpack-dev-server": "^4.9.0"
|
"webpack-dev-server": "^4.13.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -385,8 +385,18 @@ defmodule Cannery.Accounts do
|
|||||||
"""
|
"""
|
||||||
@spec allow_registration?() :: boolean()
|
@spec allow_registration?() :: boolean()
|
||||||
def allow_registration? do
|
def allow_registration? do
|
||||||
Application.get_env(:cannery, Cannery.Accounts)[:registration] == "public" or
|
registration_mode() == :public or list_users_by_role(:admin) |> Enum.empty?()
|
||||||
list_users_by_role(:admin) |> Enum.empty?()
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns an atom representing the current configured registration mode
|
||||||
|
"""
|
||||||
|
@spec registration_mode() :: :public | :invite_only
|
||||||
|
def registration_mode do
|
||||||
|
case Application.get_env(:cannery, Cannery.Accounts)[:registration] do
|
||||||
|
"public" -> :public
|
||||||
|
_other -> :invite_only
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -100,13 +100,23 @@ defmodule Cannery.Accounts.Invites do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec get_use_count(Invite.t(), User.t()) :: non_neg_integer()
|
@spec get_use_count(Invite.t(), User.t()) :: non_neg_integer() | nil
|
||||||
def get_use_count(%Invite{id: invite_id}, %User{role: :admin}) do
|
def get_use_count(%Invite{id: invite_id} = invite, user) do
|
||||||
Repo.one(
|
[invite] |> get_use_counts(user) |> Map.get(invite_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_use_counts([Invite.t()], User.t()) ::
|
||||||
|
%{optional(Invite.id()) => non_neg_integer()}
|
||||||
|
def get_use_counts(invites, %User{role: :admin}) do
|
||||||
|
invite_ids = invites |> Enum.map(fn %{id: invite_id} -> invite_id end)
|
||||||
|
|
||||||
|
Repo.all(
|
||||||
from u in User,
|
from u in User,
|
||||||
where: u.invite_id == ^invite_id,
|
where: u.invite_id in ^invite_ids,
|
||||||
select: count(u.id)
|
group_by: u.invite_id,
|
||||||
|
select: {u.invite_id, count(u.id)}
|
||||||
)
|
)
|
||||||
|
|> Map.new()
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec decrement_invite_changeset(Invite.t()) :: Invite.changeset()
|
@spec decrement_invite_changeset(Invite.t()) :: Invite.changeset()
|
||||||
|
@ -4,7 +4,8 @@ defmodule Cannery.ActivityLog do
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import Ecto.Query, warn: false
|
import Ecto.Query, warn: false
|
||||||
alias Cannery.{Accounts.User, ActivityLog.ShotGroup, Ammo.AmmoGroup, Repo}
|
alias Cannery.Ammo.{AmmoGroup, AmmoType}
|
||||||
|
alias Cannery.{Accounts.User, ActivityLog.ShotGroup, Repo}
|
||||||
alias Ecto.Multi
|
alias Ecto.Multi
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
@ -31,8 +32,10 @@ defmodule Cannery.ActivityLog do
|
|||||||
|
|
||||||
Repo.all(
|
Repo.all(
|
||||||
from sg in ShotGroup,
|
from sg in ShotGroup,
|
||||||
left_join: ag in assoc(sg, :ammo_group),
|
left_join: ag in AmmoGroup,
|
||||||
left_join: at in assoc(ag, :ammo_type),
|
on: sg.ammo_group_id == ag.id,
|
||||||
|
left_join: at in AmmoType,
|
||||||
|
on: ag.ammo_type_id == at.id,
|
||||||
where: sg.user_id == ^user_id,
|
where: sg.user_id == ^user_id,
|
||||||
where:
|
where:
|
||||||
fragment(
|
fragment(
|
||||||
@ -61,6 +64,18 @@ defmodule Cannery.ActivityLog do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec list_shot_groups_for_ammo_group(AmmoGroup.t(), User.t()) :: [ShotGroup.t()]
|
||||||
|
def list_shot_groups_for_ammo_group(
|
||||||
|
%AmmoGroup{id: ammo_group_id, user_id: user_id},
|
||||||
|
%User{id: user_id}
|
||||||
|
) do
|
||||||
|
Repo.all(
|
||||||
|
from sg in ShotGroup,
|
||||||
|
where: sg.ammo_group_id == ^ammo_group_id,
|
||||||
|
where: sg.user_id == ^user_id
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Gets a single shot_group.
|
Gets a single shot_group.
|
||||||
|
|
||||||
@ -107,9 +122,15 @@ defmodule Cannery.ActivityLog do
|
|||||||
)
|
)
|
||||||
|> Multi.run(
|
|> Multi.run(
|
||||||
:ammo_group,
|
:ammo_group,
|
||||||
fn repo, %{create_shot_group: %{ammo_group_id: ammo_group_id, user_id: user_id}} ->
|
fn _repo, %{create_shot_group: %{ammo_group_id: ammo_group_id, user_id: user_id}} ->
|
||||||
{:ok,
|
ammo_group =
|
||||||
repo.one(from ag in AmmoGroup, where: ag.id == ^ammo_group_id and ag.user_id == ^user_id)}
|
Repo.one(
|
||||||
|
from ag in AmmoGroup,
|
||||||
|
where: ag.id == ^ammo_group_id,
|
||||||
|
where: ag.user_id == ^user_id
|
||||||
|
)
|
||||||
|
|
||||||
|
{:ok, ammo_group}
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
|> Multi.update(
|
|> Multi.update(
|
||||||
@ -220,4 +241,112 @@ defmodule Cannery.ActivityLog do
|
|||||||
{:error, _other_transaction, _value, _changes_so_far} -> {:error, nil}
|
{:error, _other_transaction, _value, _changes_so_far} -> {:error, nil}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns the number of shot rounds for an ammo group
|
||||||
|
"""
|
||||||
|
@spec get_used_count(AmmoGroup.t(), User.t()) :: non_neg_integer()
|
||||||
|
def get_used_count(%AmmoGroup{id: ammo_group_id} = ammo_group, user) do
|
||||||
|
[ammo_group]
|
||||||
|
|> get_used_counts(user)
|
||||||
|
|> Map.get(ammo_group_id, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns the number of shot rounds for multiple ammo groups
|
||||||
|
"""
|
||||||
|
@spec get_used_counts([AmmoGroup.t()], User.t()) ::
|
||||||
|
%{optional(AmmoGroup.id()) => non_neg_integer()}
|
||||||
|
def get_used_counts(ammo_groups, %User{id: user_id}) do
|
||||||
|
ammo_group_ids =
|
||||||
|
ammo_groups
|
||||||
|
|> Enum.map(fn %{id: ammo_group_id} -> ammo_group_id end)
|
||||||
|
|
||||||
|
Repo.all(
|
||||||
|
from sg in ShotGroup,
|
||||||
|
where: sg.ammo_group_id in ^ammo_group_ids,
|
||||||
|
where: sg.user_id == ^user_id,
|
||||||
|
group_by: sg.ammo_group_id,
|
||||||
|
select: {sg.ammo_group_id, sum(sg.count)}
|
||||||
|
)
|
||||||
|
|> Map.new()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns the last entered shot group date for an ammo group
|
||||||
|
"""
|
||||||
|
@spec get_last_used_date(AmmoGroup.t(), User.t()) :: Date.t() | nil
|
||||||
|
def get_last_used_date(%AmmoGroup{id: ammo_group_id} = ammo_group, user) do
|
||||||
|
[ammo_group]
|
||||||
|
|> get_last_used_dates(user)
|
||||||
|
|> Map.get(ammo_group_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns the last entered shot group date for an ammo group
|
||||||
|
"""
|
||||||
|
@spec get_last_used_dates([AmmoGroup.t()], User.t()) :: %{optional(AmmoGroup.id()) => Date.t()}
|
||||||
|
def get_last_used_dates(ammo_groups, %User{id: user_id}) do
|
||||||
|
ammo_group_ids =
|
||||||
|
ammo_groups
|
||||||
|
|> Enum.map(fn %AmmoGroup{id: ammo_group_id, user_id: ^user_id} -> ammo_group_id end)
|
||||||
|
|
||||||
|
Repo.all(
|
||||||
|
from sg in ShotGroup,
|
||||||
|
where: sg.ammo_group_id in ^ammo_group_ids,
|
||||||
|
where: sg.user_id == ^user_id,
|
||||||
|
group_by: sg.ammo_group_id,
|
||||||
|
select: {sg.ammo_group_id, max(sg.date)}
|
||||||
|
)
|
||||||
|
|> Map.new()
|
||||||
|
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})
|
||||||
|
35
|
||||||
|
|
||||||
|
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} = ammo_type, user) do
|
||||||
|
[ammo_type]
|
||||||
|
|> get_used_count_for_ammo_types(user)
|
||||||
|
|> Map.get(ammo_type_id, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Gets the total number of rounds shot for multiple ammo types
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> get_used_count_for_ammo_types(123, %User{id: 123})
|
||||||
|
35
|
||||||
|
|
||||||
|
"""
|
||||||
|
@spec get_used_count_for_ammo_types([AmmoType.t()], User.t()) ::
|
||||||
|
%{optional(AmmoType.id()) => non_neg_integer()}
|
||||||
|
def get_used_count_for_ammo_types(ammo_types, %User{id: user_id}) do
|
||||||
|
ammo_type_ids =
|
||||||
|
ammo_types
|
||||||
|
|> Enum.map(fn %AmmoType{id: ammo_type_id, user_id: ^user_id} -> ammo_type_id end)
|
||||||
|
|
||||||
|
Repo.all(
|
||||||
|
from ag in AmmoGroup,
|
||||||
|
left_join: sg in ShotGroup,
|
||||||
|
on: ag.id == sg.ammo_group_id,
|
||||||
|
where: ag.ammo_type_id in ^ammo_type_ids,
|
||||||
|
where: not (sg.count |> is_nil()),
|
||||||
|
group_by: ag.ammo_type_id,
|
||||||
|
select: {ag.ammo_type_id, sum(sg.count)}
|
||||||
|
)
|
||||||
|
|> Map.new()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -6,7 +6,7 @@ defmodule Cannery.ActivityLog.ShotGroup do
|
|||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
import CanneryWeb.Gettext
|
import CanneryWeb.Gettext
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
alias Cannery.{Accounts.User, ActivityLog.ShotGroup, Ammo.AmmoGroup, Repo}
|
alias Cannery.{Accounts.User, Ammo, Ammo.AmmoGroup}
|
||||||
alias Ecto.{Changeset, UUID}
|
alias Ecto.{Changeset, UUID}
|
||||||
|
|
||||||
@derive {Jason.Encoder,
|
@derive {Jason.Encoder,
|
||||||
@ -24,25 +24,23 @@ defmodule Cannery.ActivityLog.ShotGroup do
|
|||||||
field :date, :date
|
field :date, :date
|
||||||
field :notes, :string
|
field :notes, :string
|
||||||
|
|
||||||
belongs_to :user, User
|
field :user_id, :binary_id
|
||||||
belongs_to :ammo_group, AmmoGroup
|
field :ammo_group_id, :binary_id
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
@type t :: %ShotGroup{
|
@type t :: %__MODULE__{
|
||||||
id: id(),
|
id: id(),
|
||||||
count: integer,
|
count: integer,
|
||||||
notes: String.t() | nil,
|
notes: String.t() | nil,
|
||||||
date: Date.t() | nil,
|
date: Date.t() | nil,
|
||||||
ammo_group: AmmoGroup.t() | nil,
|
|
||||||
ammo_group_id: AmmoGroup.id(),
|
ammo_group_id: AmmoGroup.id(),
|
||||||
user: User.t() | nil,
|
|
||||||
user_id: User.id(),
|
user_id: User.id(),
|
||||||
inserted_at: NaiveDateTime.t(),
|
inserted_at: NaiveDateTime.t(),
|
||||||
updated_at: NaiveDateTime.t()
|
updated_at: NaiveDateTime.t()
|
||||||
}
|
}
|
||||||
@type new_shot_group :: %ShotGroup{}
|
@type new_shot_group :: %__MODULE__{}
|
||||||
@type id :: UUID.t()
|
@type id :: UUID.t()
|
||||||
@type changeset :: Changeset.t(t() | new_shot_group())
|
@type changeset :: Changeset.t(t() | new_shot_group())
|
||||||
|
|
||||||
@ -58,42 +56,47 @@ defmodule Cannery.ActivityLog.ShotGroup do
|
|||||||
%User{id: user_id},
|
%User{id: user_id},
|
||||||
%AmmoGroup{id: ammo_group_id, user_id: user_id} = ammo_group,
|
%AmmoGroup{id: ammo_group_id, user_id: user_id} = ammo_group,
|
||||||
attrs
|
attrs
|
||||||
)
|
) do
|
||||||
when not (user_id |> is_nil()) and not (ammo_group_id |> is_nil()) do
|
|
||||||
shot_group
|
shot_group
|
||||||
|> change(user_id: user_id)
|
|> change(user_id: user_id)
|
||||||
|> change(ammo_group_id: ammo_group_id)
|
|> change(ammo_group_id: ammo_group_id)
|
||||||
|> cast(attrs, [:count, :notes, :date])
|
|> cast(attrs, [:count, :notes, :date])
|
||||||
|> validate_number(:count, greater_than: 0)
|
|
||||||
|> validate_create_shot_group_count(ammo_group)
|
|> validate_create_shot_group_count(ammo_group)
|
||||||
|> validate_required([:count, :date, :ammo_group_id, :user_id])
|
|> validate_required([:date, :ammo_group_id, :user_id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_changeset(shot_group, _invalid_user, _invalid_ammo_group, attrs) do
|
def create_changeset(shot_group, _invalid_user, _invalid_ammo_group, attrs) do
|
||||||
shot_group
|
shot_group
|
||||||
|> cast(attrs, [:count, :notes, :date])
|
|> cast(attrs, [:count, :notes, :date])
|
||||||
|> validate_number(:count, greater_than: 0)
|
|> validate_required([:ammo_group_id, :user_id])
|
||||||
|> validate_required([:count, :ammo_group_id, :user_id])
|
|
||||||
|> add_error(:invalid, dgettext("errors", "Please select a valid user and ammo pack"))
|
|> add_error(:invalid, dgettext("errors", "Please select a valid user and ammo pack"))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp validate_create_shot_group_count(changeset, %AmmoGroup{count: ammo_group_count}) do
|
defp validate_create_shot_group_count(changeset, %AmmoGroup{count: ammo_group_count}) do
|
||||||
if changeset |> Changeset.get_field(:count) > ammo_group_count do
|
case changeset |> Changeset.get_field(:count) do
|
||||||
error = dgettext("errors", "Count must be less than %{count}", count: ammo_group_count)
|
nil ->
|
||||||
changeset |> Changeset.add_error(:count, error)
|
changeset |> Changeset.add_error(:ammo_left, dgettext("errors", "can't be blank"))
|
||||||
else
|
|
||||||
changeset
|
count when count > ammo_group_count ->
|
||||||
|
changeset
|
||||||
|
|> Changeset.add_error(:ammo_left, dgettext("errors", "Ammo left must be at least 0"))
|
||||||
|
|
||||||
|
count when count <= 0 ->
|
||||||
|
error =
|
||||||
|
dgettext("errors", "Ammo left can be at most %{count} rounds",
|
||||||
|
count: ammo_group_count - 1
|
||||||
|
)
|
||||||
|
|
||||||
|
changeset |> Changeset.add_error(:ammo_left, error)
|
||||||
|
|
||||||
|
_valid_count ->
|
||||||
|
changeset
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc false
|
@doc false
|
||||||
@spec update_changeset(t() | new_shot_group(), User.t(), attrs :: map()) :: changeset()
|
@spec update_changeset(t() | new_shot_group(), User.t(), attrs :: map()) :: changeset()
|
||||||
def update_changeset(
|
def update_changeset(%__MODULE__{} = shot_group, user, attrs) do
|
||||||
%ShotGroup{user_id: user_id} = shot_group,
|
|
||||||
%User{id: user_id} = user,
|
|
||||||
attrs
|
|
||||||
)
|
|
||||||
when not (user_id |> is_nil()) do
|
|
||||||
shot_group
|
shot_group
|
||||||
|> cast(attrs, [:count, :notes, :date])
|
|> cast(attrs, [:count, :notes, :date])
|
||||||
|> validate_number(:count, greater_than: 0)
|
|> validate_number(:count, greater_than: 0)
|
||||||
@ -103,26 +106,21 @@ defmodule Cannery.ActivityLog.ShotGroup do
|
|||||||
|
|
||||||
defp validate_update_shot_group_count(
|
defp validate_update_shot_group_count(
|
||||||
changeset,
|
changeset,
|
||||||
%ShotGroup{count: count} = shot_group,
|
%__MODULE__{ammo_group_id: ammo_group_id, count: count},
|
||||||
%User{id: user_id}
|
user
|
||||||
)
|
) do
|
||||||
when not (user_id |> is_nil()) do
|
%{count: ammo_group_count} = Ammo.get_ammo_group!(ammo_group_id, user)
|
||||||
%{ammo_group: %AmmoGroup{count: ammo_group_count, user_id: ^user_id}} =
|
|
||||||
shot_group |> Repo.preload(:ammo_group)
|
|
||||||
|
|
||||||
new_shot_group_count = changeset |> Changeset.get_field(:count)
|
new_shot_group_count = changeset |> Changeset.get_field(:count)
|
||||||
shot_diff_to_add = new_shot_group_count - count
|
shot_diff_to_add = new_shot_group_count - count
|
||||||
|
|
||||||
cond do
|
if shot_diff_to_add > ammo_group_count do
|
||||||
shot_diff_to_add > ammo_group_count ->
|
error =
|
||||||
error = dgettext("errors", "Count must be less than %{count}", count: ammo_group_count)
|
dgettext("errors", "Count can be at most %{count} shots", count: ammo_group_count + count)
|
||||||
changeset |> Changeset.add_error(:count, error)
|
|
||||||
|
|
||||||
new_shot_group_count <= 0 ->
|
changeset |> Changeset.add_error(:count, error)
|
||||||
changeset |> Changeset.add_error(:count, dgettext("errors", "Count must be at least 1"))
|
else
|
||||||
|
changeset
|
||||||
true ->
|
|
||||||
changeset
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -9,8 +9,8 @@ defmodule Cannery.Ammo.AmmoGroup do
|
|||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
import CanneryWeb.Gettext
|
import CanneryWeb.Gettext
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
alias Cannery.Ammo.{AmmoGroup, AmmoType}
|
alias Cannery.Ammo.AmmoType
|
||||||
alias Cannery.{Accounts.User, ActivityLog.ShotGroup, Containers, Containers.Container}
|
alias Cannery.{Accounts.User, Containers, Containers.Container}
|
||||||
alias Ecto.{Changeset, UUID}
|
alias Ecto.{Changeset, UUID}
|
||||||
|
|
||||||
@derive {Jason.Encoder,
|
@derive {Jason.Encoder,
|
||||||
@ -33,15 +33,13 @@ defmodule Cannery.Ammo.AmmoGroup do
|
|||||||
field :purchased_on, :date
|
field :purchased_on, :date
|
||||||
|
|
||||||
belongs_to :ammo_type, AmmoType
|
belongs_to :ammo_type, AmmoType
|
||||||
belongs_to :container, Container
|
field :container_id, :binary_id
|
||||||
belongs_to :user, User
|
field :user_id, :binary_id
|
||||||
|
|
||||||
has_many :shot_groups, ShotGroup
|
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
@type t :: %AmmoGroup{
|
@type t :: %__MODULE__{
|
||||||
id: id(),
|
id: id(),
|
||||||
count: integer,
|
count: integer,
|
||||||
notes: String.t() | nil,
|
notes: String.t() | nil,
|
||||||
@ -50,14 +48,12 @@ defmodule Cannery.Ammo.AmmoGroup do
|
|||||||
purchased_on: Date.t(),
|
purchased_on: Date.t(),
|
||||||
ammo_type: AmmoType.t() | nil,
|
ammo_type: AmmoType.t() | nil,
|
||||||
ammo_type_id: AmmoType.id(),
|
ammo_type_id: AmmoType.id(),
|
||||||
container: Container.t() | nil,
|
|
||||||
container_id: Container.id(),
|
container_id: Container.id(),
|
||||||
user: User.t() | nil,
|
|
||||||
user_id: User.id(),
|
user_id: User.id(),
|
||||||
inserted_at: NaiveDateTime.t(),
|
inserted_at: NaiveDateTime.t(),
|
||||||
updated_at: NaiveDateTime.t()
|
updated_at: NaiveDateTime.t()
|
||||||
}
|
}
|
||||||
@type new_ammo_group :: %AmmoGroup{}
|
@type new_ammo_group :: %__MODULE__{}
|
||||||
@type id :: UUID.t()
|
@type id :: UUID.t()
|
||||||
@type changeset :: Changeset.t(t() | new_ammo_group())
|
@type changeset :: Changeset.t(t() | new_ammo_group())
|
||||||
|
|
||||||
@ -76,8 +72,7 @@ defmodule Cannery.Ammo.AmmoGroup do
|
|||||||
%User{id: user_id},
|
%User{id: user_id},
|
||||||
attrs
|
attrs
|
||||||
)
|
)
|
||||||
when not (ammo_type_id |> is_nil()) and not (container_id |> is_nil()) and
|
when is_binary(ammo_type_id) and is_binary(container_id) and is_binary(user_id) do
|
||||||
not (user_id |> is_nil()) do
|
|
||||||
ammo_group
|
ammo_group
|
||||||
|> change(ammo_type_id: ammo_type_id)
|
|> change(ammo_type_id: ammo_type_id)
|
||||||
|> change(user_id: user_id)
|
|> change(user_id: user_id)
|
||||||
|
@ -8,7 +8,7 @@ defmodule Cannery.Ammo.AmmoType do
|
|||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
alias Cannery.Accounts.User
|
alias Cannery.Accounts.User
|
||||||
alias Cannery.Ammo.{AmmoGroup, AmmoType}
|
alias Cannery.Ammo.AmmoGroup
|
||||||
alias Ecto.{Changeset, UUID}
|
alias Ecto.{Changeset, UUID}
|
||||||
|
|
||||||
@derive {Jason.Encoder,
|
@derive {Jason.Encoder,
|
||||||
@ -64,14 +64,14 @@ defmodule Cannery.Ammo.AmmoType do
|
|||||||
field :manufacturer, :string
|
field :manufacturer, :string
|
||||||
field :upc, :string
|
field :upc, :string
|
||||||
|
|
||||||
belongs_to :user, User
|
field :user_id, :binary_id
|
||||||
|
|
||||||
has_many :ammo_groups, AmmoGroup
|
has_many :ammo_groups, AmmoGroup
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
@type t :: %AmmoType{
|
@type t :: %__MODULE__{
|
||||||
id: id(),
|
id: id(),
|
||||||
name: String.t(),
|
name: String.t(),
|
||||||
desc: String.t() | nil,
|
desc: String.t() | nil,
|
||||||
@ -95,12 +95,11 @@ defmodule Cannery.Ammo.AmmoType do
|
|||||||
manufacturer: String.t() | nil,
|
manufacturer: String.t() | nil,
|
||||||
upc: String.t() | nil,
|
upc: String.t() | nil,
|
||||||
user_id: User.id(),
|
user_id: User.id(),
|
||||||
user: User.t() | nil,
|
|
||||||
ammo_groups: [AmmoGroup.t()] | nil,
|
ammo_groups: [AmmoGroup.t()] | nil,
|
||||||
inserted_at: NaiveDateTime.t(),
|
inserted_at: NaiveDateTime.t(),
|
||||||
updated_at: NaiveDateTime.t()
|
updated_at: NaiveDateTime.t()
|
||||||
}
|
}
|
||||||
@type new_ammo_type :: %AmmoType{}
|
@type new_ammo_type :: %__MODULE__{}
|
||||||
@type id :: UUID.t()
|
@type id :: UUID.t()
|
||||||
@type changeset :: Changeset.t(t() | new_ammo_type())
|
@type changeset :: Changeset.t(t() | new_ammo_type())
|
||||||
|
|
||||||
|
12
lib/cannery/comparable_date.ex
Normal file
12
lib/cannery/comparable_date.ex
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
defmodule Cannery.ComparableDate do
|
||||||
|
@moduledoc """
|
||||||
|
A custom `Date` module that provides a `compare/2` function that is comparable
|
||||||
|
with nil values
|
||||||
|
"""
|
||||||
|
|
||||||
|
@spec compare(Date.t() | any(), Date.t() | any()) :: :lt | :gt | :eq
|
||||||
|
def compare(%Date{} = date_1, %Date{} = date_2), do: Date.compare(date_1, date_2)
|
||||||
|
def compare(%Date{}, _date_2), do: :lt
|
||||||
|
def compare(_date_1, %Date{}), do: :gt
|
||||||
|
def compare(_date_1, _date_2), do: :eq
|
||||||
|
end
|
15
lib/cannery/comparable_datetime.ex
Normal file
15
lib/cannery/comparable_datetime.ex
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
defmodule Cannery.ComparableDateTime do
|
||||||
|
@moduledoc """
|
||||||
|
A custom `DateTime` module that provides a `compare/2` function that is
|
||||||
|
comparable with nil values
|
||||||
|
"""
|
||||||
|
|
||||||
|
@spec compare(DateTime.t() | any(), DateTime.t() | any()) :: :lt | :gt | :eq
|
||||||
|
def compare(%DateTime{} = datetime_1, %DateTime{} = datetime_2) do
|
||||||
|
DateTime.compare(datetime_1, datetime_2)
|
||||||
|
end
|
||||||
|
|
||||||
|
def compare(%DateTime{}, _datetime_2), do: :lt
|
||||||
|
def compare(_datetime_1, %DateTime{}), do: :gt
|
||||||
|
def compare(_datetime_1, _datetime_2), do: :eq
|
||||||
|
end
|
@ -5,10 +5,12 @@ defmodule Cannery.Containers do
|
|||||||
|
|
||||||
import CanneryWeb.Gettext
|
import CanneryWeb.Gettext
|
||||||
import Ecto.Query, warn: false
|
import Ecto.Query, warn: false
|
||||||
alias Cannery.{Accounts.User, Ammo.AmmoGroup, Repo, Tags.Tag}
|
alias Cannery.{Accounts.User, Ammo.AmmoGroup, Repo}
|
||||||
alias Cannery.Containers.{Container, ContainerTag}
|
alias Cannery.Containers.{Container, ContainerTag, Tag}
|
||||||
alias Ecto.Changeset
|
alias Ecto.Changeset
|
||||||
|
|
||||||
|
@container_preloads [:tags]
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns the list of containers.
|
Returns the list of containers.
|
||||||
|
|
||||||
@ -28,11 +30,9 @@ defmodule Cannery.Containers do
|
|||||||
as: :c,
|
as: :c,
|
||||||
left_join: t in assoc(c, :tags),
|
left_join: t in assoc(c, :tags),
|
||||||
as: :t,
|
as: :t,
|
||||||
left_join: ag in assoc(c, :ammo_groups),
|
|
||||||
as: :ag,
|
|
||||||
where: c.user_id == ^user_id,
|
where: c.user_id == ^user_id,
|
||||||
order_by: c.name,
|
order_by: c.name,
|
||||||
preload: [tags: t, ammo_groups: ag]
|
preload: ^@container_preloads
|
||||||
)
|
)
|
||||||
|> list_containers_search(search)
|
|> list_containers_search(search)
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
@ -106,12 +106,10 @@ defmodule Cannery.Containers do
|
|||||||
def get_container!(id, %User{id: user_id}) do
|
def get_container!(id, %User{id: user_id}) do
|
||||||
Repo.one!(
|
Repo.one!(
|
||||||
from c in Container,
|
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.user_id == ^user_id,
|
||||||
where: c.id == ^id,
|
where: c.id == ^id,
|
||||||
order_by: c.name,
|
order_by: c.name,
|
||||||
preload: [tags: t, ammo_groups: ag]
|
preload: ^@container_preloads
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -130,7 +128,19 @@ defmodule Cannery.Containers do
|
|||||||
@spec create_container(attrs :: map(), User.t()) ::
|
@spec create_container(attrs :: map(), User.t()) ::
|
||||||
{:ok, Container.t()} | {:error, Container.changeset()}
|
{:ok, Container.t()} | {:error, Container.changeset()}
|
||||||
def create_container(attrs, %User{} = user) do
|
def create_container(attrs, %User{} = user) do
|
||||||
%Container{} |> Container.create_changeset(user, attrs) |> Repo.insert()
|
%Container{}
|
||||||
|
|> Container.create_changeset(user, attrs)
|
||||||
|
|> Repo.insert()
|
||||||
|
|> case do
|
||||||
|
{:ok, container} -> {:ok, container |> preload_container()}
|
||||||
|
{:error, changeset} -> {:error, changeset}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec preload_container(Container.t()) :: Container.t()
|
||||||
|
@spec preload_container([Container.t()]) :: [Container.t()]
|
||||||
|
def preload_container(container) do
|
||||||
|
container |> Repo.preload(@container_preloads)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
@ -148,7 +158,13 @@ defmodule Cannery.Containers do
|
|||||||
@spec update_container(Container.t(), User.t(), attrs :: map()) ::
|
@spec update_container(Container.t(), User.t(), attrs :: map()) ::
|
||||||
{:ok, Container.t()} | {:error, Container.changeset()}
|
{:ok, Container.t()} | {:error, Container.changeset()}
|
||||||
def update_container(%Container{user_id: user_id} = container, %User{id: user_id}, attrs) do
|
def update_container(%Container{user_id: user_id} = container, %User{id: user_id}, attrs) do
|
||||||
container |> Container.update_changeset(attrs) |> Repo.update()
|
container
|
||||||
|
|> Container.update_changeset(attrs)
|
||||||
|
|> Repo.update()
|
||||||
|
|> case do
|
||||||
|
{:ok, container} -> {:ok, container |> preload_container()}
|
||||||
|
{:error, changeset} -> {:error, changeset}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
@ -173,7 +189,12 @@ defmodule Cannery.Containers do
|
|||||||
)
|
)
|
||||||
|> case do
|
|> case do
|
||||||
0 ->
|
0 ->
|
||||||
container |> Repo.delete()
|
container
|
||||||
|
|> Repo.delete()
|
||||||
|
|> case do
|
||||||
|
{:ok, container} -> {:ok, container |> preload_container()}
|
||||||
|
{:error, changeset} -> {:error, changeset}
|
||||||
|
end
|
||||||
|
|
||||||
_amount ->
|
_amount ->
|
||||||
error = dgettext("errors", "Container must be empty before deleting")
|
error = dgettext("errors", "Container must be empty before deleting")
|
||||||
@ -214,8 +235,11 @@ defmodule Cannery.Containers do
|
|||||||
%Container{user_id: user_id} = container,
|
%Container{user_id: user_id} = container,
|
||||||
%Tag{user_id: user_id} = tag,
|
%Tag{user_id: user_id} = tag,
|
||||||
%User{id: user_id}
|
%User{id: user_id}
|
||||||
),
|
) do
|
||||||
do: %ContainerTag{} |> ContainerTag.create_changeset(tag, container) |> Repo.insert!()
|
%ContainerTag{}
|
||||||
|
|> ContainerTag.create_changeset(tag, container)
|
||||||
|
|> Repo.insert!()
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Removes a tag from a container
|
Removes a tag from a container
|
||||||
@ -226,45 +250,175 @@ defmodule Cannery.Containers do
|
|||||||
%Container{}
|
%Container{}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@spec remove_tag!(Container.t(), Tag.t(), User.t()) :: non_neg_integer()
|
@spec remove_tag!(Container.t(), Tag.t(), User.t()) :: {non_neg_integer(), [ContainerTag.t()]}
|
||||||
def remove_tag!(
|
def remove_tag!(
|
||||||
%Container{id: container_id, user_id: user_id},
|
%Container{id: container_id, user_id: user_id},
|
||||||
%Tag{id: tag_id, user_id: user_id},
|
%Tag{id: tag_id, user_id: user_id},
|
||||||
%User{id: user_id}
|
%User{id: user_id}
|
||||||
) do
|
) do
|
||||||
{count, _} =
|
{count, results} =
|
||||||
Repo.delete_all(
|
Repo.delete_all(
|
||||||
from ct in ContainerTag,
|
from ct in ContainerTag,
|
||||||
where: ct.container_id == ^container_id,
|
where: ct.container_id == ^container_id,
|
||||||
where: ct.tag_id == ^tag_id
|
where: ct.tag_id == ^tag_id,
|
||||||
|
select: ct
|
||||||
)
|
)
|
||||||
|
|
||||||
if count == 0, do: raise("could not delete container tag"), else: count
|
if count == 0, do: raise("could not delete container tag"), else: {count, results}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Container Tags
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns the list of tags.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> list_tags(%User{id: 123})
|
||||||
|
[%Tag{}, ...]
|
||||||
|
|
||||||
|
iex> list_tags("cool", %User{id: 123})
|
||||||
|
[%Tag{name: "my cool tag"}, ...]
|
||||||
|
|
||||||
|
"""
|
||||||
|
@spec list_tags(User.t()) :: [Tag.t()]
|
||||||
|
@spec list_tags(search :: nil | String.t(), User.t()) :: [Tag.t()]
|
||||||
|
def list_tags(search \\ nil, user)
|
||||||
|
|
||||||
|
def list_tags(search, %{id: user_id}) when search |> is_nil() or search == "",
|
||||||
|
do: Repo.all(from t in Tag, where: t.user_id == ^user_id, order_by: t.name)
|
||||||
|
|
||||||
|
def list_tags(search, %{id: user_id}) when search |> is_binary() do
|
||||||
|
trimmed_search = String.trim(search)
|
||||||
|
|
||||||
|
Repo.all(
|
||||||
|
from t in Tag,
|
||||||
|
where: t.user_id == ^user_id,
|
||||||
|
where:
|
||||||
|
fragment(
|
||||||
|
"? @@ websearch_to_tsquery('english', ?)",
|
||||||
|
t.search,
|
||||||
|
^trimmed_search
|
||||||
|
),
|
||||||
|
order_by: {
|
||||||
|
:desc,
|
||||||
|
fragment(
|
||||||
|
"ts_rank_cd(?, websearch_to_tsquery('english', ?), 4)",
|
||||||
|
t.search,
|
||||||
|
^trimmed_search
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns number of rounds in container. If data is already preloaded, then
|
Gets a single tag.
|
||||||
there will be no db hit.
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> get_tag(123, %User{id: 123})
|
||||||
|
{:ok, %Tag{}}
|
||||||
|
|
||||||
|
iex> get_tag(456, %User{id: 123})
|
||||||
|
{:error, :not_found}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@spec get_container_ammo_group_count!(Container.t()) :: non_neg_integer()
|
@spec get_tag(Tag.id(), User.t()) :: {:ok, Tag.t()} | {:error, :not_found}
|
||||||
def get_container_ammo_group_count!(%Container{} = container) do
|
def get_tag(id, %User{id: user_id}) do
|
||||||
container
|
Repo.one(from t in Tag, where: t.id == ^id and t.user_id == ^user_id)
|
||||||
|> Repo.preload(:ammo_groups)
|
|> case do
|
||||||
|> Map.fetch!(:ammo_groups)
|
nil -> {:error, :not_found}
|
||||||
|> Enum.reject(fn %{count: count} -> count == 0 end)
|
tag -> {:ok, tag}
|
||||||
|> Enum.count()
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns number of rounds in container. If data is already preloaded, then
|
Gets a single tag.
|
||||||
there will be no db hit.
|
|
||||||
|
Raises `Ecto.NoResultsError` if the Tag does not exist.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> get_tag!(123, %User{id: 123})
|
||||||
|
%Tag{}
|
||||||
|
|
||||||
|
iex> get_tag!(456, %User{id: 123})
|
||||||
|
** (Ecto.NoResultsError)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@spec get_container_rounds!(Container.t()) :: non_neg_integer()
|
@spec get_tag!(Tag.id(), User.t()) :: Tag.t()
|
||||||
def get_container_rounds!(%Container{} = container) do
|
def get_tag!(id, %User{id: user_id}) do
|
||||||
container
|
Repo.one!(
|
||||||
|> Repo.preload(:ammo_groups)
|
from t in Tag,
|
||||||
|> Map.fetch!(:ammo_groups)
|
where: t.id == ^id,
|
||||||
|> Enum.map(fn %{count: count} -> count end)
|
where: t.user_id == ^user_id
|
||||||
|> Enum.sum()
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Creates a tag.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> create_tag(%{field: value}, %User{id: 123})
|
||||||
|
{:ok, %Tag{}}
|
||||||
|
|
||||||
|
iex> create_tag(%{field: bad_value}, %User{id: 123})
|
||||||
|
{:error, %Changeset{}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
@spec create_tag(attrs :: map(), User.t()) ::
|
||||||
|
{:ok, Tag.t()} | {:error, Tag.changeset()}
|
||||||
|
def create_tag(attrs, %User{} = user) do
|
||||||
|
%Tag{} |> Tag.create_changeset(user, attrs) |> Repo.insert()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Updates a tag.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> update_tag(tag, %{field: new_value}, %User{id: 123})
|
||||||
|
{:ok, %Tag{}}
|
||||||
|
|
||||||
|
iex> update_tag(tag, %{field: bad_value}, %User{id: 123})
|
||||||
|
{:error, %Changeset{}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
@spec update_tag(Tag.t(), attrs :: map(), User.t()) ::
|
||||||
|
{:ok, Tag.t()} | {:error, Tag.changeset()}
|
||||||
|
def update_tag(%Tag{user_id: user_id} = tag, attrs, %User{id: user_id}) do
|
||||||
|
tag |> Tag.update_changeset(attrs) |> Repo.update()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Deletes a tag.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> delete_tag(tag, %User{id: 123})
|
||||||
|
{:ok, %Tag{}}
|
||||||
|
|
||||||
|
iex> delete_tag(tag, %User{id: 123})
|
||||||
|
{:error, %Changeset{}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
@spec delete_tag(Tag.t(), User.t()) :: {:ok, Tag.t()} | {:error, Tag.changeset()}
|
||||||
|
def delete_tag(%Tag{user_id: user_id} = tag, %User{id: user_id}) do
|
||||||
|
tag |> Repo.delete()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Deletes a tag.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> delete_tag!(tag, %User{id: 123})
|
||||||
|
%Tag{}
|
||||||
|
|
||||||
|
"""
|
||||||
|
@spec delete_tag!(Tag.t(), User.t()) :: Tag.t()
|
||||||
|
def delete_tag!(%Tag{user_id: user_id} = tag, %User{id: user_id}) do
|
||||||
|
tag |> Repo.delete!()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -6,8 +6,7 @@ defmodule Cannery.Containers.Container do
|
|||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
alias Ecto.{Changeset, UUID}
|
alias Ecto.{Changeset, UUID}
|
||||||
alias Cannery.Containers.{Container, ContainerTag}
|
alias Cannery.{Accounts.User, Containers.ContainerTag, Containers.Tag}
|
||||||
alias Cannery.{Accounts.User, Ammo.AmmoGroup, Tags.Tag}
|
|
||||||
|
|
||||||
@derive {Jason.Encoder,
|
@derive {Jason.Encoder,
|
||||||
only: [
|
only: [
|
||||||
@ -26,28 +25,25 @@ defmodule Cannery.Containers.Container do
|
|||||||
field :location, :string
|
field :location, :string
|
||||||
field :type, :string
|
field :type, :string
|
||||||
|
|
||||||
belongs_to :user, User
|
field :user_id, :binary_id
|
||||||
|
|
||||||
has_many :ammo_groups, AmmoGroup
|
|
||||||
many_to_many :tags, Tag, join_through: ContainerTag
|
many_to_many :tags, Tag, join_through: ContainerTag
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
@type t :: %Container{
|
@type t :: %__MODULE__{
|
||||||
id: id(),
|
id: id(),
|
||||||
name: String.t(),
|
name: String.t(),
|
||||||
desc: String.t(),
|
desc: String.t(),
|
||||||
location: String.t(),
|
location: String.t(),
|
||||||
type: String.t(),
|
type: String.t(),
|
||||||
user: User.t(),
|
|
||||||
user_id: User.id(),
|
user_id: User.id(),
|
||||||
ammo_groups: [AmmoGroup.t()] | nil,
|
|
||||||
tags: [Tag.t()] | nil,
|
tags: [Tag.t()] | nil,
|
||||||
inserted_at: NaiveDateTime.t(),
|
inserted_at: NaiveDateTime.t(),
|
||||||
updated_at: NaiveDateTime.t()
|
updated_at: NaiveDateTime.t()
|
||||||
}
|
}
|
||||||
@type new_container :: %Container{}
|
@type new_container :: %__MODULE__{}
|
||||||
@type id :: UUID.t()
|
@type id :: UUID.t()
|
||||||
@type changeset :: Changeset.t(t() | new_container())
|
@type changeset :: Changeset.t(t() | new_container())
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
defmodule Cannery.Containers.ContainerTag do
|
defmodule Cannery.Containers.ContainerTag do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
Thru-table struct for associating Cannery.Containers.Container and
|
Thru-table struct for associating Cannery.Containers.Container and
|
||||||
Cannery.Tags.Tag.
|
Cannery.Containers.Tag.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
alias Cannery.{Containers.Container, Containers.ContainerTag, Tags.Tag}
|
alias Cannery.Containers.{Container, Tag}
|
||||||
alias Ecto.{Changeset, UUID}
|
alias Ecto.{Changeset, UUID}
|
||||||
|
|
||||||
@primary_key {:id, :binary_id, autogenerate: true}
|
@primary_key {:id, :binary_id, autogenerate: true}
|
||||||
@ -18,7 +18,7 @@ defmodule Cannery.Containers.ContainerTag do
|
|||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
@type t :: %ContainerTag{
|
@type t :: %__MODULE__{
|
||||||
id: id(),
|
id: id(),
|
||||||
container: Container.t(),
|
container: Container.t(),
|
||||||
container_id: Container.id(),
|
container_id: Container.id(),
|
||||||
@ -27,7 +27,7 @@ defmodule Cannery.Containers.ContainerTag do
|
|||||||
inserted_at: NaiveDateTime.t(),
|
inserted_at: NaiveDateTime.t(),
|
||||||
updated_at: NaiveDateTime.t()
|
updated_at: NaiveDateTime.t()
|
||||||
}
|
}
|
||||||
@type new_container_tag :: %ContainerTag{}
|
@type new_container_tag :: %__MODULE__{}
|
||||||
@type id :: UUID.t()
|
@type id :: UUID.t()
|
||||||
@type changeset :: Changeset.t(t() | new_container_tag())
|
@type changeset :: Changeset.t(t() | new_container_tag())
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
defmodule Cannery.Tags.Tag do
|
defmodule Cannery.Containers.Tag do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
Tags are added to containers to help organize, and can include custom-defined
|
Tags are added to containers to help organize, and can include custom-defined
|
||||||
text and bg colors.
|
text and bg colors.
|
||||||
@ -6,8 +6,8 @@ defmodule Cannery.Tags.Tag do
|
|||||||
|
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
alias Cannery.Accounts.User
|
||||||
alias Ecto.{Changeset, UUID}
|
alias Ecto.{Changeset, UUID}
|
||||||
alias Cannery.{Accounts.User, Tags.Tag}
|
|
||||||
|
|
||||||
@derive {Jason.Encoder,
|
@derive {Jason.Encoder,
|
||||||
only: [
|
only: [
|
||||||
@ -23,22 +23,21 @@ defmodule Cannery.Tags.Tag do
|
|||||||
field :bg_color, :string
|
field :bg_color, :string
|
||||||
field :text_color, :string
|
field :text_color, :string
|
||||||
|
|
||||||
belongs_to :user, User
|
field :user_id, :binary_id
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
@type t :: %Tag{
|
@type t :: %__MODULE__{
|
||||||
id: id(),
|
id: id(),
|
||||||
name: String.t(),
|
name: String.t(),
|
||||||
bg_color: String.t(),
|
bg_color: String.t(),
|
||||||
text_color: String.t(),
|
text_color: String.t(),
|
||||||
user: User.t() | nil,
|
|
||||||
user_id: User.id(),
|
user_id: User.id(),
|
||||||
inserted_at: NaiveDateTime.t(),
|
inserted_at: NaiveDateTime.t(),
|
||||||
updated_at: NaiveDateTime.t()
|
updated_at: NaiveDateTime.t()
|
||||||
}
|
}
|
||||||
@type new_tag() :: %Tag{}
|
@type new_tag() :: %__MODULE__{}
|
||||||
@type id() :: UUID.t()
|
@type id() :: UUID.t()
|
||||||
@type changeset() :: Changeset.t(t() | new_tag())
|
@type changeset() :: Changeset.t(t() | new_tag())
|
||||||
|
|
@ -1,149 +0,0 @@
|
|||||||
defmodule Cannery.Tags do
|
|
||||||
@moduledoc """
|
|
||||||
The Tags context.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import Ecto.Query, warn: false
|
|
||||||
import CanneryWeb.Gettext
|
|
||||||
alias Cannery.{Accounts.User, Repo, Tags.Tag}
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Returns the list of tags.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> list_tags(%User{id: 123})
|
|
||||||
[%Tag{}, ...]
|
|
||||||
|
|
||||||
iex> list_tags("cool", %User{id: 123})
|
|
||||||
[%Tag{name: "my cool tag"}, ...]
|
|
||||||
|
|
||||||
"""
|
|
||||||
@spec list_tags(User.t()) :: [Tag.t()]
|
|
||||||
@spec list_tags(search :: nil | String.t(), User.t()) :: [Tag.t()]
|
|
||||||
def list_tags(search \\ nil, user)
|
|
||||||
|
|
||||||
def list_tags(search, %{id: user_id}) when search |> is_nil() or search == "",
|
|
||||||
do: Repo.all(from t in Tag, where: t.user_id == ^user_id, order_by: t.name)
|
|
||||||
|
|
||||||
def list_tags(search, %{id: user_id}) when search |> is_binary() do
|
|
||||||
trimmed_search = String.trim(search)
|
|
||||||
|
|
||||||
Repo.all(
|
|
||||||
from t in Tag,
|
|
||||||
where: t.user_id == ^user_id,
|
|
||||||
where:
|
|
||||||
fragment(
|
|
||||||
"search @@ websearch_to_tsquery('english', ?)",
|
|
||||||
^trimmed_search
|
|
||||||
),
|
|
||||||
order_by: {
|
|
||||||
:desc,
|
|
||||||
fragment(
|
|
||||||
"ts_rank_cd(search, websearch_to_tsquery('english', ?), 4)",
|
|
||||||
^trimmed_search
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Gets a single tag.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> get_tag(123, %User{id: 123})
|
|
||||||
{:ok, %Tag{}}
|
|
||||||
|
|
||||||
iex> get_tag(456, %User{id: 123})
|
|
||||||
{:error, "tag not found"}
|
|
||||||
|
|
||||||
"""
|
|
||||||
@spec get_tag(Tag.id(), User.t()) :: {:ok, Tag.t()} | {:error, String.t()}
|
|
||||||
def get_tag(id, %User{id: user_id}) do
|
|
||||||
Repo.one(from t in Tag, where: t.id == ^id and t.user_id == ^user_id)
|
|
||||||
|> case do
|
|
||||||
nil -> {:error, dgettext("errors", "Tag not found")}
|
|
||||||
tag -> {:ok, tag}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Gets a single tag.
|
|
||||||
|
|
||||||
Raises `Ecto.NoResultsError` if the Tag does not exist.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> get_tag!(123, %User{id: 123})
|
|
||||||
%Tag{}
|
|
||||||
|
|
||||||
iex> get_tag!(456, %User{id: 123})
|
|
||||||
** (Ecto.NoResultsError)
|
|
||||||
|
|
||||||
"""
|
|
||||||
@spec get_tag!(Tag.id(), User.t()) :: Tag.t()
|
|
||||||
def get_tag!(id, %User{id: user_id}),
|
|
||||||
do: Repo.one!(from t in Tag, where: t.id == ^id and t.user_id == ^user_id)
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Creates a tag.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> create_tag(%{field: value}, %User{id: 123})
|
|
||||||
{:ok, %Tag{}}
|
|
||||||
|
|
||||||
iex> create_tag(%{field: bad_value}, %User{id: 123})
|
|
||||||
{:error, %Changeset{}}
|
|
||||||
|
|
||||||
"""
|
|
||||||
@spec create_tag(attrs :: map(), User.t()) ::
|
|
||||||
{:ok, Tag.t()} | {:error, Tag.changeset()}
|
|
||||||
def create_tag(attrs, %User{} = user),
|
|
||||||
do: %Tag{} |> Tag.create_changeset(user, attrs) |> Repo.insert()
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Updates a tag.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> update_tag(tag, %{field: new_value}, %User{id: 123})
|
|
||||||
{:ok, %Tag{}}
|
|
||||||
|
|
||||||
iex> update_tag(tag, %{field: bad_value}, %User{id: 123})
|
|
||||||
{:error, %Changeset{}}
|
|
||||||
|
|
||||||
"""
|
|
||||||
@spec update_tag(Tag.t(), attrs :: map(), User.t()) ::
|
|
||||||
{:ok, Tag.t()} | {:error, Tag.changeset()}
|
|
||||||
def update_tag(%Tag{user_id: user_id} = tag, attrs, %User{id: user_id}),
|
|
||||||
do: tag |> Tag.update_changeset(attrs) |> Repo.update()
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Deletes a tag.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> delete_tag(tag, %User{id: 123})
|
|
||||||
{:ok, %Tag{}}
|
|
||||||
|
|
||||||
iex> delete_tag(tag, %User{id: 123})
|
|
||||||
{:error, %Changeset{}}
|
|
||||||
|
|
||||||
"""
|
|
||||||
@spec delete_tag(Tag.t(), User.t()) :: {:ok, Tag.t()} | {:error, Tag.changeset()}
|
|
||||||
def delete_tag(%Tag{user_id: user_id} = tag, %User{id: user_id}), do: tag |> Repo.delete()
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Deletes a tag.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> delete_tag!(tag, %User{id: 123})
|
|
||||||
%Tag{}
|
|
||||||
|
|
||||||
"""
|
|
||||||
@spec delete_tag!(Tag.t(), User.t()) :: Tag.t()
|
|
||||||
def delete_tag!(%Tag{user_id: user_id} = tag, %User{id: user_id}), do: tag |> Repo.delete!()
|
|
||||||
end
|
|
@ -44,8 +44,7 @@ defmodule CanneryWeb do
|
|||||||
|
|
||||||
def live_view do
|
def live_view do
|
||||||
quote do
|
quote do
|
||||||
use Phoenix.LiveView,
|
use Phoenix.LiveView, layout: {CanneryWeb.LayoutView, :live}
|
||||||
layout: {CanneryWeb.LayoutView, "live.html"}
|
|
||||||
|
|
||||||
on_mount CanneryWeb.InitAssigns
|
on_mount CanneryWeb.InitAssigns
|
||||||
unquote(view_helpers())
|
unquote(view_helpers())
|
||||||
@ -94,7 +93,7 @@ defmodule CanneryWeb do
|
|||||||
|
|
||||||
# Import LiveView and .heex helpers (live_render, live_patch, <.form>, etc)
|
# Import LiveView and .heex helpers (live_render, live_patch, <.form>, etc)
|
||||||
# Import basic rendering functionality (render, render_layout, etc)
|
# Import basic rendering functionality (render, render_layout, etc)
|
||||||
import CanneryWeb.{ErrorHelpers, Gettext, LiveHelpers, ViewHelpers}
|
import CanneryWeb.{ErrorHelpers, Gettext, CoreComponents, ViewHelpers}
|
||||||
import Phoenix.{Component, View}
|
import Phoenix.{Component, View}
|
||||||
|
|
||||||
alias CanneryWeb.Endpoint
|
alias CanneryWeb.Endpoint
|
||||||
|
@ -5,6 +5,7 @@ defmodule CanneryWeb.Components.AddShotGroupComponent do
|
|||||||
|
|
||||||
use CanneryWeb, :live_component
|
use CanneryWeb, :live_component
|
||||||
alias Cannery.{Accounts.User, ActivityLog, ActivityLog.ShotGroup, Ammo.AmmoGroup}
|
alias Cannery.{Accounts.User, ActivityLog, ActivityLog.ShotGroup, Ammo.AmmoGroup}
|
||||||
|
alias Ecto.Changeset
|
||||||
alias Phoenix.LiveView.{JS, Socket}
|
alias Phoenix.LiveView.{JS, Socket}
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
@ -18,7 +19,7 @@ defmodule CanneryWeb.Components.AddShotGroupComponent do
|
|||||||
) :: {:ok, Socket.t()}
|
) :: {:ok, Socket.t()}
|
||||||
def update(%{ammo_group: ammo_group, current_user: current_user} = assigns, socket) do
|
def update(%{ammo_group: ammo_group, current_user: current_user} = assigns, socket) do
|
||||||
changeset =
|
changeset =
|
||||||
%ShotGroup{date: NaiveDateTime.utc_now(), count: 1}
|
%ShotGroup{date: Date.utc_today()}
|
||||||
|> ShotGroup.create_changeset(current_user, ammo_group, %{})
|
|> ShotGroup.create_changeset(current_user, ammo_group, %{})
|
||||||
|
|
||||||
{:ok, socket |> assign(assigns) |> assign(:changeset, changeset)}
|
{:ok, socket |> assign(assigns) |> assign(:changeset, changeset)}
|
||||||
@ -32,10 +33,13 @@ defmodule CanneryWeb.Components.AddShotGroupComponent do
|
|||||||
) do
|
) do
|
||||||
params = shot_group_params |> process_params(ammo_group)
|
params = shot_group_params |> process_params(ammo_group)
|
||||||
|
|
||||||
|
changeset = %ShotGroup{} |> ShotGroup.create_changeset(current_user, ammo_group, params)
|
||||||
|
|
||||||
changeset =
|
changeset =
|
||||||
%ShotGroup{}
|
case changeset |> Changeset.apply_action(:validate) do
|
||||||
|> ShotGroup.create_changeset(current_user, ammo_group, params)
|
{:ok, _data} -> changeset
|
||||||
|> Map.put(:action, :validate)
|
{:error, changeset} -> changeset
|
||||||
|
end
|
||||||
|
|
||||||
{:noreply, socket |> assign(:changeset, changeset)}
|
{:noreply, socket |> assign(:changeset, changeset)}
|
||||||
end
|
end
|
||||||
@ -56,7 +60,7 @@ defmodule CanneryWeb.Components.AddShotGroupComponent do
|
|||||||
prompt = dgettext("prompts", "Shots recorded successfully")
|
prompt = dgettext("prompts", "Shots recorded successfully")
|
||||||
socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
|
socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
|
||||||
|
|
||||||
{:error, %Ecto.Changeset{} = changeset} ->
|
{:error, %Changeset{} = changeset} ->
|
||||||
socket |> assign(changeset: changeset)
|
socket |> assign(changeset: changeset)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -65,14 +69,14 @@ defmodule CanneryWeb.Components.AddShotGroupComponent do
|
|||||||
|
|
||||||
# calculate count from shots left
|
# calculate count from shots left
|
||||||
defp process_params(params, %AmmoGroup{count: count}) do
|
defp process_params(params, %AmmoGroup{count: count}) do
|
||||||
new_count =
|
shot_group_count =
|
||||||
if params |> Map.get("ammo_left", "0") == "" do
|
if params |> Map.get("ammo_left", "") == "" do
|
||||||
"0"
|
nil
|
||||||
else
|
else
|
||||||
params |> Map.get("ammo_left", "0")
|
new_count = params |> Map.get("ammo_left") |> String.to_integer()
|
||||||
|
count - new_count
|
||||||
end
|
end
|
||||||
|> String.to_integer()
|
|
||||||
|
|
||||||
params |> Map.put("count", count - new_count)
|
params |> Map.put("count", shot_group_count)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -37,9 +37,11 @@
|
|||||||
|
|
||||||
<%= label(f, :notes, gettext("Notes"), class: "title text-lg text-primary-600") %>
|
<%= label(f, :notes, gettext("Notes"), class: "title text-lg text-primary-600") %>
|
||||||
<%= textarea(f, :notes,
|
<%= textarea(f, :notes,
|
||||||
|
id: "add-shot-group-form-notes",
|
||||||
class: "input input-primary col-span-2",
|
class: "input input-primary col-span-2",
|
||||||
placeholder: "Really great weather",
|
placeholder: gettext("Really great weather"),
|
||||||
phx_hook: "MaintainAttrs"
|
phx_hook: "MaintainAttrs",
|
||||||
|
phx_update: "ignore"
|
||||||
) %>
|
) %>
|
||||||
<%= error_tag(f, :notes, "col-span-3") %>
|
<%= error_tag(f, :notes, "col-span-3") %>
|
||||||
|
|
||||||
|
@ -1,103 +0,0 @@
|
|||||||
defmodule CanneryWeb.Components.AmmoGroupCard do
|
|
||||||
@moduledoc """
|
|
||||||
Display card for an ammo group
|
|
||||||
"""
|
|
||||||
|
|
||||||
use CanneryWeb, :component
|
|
||||||
alias Cannery.{Ammo, Ammo.AmmoGroup, Repo}
|
|
||||||
alias CanneryWeb.Endpoint
|
|
||||||
|
|
||||||
attr :ammo_group, AmmoGroup, required: true
|
|
||||||
attr :show_container, :boolean, default: false
|
|
||||||
slot(:inner_block)
|
|
||||||
|
|
||||||
def ammo_group_card(%{ammo_group: ammo_group} = assigns) do
|
|
||||||
assigns =
|
|
||||||
%{show_container: show_container} = assigns |> assign_new(:show_container, fn -> false end)
|
|
||||||
|
|
||||||
preloads = if show_container, do: [:ammo_type, :container], else: [:ammo_type]
|
|
||||||
ammo_group = ammo_group |> Repo.preload(preloads)
|
|
||||||
|
|
||||||
assigns = assigns |> assign(:ammo_group, ammo_group)
|
|
||||||
|
|
||||||
~H"""
|
|
||||||
<div
|
|
||||||
id={"ammo_group-#{@ammo_group.id}"}
|
|
||||||
class="mx-4 my-2 px-8 py-4
|
|
||||||
flex flex-col justify-center items-center
|
|
||||||
border border-gray-400 rounded-lg shadow-lg hover:shadow-md
|
|
||||||
transition-all duration-300 ease-in-out"
|
|
||||||
>
|
|
||||||
<.link navigate={Routes.ammo_group_show_path(Endpoint, :show, @ammo_group)} class="mb-2 link">
|
|
||||||
<h1 class="title text-xl title-primary-500">
|
|
||||||
<%= @ammo_group.ammo_type.name %>
|
|
||||||
</h1>
|
|
||||||
</.link>
|
|
||||||
|
|
||||||
<div class="flex flex-col justify-center items-center">
|
|
||||||
<span class="rounded-lg title text-lg">
|
|
||||||
<%= gettext("Count:") %>
|
|
||||||
<%= if @ammo_group.count == 0, do: gettext("Empty"), else: @ammo_group.count %>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span
|
|
||||||
:if={@ammo_group |> Ammo.get_original_count() != @ammo_group.count}
|
|
||||||
class="rounded-lg title text-lg"
|
|
||||||
>
|
|
||||||
<%= gettext("Original Count:") %>
|
|
||||||
<%= @ammo_group |> Ammo.get_original_count() %>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span :if={@ammo_group.notes} class="rounded-lg title text-lg">
|
|
||||||
<%= gettext("Notes:") %>
|
|
||||||
<%= @ammo_group.notes %>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span class="rounded-lg title text-lg">
|
|
||||||
<%= gettext("Purchased on:") %>
|
|
||||||
<.date date={@ammo_group.purchased_on} />
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span :if={@ammo_group |> Ammo.get_last_used_shot_group()} class="rounded-lg title text-lg">
|
|
||||||
<%= gettext("Last used on:") %>
|
|
||||||
<.date date={@ammo_group |> Ammo.get_last_used_shot_group() |> Map.get(:date)} />
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<%= if @ammo_group.price_paid do %>
|
|
||||||
<span class="rounded-lg title text-lg">
|
|
||||||
<%= gettext("Price paid:") %>
|
|
||||||
<%= gettext("$%{amount}",
|
|
||||||
amount: @ammo_group.price_paid |> :erlang.float_to_binary(decimals: 2)
|
|
||||||
) %>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span class="rounded-lg title text-lg">
|
|
||||||
<%= gettext("CPR:") %>
|
|
||||||
<%= gettext("$%{amount}",
|
|
||||||
amount: @ammo_group |> Ammo.get_cpr() |> :erlang.float_to_binary(decimals: 2)
|
|
||||||
) %>
|
|
||||||
</span>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<span :if={@show_container and @ammo_group.container} class="rounded-lg title text-lg">
|
|
||||||
<%= gettext("Container:") %>
|
|
||||||
|
|
||||||
<.link
|
|
||||||
navigate={Routes.container_show_path(Endpoint, :show, @ammo_group.container)}
|
|
||||||
class="link"
|
|
||||||
>
|
|
||||||
<%= @ammo_group.container.name %>
|
|
||||||
</.link>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
:if={assigns |> Map.has_key?(:inner_block)}
|
|
||||||
class="mt-4 flex space-x-4 justify-center items-center"
|
|
||||||
>
|
|
||||||
<%= render_slot(@inner_block) %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
end
|
|
@ -3,7 +3,8 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do
|
|||||||
A component that displays a list of ammo groups
|
A component that displays a list of ammo groups
|
||||||
"""
|
"""
|
||||||
use CanneryWeb, :live_component
|
use CanneryWeb, :live_component
|
||||||
alias Cannery.{Accounts.User, Ammo, Ammo.AmmoGroup, Repo}
|
alias Cannery.{Accounts.User, Ammo.AmmoGroup, ComparableDate}
|
||||||
|
alias Cannery.{ActivityLog, Ammo, Containers}
|
||||||
alias Ecto.UUID
|
alias Ecto.UUID
|
||||||
alias Phoenix.LiveView.{Rendered, Socket}
|
alias Phoenix.LiveView.{Rendered, Socket}
|
||||||
|
|
||||||
@ -50,12 +51,12 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do
|
|||||||
if actions == [] do
|
if actions == [] do
|
||||||
[]
|
[]
|
||||||
else
|
else
|
||||||
[%{label: nil, key: :actions, sortable: false}]
|
[%{label: gettext("Actions"), key: :actions, sortable: false}]
|
||||||
end
|
end
|
||||||
|
|
||||||
columns = [
|
columns = [
|
||||||
%{label: gettext("Purchased on"), key: :purchased_on},
|
%{label: gettext("Purchased on"), key: :purchased_on, type: ComparableDate},
|
||||||
%{label: gettext("Last used on"), key: :used_up_on} | columns
|
%{label: gettext("Last used on"), key: :used_up_on, type: ComparableDate} | columns
|
||||||
]
|
]
|
||||||
|
|
||||||
columns =
|
columns =
|
||||||
@ -94,13 +95,15 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do
|
|||||||
ammo_type: ammo_type,
|
ammo_type: ammo_type,
|
||||||
columns: columns,
|
columns: columns,
|
||||||
container: container,
|
container: container,
|
||||||
|
original_counts: Ammo.get_original_counts(ammo_groups, current_user),
|
||||||
|
cprs: Ammo.get_cprs(ammo_groups, current_user),
|
||||||
|
last_used_dates: ActivityLog.get_last_used_dates(ammo_groups, current_user),
|
||||||
actions: actions,
|
actions: actions,
|
||||||
range: range
|
range: range
|
||||||
}
|
}
|
||||||
|
|
||||||
rows =
|
rows =
|
||||||
ammo_groups
|
ammo_groups
|
||||||
|> Repo.preload([:ammo_type, :container])
|
|
||||||
|> Enum.map(fn ammo_group ->
|
|> Enum.map(fn ammo_group ->
|
||||||
ammo_group |> get_row_data_for_ammo_group(extra_data)
|
ammo_group |> get_row_data_for_ammo_group(extra_data)
|
||||||
end)
|
end)
|
||||||
@ -124,8 +127,6 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do
|
|||||||
|
|
||||||
@spec get_row_data_for_ammo_group(AmmoGroup.t(), additional_data :: map()) :: map()
|
@spec get_row_data_for_ammo_group(AmmoGroup.t(), additional_data :: map()) :: map()
|
||||||
defp get_row_data_for_ammo_group(ammo_group, %{columns: columns} = additional_data) do
|
defp get_row_data_for_ammo_group(ammo_group, %{columns: columns} = additional_data) do
|
||||||
ammo_group = ammo_group |> Repo.preload([:ammo_type, :container])
|
|
||||||
|
|
||||||
columns
|
columns
|
||||||
|> Map.new(fn %{key: key} ->
|
|> Map.new(fn %{key: key} ->
|
||||||
{key, get_value_for_key(key, ammo_group, additional_data)}
|
{key, get_value_for_key(key, ammo_group, additional_data)}
|
||||||
@ -150,30 +151,23 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do
|
|||||||
defp get_value_for_key(:price_paid, %{price_paid: nil}, _additional_data), do: {"", nil}
|
defp get_value_for_key(:price_paid, %{price_paid: nil}, _additional_data), do: {"", nil}
|
||||||
|
|
||||||
defp get_value_for_key(:price_paid, %{price_paid: price_paid}, _additional_data),
|
defp get_value_for_key(:price_paid, %{price_paid: price_paid}, _additional_data),
|
||||||
do: gettext("$%{amount}", amount: price_paid |> :erlang.float_to_binary(decimals: 2))
|
do: gettext("$%{amount}", amount: display_currency(price_paid))
|
||||||
|
|
||||||
defp get_value_for_key(:purchased_on, %{purchased_on: purchased_on}, _additional_data) do
|
|
||||||
assigns = %{purchased_on: purchased_on}
|
|
||||||
|
|
||||||
|
defp get_value_for_key(:purchased_on, %{purchased_on: purchased_on} = assigns, _additional_data) do
|
||||||
{purchased_on,
|
{purchased_on,
|
||||||
~H"""
|
~H"""
|
||||||
<.date date={@purchased_on} />
|
<.date id={"#{@id}-purchased-on"} date={@purchased_on} />
|
||||||
"""}
|
"""}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_value_for_key(:used_up_on, ammo_group, _additional_data) do
|
defp get_value_for_key(:used_up_on, %{id: ammo_group_id}, %{last_used_dates: last_used_dates}) do
|
||||||
last_shot_group_date =
|
last_used_date = last_used_dates |> Map.get(ammo_group_id)
|
||||||
case ammo_group |> Ammo.get_last_used_shot_group() do
|
assigns = %{id: ammo_group_id, last_used_date: last_used_date}
|
||||||
%{date: last_shot_group_date} -> last_shot_group_date
|
|
||||||
_no_shot_groups -> nil
|
|
||||||
end
|
|
||||||
|
|
||||||
assigns = %{last_shot_group_date: last_shot_group_date}
|
{last_used_date,
|
||||||
|
|
||||||
{last_shot_group_date,
|
|
||||||
~H"""
|
~H"""
|
||||||
<%= if @last_shot_group_date do %>
|
<%= if @last_used_date do %>
|
||||||
<.date date={@last_shot_group_date} />
|
<.date id={"#{@id}-last-used-date"} date={@last_used_date} />
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= gettext("Never used") %>
|
<%= gettext("Never used") %>
|
||||||
<% end %>
|
<% end %>
|
||||||
@ -189,8 +183,11 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do
|
|||||||
"""}
|
"""}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_value_for_key(:remaining, ammo_group, _additional_data),
|
defp get_value_for_key(:remaining, ammo_group, %{current_user: current_user}),
|
||||||
do: gettext("%{percentage}%", percentage: ammo_group |> Ammo.get_percentage_remaining())
|
do:
|
||||||
|
gettext("%{percentage}%",
|
||||||
|
percentage: ammo_group |> Ammo.get_percentage_remaining(current_user)
|
||||||
|
)
|
||||||
|
|
||||||
defp get_value_for_key(:actions, ammo_group, %{actions: actions}) do
|
defp get_value_for_key(:actions, ammo_group, %{actions: actions}) do
|
||||||
assigns = %{actions: actions, ammo_group: ammo_group}
|
assigns = %{actions: actions, ammo_group: ammo_group}
|
||||||
@ -204,31 +201,40 @@ defmodule CanneryWeb.Components.AmmoGroupTableComponent do
|
|||||||
|
|
||||||
defp get_value_for_key(
|
defp get_value_for_key(
|
||||||
:container,
|
:container,
|
||||||
%{container: %{name: container_name}} = ammo_group,
|
%{container_id: container_id} = ammo_group,
|
||||||
%{container: container}
|
%{container: container, current_user: current_user}
|
||||||
) do
|
) do
|
||||||
assigns = %{container: container, ammo_group: ammo_group}
|
assigns = %{
|
||||||
|
container:
|
||||||
|
%{name: container_name} = container_id |> Containers.get_container!(current_user),
|
||||||
|
container_block: container,
|
||||||
|
ammo_group: ammo_group
|
||||||
|
}
|
||||||
|
|
||||||
{container_name,
|
{container_name,
|
||||||
~H"""
|
~H"""
|
||||||
<%= render_slot(@container, @ammo_group) %>
|
<%= render_slot(@container_block, {@ammo_group, @container}) %>
|
||||||
"""}
|
"""}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_value_for_key(:original_count, ammo_group, _additional_data),
|
defp get_value_for_key(:original_count, %{id: ammo_group_id}, %{
|
||||||
do: ammo_group |> Ammo.get_original_count()
|
original_counts: original_counts
|
||||||
|
}) do
|
||||||
|
Map.fetch!(original_counts, ammo_group_id)
|
||||||
|
end
|
||||||
|
|
||||||
defp get_value_for_key(:cpr, %{price_paid: nil}, _additional_data),
|
defp get_value_for_key(:cpr, %{price_paid: nil}, _additional_data),
|
||||||
do: gettext("No cost information")
|
do: gettext("No cost information")
|
||||||
|
|
||||||
defp get_value_for_key(:cpr, ammo_group, _additional_data) do
|
defp get_value_for_key(:cpr, %{id: ammo_group_id}, %{cprs: cprs}) do
|
||||||
gettext("$%{amount}",
|
gettext("$%{amount}", amount: display_currency(Map.fetch!(cprs, ammo_group_id)))
|
||||||
amount: ammo_group |> Ammo.get_cpr() |> :erlang.float_to_binary(decimals: 2)
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_value_for_key(:count, %{count: count}, _additional_data),
|
defp get_value_for_key(:count, %{count: count}, _additional_data),
|
||||||
do: if(count == 0, do: gettext("Empty"), else: count)
|
do: if(count == 0, do: gettext("Empty"), else: count)
|
||||||
|
|
||||||
defp get_value_for_key(key, ammo_group, _additional_data), do: ammo_group |> Map.get(key)
|
defp get_value_for_key(key, ammo_group, _additional_data), do: ammo_group |> Map.get(key)
|
||||||
|
|
||||||
|
@spec display_currency(float()) :: String.t()
|
||||||
|
defp display_currency(float), do: :erlang.float_to_binary(float, decimals: 2)
|
||||||
end
|
end
|
||||||
|
@ -3,7 +3,7 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do
|
|||||||
A component that displays a list of ammo type
|
A component that displays a list of ammo type
|
||||||
"""
|
"""
|
||||||
use CanneryWeb, :live_component
|
use CanneryWeb, :live_component
|
||||||
alias Cannery.{Accounts.User, Ammo, Ammo.AmmoType}
|
alias Cannery.{Accounts.User, ActivityLog, Ammo, Ammo.AmmoType}
|
||||||
alias Ecto.UUID
|
alias Ecto.UUID
|
||||||
alias Phoenix.LiveView.{Rendered, Socket}
|
alias Phoenix.LiveView.{Rendered, Socket}
|
||||||
|
|
||||||
@ -103,13 +103,13 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do
|
|||||||
[
|
[
|
||||||
%{
|
%{
|
||||||
label: gettext("Used packs"),
|
label: gettext("Used packs"),
|
||||||
key: :used_ammo_count,
|
key: :used_packs_count,
|
||||||
type: :used_ammo_count
|
type: :used_packs_count
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
label: gettext("Total ever packs"),
|
label: gettext("Total ever packs"),
|
||||||
key: :historical_ammo_count,
|
key: :historical_packs_count,
|
||||||
type: :historical_ammo_count
|
type: :historical_packs_count
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
else
|
else
|
||||||
@ -118,10 +118,38 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do
|
|||||||
)
|
)
|
||||||
|> Kernel.++([
|
|> Kernel.++([
|
||||||
%{label: gettext("Average CPR"), key: :avg_price_paid, type: :avg_price_paid},
|
%{label: gettext("Average CPR"), key: :avg_price_paid, type: :avg_price_paid},
|
||||||
%{label: nil, key: "actions", type: :actions, sortable: false}
|
%{label: gettext("Actions"), key: "actions", type: :actions, sortable: false}
|
||||||
])
|
])
|
||||||
|
|
||||||
extra_data = %{actions: actions, current_user: current_user}
|
round_counts = ammo_types |> Ammo.get_round_count_for_ammo_types(current_user)
|
||||||
|
|
||||||
|
used_counts =
|
||||||
|
show_used && ammo_types |> ActivityLog.get_used_count_for_ammo_types(current_user)
|
||||||
|
|
||||||
|
historical_round_counts =
|
||||||
|
show_used && ammo_types |> Ammo.get_historical_count_for_ammo_types(current_user)
|
||||||
|
|
||||||
|
packs_count = ammo_types |> Ammo.get_ammo_groups_count_for_types(current_user)
|
||||||
|
|
||||||
|
historical_packs_count =
|
||||||
|
show_used && ammo_types |> Ammo.get_ammo_groups_count_for_types(current_user, true)
|
||||||
|
|
||||||
|
used_packs_count =
|
||||||
|
show_used && ammo_types |> Ammo.get_used_ammo_groups_count_for_types(current_user)
|
||||||
|
|
||||||
|
average_costs = ammo_types |> Ammo.get_average_cost_for_ammo_types(current_user)
|
||||||
|
|
||||||
|
extra_data = %{
|
||||||
|
actions: actions,
|
||||||
|
current_user: current_user,
|
||||||
|
used_counts: used_counts,
|
||||||
|
round_counts: round_counts,
|
||||||
|
historical_round_counts: historical_round_counts,
|
||||||
|
packs_count: packs_count,
|
||||||
|
used_packs_count: used_packs_count,
|
||||||
|
historical_packs_count: historical_packs_count,
|
||||||
|
average_costs: average_costs
|
||||||
|
}
|
||||||
|
|
||||||
rows =
|
rows =
|
||||||
ammo_types
|
ammo_types
|
||||||
@ -156,28 +184,44 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do
|
|||||||
defp get_ammo_type_value(:boolean, key, ammo_type, _other_data),
|
defp get_ammo_type_value(:boolean, key, ammo_type, _other_data),
|
||||||
do: ammo_type |> Map.get(key) |> humanize()
|
do: ammo_type |> Map.get(key) |> humanize()
|
||||||
|
|
||||||
defp get_ammo_type_value(:round_count, _key, ammo_type, %{current_user: current_user}),
|
defp get_ammo_type_value(:round_count, _key, %{id: ammo_type_id}, %{round_counts: round_counts}),
|
||||||
do: ammo_type |> Ammo.get_round_count_for_ammo_type(current_user)
|
do: Map.get(round_counts, ammo_type_id)
|
||||||
|
|
||||||
defp get_ammo_type_value(:historical_round_count, _key, ammo_type, %{current_user: current_user}),
|
defp get_ammo_type_value(
|
||||||
do: ammo_type |> Ammo.get_historical_count_for_ammo_type(current_user)
|
:historical_round_count,
|
||||||
|
_key,
|
||||||
|
%{id: ammo_type_id},
|
||||||
|
%{historical_round_counts: historical_round_counts}
|
||||||
|
),
|
||||||
|
do: Map.get(historical_round_counts, ammo_type_id)
|
||||||
|
|
||||||
defp get_ammo_type_value(:used_round_count, _key, ammo_type, %{current_user: current_user}),
|
defp get_ammo_type_value(:used_round_count, _key, %{id: ammo_type_id}, %{
|
||||||
do: ammo_type |> Ammo.get_used_count_for_ammo_type(current_user)
|
used_counts: used_counts
|
||||||
|
}),
|
||||||
|
do: Map.get(used_counts, ammo_type_id)
|
||||||
|
|
||||||
defp get_ammo_type_value(:historical_ammo_count, _key, ammo_type, %{current_user: current_user}),
|
defp get_ammo_type_value(
|
||||||
do: ammo_type |> Ammo.get_ammo_groups_count_for_type(current_user, true)
|
:historical_packs_count,
|
||||||
|
_key,
|
||||||
|
%{id: ammo_type_id},
|
||||||
|
%{historical_packs_count: historical_packs_count}
|
||||||
|
),
|
||||||
|
do: Map.get(historical_packs_count, ammo_type_id)
|
||||||
|
|
||||||
defp get_ammo_type_value(:used_ammo_count, _key, ammo_type, %{current_user: current_user}),
|
defp get_ammo_type_value(:used_packs_count, _key, %{id: ammo_type_id}, %{
|
||||||
do: ammo_type |> Ammo.get_used_ammo_groups_count_for_type(current_user)
|
used_packs_count: used_packs_count
|
||||||
|
}),
|
||||||
|
do: Map.get(used_packs_count, ammo_type_id)
|
||||||
|
|
||||||
defp get_ammo_type_value(:ammo_count, _key, ammo_type, %{current_user: current_user}),
|
defp get_ammo_type_value(:ammo_count, _key, %{id: ammo_type_id}, %{packs_count: packs_count}),
|
||||||
do: ammo_type |> Ammo.get_ammo_groups_count_for_type(current_user)
|
do: Map.get(packs_count, ammo_type_id)
|
||||||
|
|
||||||
defp get_ammo_type_value(:avg_price_paid, _key, ammo_type, %{current_user: current_user}) do
|
defp get_ammo_type_value(:avg_price_paid, _key, %{id: ammo_type_id}, %{
|
||||||
case ammo_type |> Ammo.get_average_cost_for_ammo_type!(current_user) do
|
average_costs: average_costs
|
||||||
|
}) do
|
||||||
|
case Map.get(average_costs, ammo_type_id) do
|
||||||
nil -> gettext("No cost information")
|
nil -> gettext("No cost information")
|
||||||
count -> gettext("$%{amount}", amount: count |> :erlang.float_to_binary(decimals: 2))
|
count -> gettext("$%{amount}", amount: display_currency(count))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -185,11 +229,7 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do
|
|||||||
assigns = %{ammo_type: ammo_type}
|
assigns = %{ammo_type: ammo_type}
|
||||||
|
|
||||||
~H"""
|
~H"""
|
||||||
<.link
|
<.link navigate={Routes.ammo_type_show_path(Endpoint, :show, @ammo_type)} class="link">
|
||||||
navigate={Routes.ammo_type_show_path(Endpoint, :show, @ammo_type)}
|
|
||||||
class="link"
|
|
||||||
data-qa={"view-name-#{@ammo_type.id}"}
|
|
||||||
>
|
|
||||||
<%= @ammo_type.name %>
|
<%= @ammo_type.name %>
|
||||||
</.link>
|
</.link>
|
||||||
"""
|
"""
|
||||||
@ -206,4 +246,7 @@ defmodule CanneryWeb.Components.AmmoTypeTableComponent do
|
|||||||
defp get_ammo_type_value(nil, _key, _ammo_type, _other_data), do: nil
|
defp get_ammo_type_value(nil, _key, _ammo_type, _other_data), do: nil
|
||||||
|
|
||||||
defp get_ammo_type_value(_other, key, ammo_type, _other_data), do: ammo_type |> Map.get(key)
|
defp get_ammo_type_value(_other, key, ammo_type, _other_data), do: ammo_type |> Map.get(key)
|
||||||
|
|
||||||
|
@spec display_currency(float()) :: String.t()
|
||||||
|
defp display_currency(float), do: :erlang.float_to_binary(float, decimals: 2)
|
||||||
end
|
end
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
defmodule CanneryWeb.Components.ContainerCard do
|
|
||||||
@moduledoc """
|
|
||||||
Display card for a container
|
|
||||||
"""
|
|
||||||
|
|
||||||
use CanneryWeb, :component
|
|
||||||
import CanneryWeb.Components.TagCard
|
|
||||||
alias Cannery.{Containers, Containers.Container, Repo}
|
|
||||||
alias CanneryWeb.Endpoint
|
|
||||||
alias Phoenix.LiveView.Rendered
|
|
||||||
|
|
||||||
attr :container, Container, required: true
|
|
||||||
slot(:tag_actions)
|
|
||||||
slot(:inner_block)
|
|
||||||
|
|
||||||
@spec container_card(assigns :: map()) :: Rendered.t()
|
|
||||||
def container_card(%{container: container} = assigns) do
|
|
||||||
assigns =
|
|
||||||
assigns
|
|
||||||
|> assign(container: container |> Repo.preload([:tags, :ammo_groups]))
|
|
||||||
|> assign_new(:tag_actions, fn -> [] end)
|
|
||||||
|
|
||||||
~H"""
|
|
||||||
<div
|
|
||||||
id={"container-#{@container.id}"}
|
|
||||||
class="overflow-hidden max-w-full mx-4 mb-4 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
|
|
||||||
transition-all duration-300 ease-in-out"
|
|
||||||
>
|
|
||||||
<div class="max-w-full mb-4 flex flex-col justify-center items-center space-y-2">
|
|
||||||
<.link navigate={Routes.container_show_path(Endpoint, :show, @container)} class="link">
|
|
||||||
<h1 class="px-4 py-2 rounded-lg title text-xl">
|
|
||||||
<%= @container.name %>
|
|
||||||
</h1>
|
|
||||||
</.link>
|
|
||||||
|
|
||||||
<span :if={@container.desc} class="rounded-lg title text-lg">
|
|
||||||
<%= gettext("Description:") %>
|
|
||||||
<%= @container.desc %>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span class="rounded-lg title text-lg">
|
|
||||||
<%= gettext("Type:") %>
|
|
||||||
<%= @container.type %>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span :if={@container.location} class="rounded-lg title text-lg">
|
|
||||||
<%= gettext("Location:") %>
|
|
||||||
<%= @container.location %>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<%= unless @container.ammo_groups |> Enum.empty?() do %>
|
|
||||||
<span class="rounded-lg title text-lg">
|
|
||||||
<%= gettext("Packs:") %>
|
|
||||||
<%= @container |> Containers.get_container_ammo_group_count!() %>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<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">
|
|
||||||
<.simple_tag_card :for={tag <- @container.tags} :if={@container.tags} tag={tag} />
|
|
||||||
|
|
||||||
<%= render_slot(@tag_actions) %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
:if={assigns |> Map.has_key?(:inner_block)}
|
|
||||||
class="flex space-x-4 justify-center items-center"
|
|
||||||
>
|
|
||||||
<%= render_slot(@inner_block) %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
end
|
|
@ -3,8 +3,7 @@ defmodule CanneryWeb.Components.ContainerTableComponent do
|
|||||||
A component that displays a list of containers
|
A component that displays a list of containers
|
||||||
"""
|
"""
|
||||||
use CanneryWeb, :live_component
|
use CanneryWeb, :live_component
|
||||||
alias Cannery.{Accounts.User, Containers, Containers.Container, Repo}
|
alias Cannery.{Accounts.User, Ammo, Containers.Container}
|
||||||
alias CanneryWeb.Components.TagCard
|
|
||||||
alias Ecto.UUID
|
alias Ecto.UUID
|
||||||
alias Phoenix.LiveView.{Rendered, Socket}
|
alias Phoenix.LiveView.{Rendered, Socket}
|
||||||
|
|
||||||
@ -46,11 +45,7 @@ defmodule CanneryWeb.Components.ContainerTableComponent do
|
|||||||
%{label: gettext("Name"), key: :name, type: :string},
|
%{label: gettext("Name"), key: :name, type: :string},
|
||||||
%{label: gettext("Description"), key: :desc, type: :string},
|
%{label: gettext("Description"), key: :desc, type: :string},
|
||||||
%{label: gettext("Location"), key: :location, type: :string},
|
%{label: gettext("Location"), key: :location, type: :string},
|
||||||
%{label: gettext("Type"), key: :type, type: :string},
|
%{label: gettext("Type"), key: :type, type: :string}
|
||||||
%{label: gettext("Packs"), key: :packs, type: :integer},
|
|
||||||
%{label: gettext("Rounds"), key: :rounds, type: :string},
|
|
||||||
%{label: gettext("Tags"), key: :tags, type: :tags},
|
|
||||||
%{label: nil, key: :actions, sortable: false, type: :actions}
|
|
||||||
]
|
]
|
||||||
|> Enum.filter(fn %{key: key, type: type} ->
|
|> Enum.filter(fn %{key: key, type: type} ->
|
||||||
# remove columns if all values match defaults
|
# remove columns if all values match defaults
|
||||||
@ -65,11 +60,19 @@ defmodule CanneryWeb.Components.ContainerTableComponent do
|
|||||||
type in [:tags, :actions] or not (container |> Map.get(key) == default_value)
|
type in [:tags, :actions] or not (container |> Map.get(key) == default_value)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|> Enum.concat([
|
||||||
|
%{label: gettext("Packs"), key: :packs, type: :integer},
|
||||||
|
%{label: gettext("Rounds"), key: :rounds, type: :integer},
|
||||||
|
%{label: gettext("Tags"), key: :tags, type: :tags},
|
||||||
|
%{label: gettext("Actions"), key: :actions, sortable: false, type: :actions}
|
||||||
|
])
|
||||||
|
|
||||||
extra_data = %{
|
extra_data = %{
|
||||||
current_user: current_user,
|
current_user: current_user,
|
||||||
tag_actions: tag_actions,
|
tag_actions: tag_actions,
|
||||||
actions: actions
|
actions: actions,
|
||||||
|
pack_count: Ammo.get_ammo_groups_count_for_containers(containers, current_user),
|
||||||
|
round_count: Ammo.get_round_count_for_containers(containers, current_user)
|
||||||
}
|
}
|
||||||
|
|
||||||
rows =
|
rows =
|
||||||
@ -101,8 +104,6 @@ defmodule CanneryWeb.Components.ContainerTableComponent do
|
|||||||
|
|
||||||
@spec get_row_data_for_container(Container.t(), columns :: [map()], extra_data :: map) :: map()
|
@spec get_row_data_for_container(Container.t(), columns :: [map()], extra_data :: map) :: map()
|
||||||
defp get_row_data_for_container(container, columns, extra_data) do
|
defp get_row_data_for_container(container, columns, extra_data) do
|
||||||
container = container |> Repo.preload([:ammo_groups, :tags])
|
|
||||||
|
|
||||||
columns
|
columns
|
||||||
|> Map.new(fn %{key: key} -> {key, get_value_for_key(key, container, extra_data)} end)
|
|> Map.new(fn %{key: key} -> {key, get_value_for_key(key, container, extra_data)} end)
|
||||||
end
|
end
|
||||||
@ -121,21 +122,27 @@ defmodule CanneryWeb.Components.ContainerTableComponent do
|
|||||||
"""}
|
"""}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_value_for_key(:packs, container, _extra_data) do
|
defp get_value_for_key(:packs, %{id: container_id}, %{pack_count: pack_count}) do
|
||||||
container |> Containers.get_container_ammo_group_count!()
|
pack_count |> Map.get(container_id, 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_value_for_key(:rounds, container, _extra_data) do
|
defp get_value_for_key(:rounds, %{id: container_id}, %{round_count: round_count}) do
|
||||||
container |> Containers.get_container_rounds!()
|
round_count |> Map.get(container_id, 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_value_for_key(:tags, container, %{tag_actions: tag_actions}) do
|
defp get_value_for_key(:tags, container, %{tag_actions: tag_actions}) do
|
||||||
assigns = %{tag_actions: tag_actions, container: container}
|
assigns = %{tag_actions: tag_actions, container: container}
|
||||||
|
|
||||||
{container.tags |> Enum.map(fn %{name: name} -> name end),
|
tag_names =
|
||||||
|
container.tags
|
||||||
|
|> Enum.map(fn %{name: name} -> name end)
|
||||||
|
|> Enum.sort()
|
||||||
|
|> Enum.join(" ")
|
||||||
|
|
||||||
|
{tag_names,
|
||||||
~H"""
|
~H"""
|
||||||
<div class="flex flex-wrap justify-center items-center">
|
<div class="flex flex-wrap justify-center items-center">
|
||||||
<TagCard.simple_tag_card :for={tag <- @container.tags} :if={@container.tags} tag={tag} />
|
<.simple_tag_card :for={tag <- @container.tags} :if={@container.tags} tag={tag} />
|
||||||
|
|
||||||
<%= render_slot(@tag_actions, @container) %>
|
<%= render_slot(@tag_actions, @container) %>
|
||||||
</div>
|
</div>
|
||||||
|
149
lib/cannery_web/components/core_components.ex
Normal file
149
lib/cannery_web/components/core_components.ex
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
defmodule CanneryWeb.CoreComponents do
|
||||||
|
@moduledoc """
|
||||||
|
Provides core UI components.
|
||||||
|
"""
|
||||||
|
use Phoenix.Component
|
||||||
|
import CanneryWeb.{Gettext, ViewHelpers}
|
||||||
|
alias Cannery.{Accounts, Accounts.Invite, Accounts.User}
|
||||||
|
alias Cannery.{Ammo, Ammo.AmmoGroup}
|
||||||
|
alias Cannery.{Containers, Containers.Container, Containers.Tag}
|
||||||
|
alias CanneryWeb.{Endpoint, HomeLive}
|
||||||
|
alias CanneryWeb.Router.Helpers, as: Routes
|
||||||
|
alias Phoenix.LiveView.{JS, Rendered}
|
||||||
|
|
||||||
|
embed_templates "core_components/*"
|
||||||
|
|
||||||
|
attr :title_content, :string, default: nil
|
||||||
|
attr :current_user, User, default: nil
|
||||||
|
|
||||||
|
def topbar(assigns)
|
||||||
|
|
||||||
|
attr :return_to, :string, required: true
|
||||||
|
slot(:inner_block)
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Renders a live component inside a modal.
|
||||||
|
|
||||||
|
The rendered modal receives a `:return_to` option to properly update
|
||||||
|
the URL when the modal is closed.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
<.modal return_to={Routes.<%= schema.singular %>_index_path(Endpoint, :index)}>
|
||||||
|
<.live_component
|
||||||
|
module={<%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Live.FormComponent}
|
||||||
|
id={@<%= schema.singular %>.id || :new}
|
||||||
|
title={@page_title}
|
||||||
|
action={@live_action}
|
||||||
|
return_to={Routes.<%= schema.singular %>_index_path(Endpoint, :index)}
|
||||||
|
<%= schema.singular %>: @<%= schema.singular %>
|
||||||
|
/>
|
||||||
|
</.modal>
|
||||||
|
"""
|
||||||
|
def modal(assigns)
|
||||||
|
|
||||||
|
defp hide_modal(js \\ %JS{}) do
|
||||||
|
js
|
||||||
|
|> JS.hide(to: "#modal", transition: "fade-out")
|
||||||
|
|> JS.hide(to: "#modal-bg", transition: "fade-out")
|
||||||
|
|> JS.hide(to: "#modal-content", transition: "fade-out-scale")
|
||||||
|
end
|
||||||
|
|
||||||
|
attr :action, :string, required: true
|
||||||
|
attr :value, :boolean, required: true
|
||||||
|
attr :id, :string, default: nil
|
||||||
|
slot(:inner_block)
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
A toggle button element that can be directed to a liveview or a
|
||||||
|
live_component's `handle_event/3`.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
<.toggle_button action="my_liveview_action" value={@some_value}>
|
||||||
|
<span>Toggle me!</span>
|
||||||
|
</.toggle_button>
|
||||||
|
<.toggle_button action="my_live_component_action" target={@myself} value={@some_value}>
|
||||||
|
<span>Whatever you want</span>
|
||||||
|
</.toggle_button>
|
||||||
|
"""
|
||||||
|
def toggle_button(assigns)
|
||||||
|
|
||||||
|
attr :container, Container, required: true
|
||||||
|
attr :current_user, User, required: true
|
||||||
|
slot(:tag_actions)
|
||||||
|
slot(:inner_block)
|
||||||
|
|
||||||
|
@spec container_card(assigns :: map()) :: Rendered.t()
|
||||||
|
def container_card(assigns)
|
||||||
|
|
||||||
|
attr :tag, Tag, required: true
|
||||||
|
slot(:inner_block, required: true)
|
||||||
|
|
||||||
|
def tag_card(assigns)
|
||||||
|
|
||||||
|
attr :tag, Tag, required: true
|
||||||
|
|
||||||
|
def simple_tag_card(assigns)
|
||||||
|
|
||||||
|
attr :ammo_group, AmmoGroup, required: true
|
||||||
|
attr :current_user, User, required: true
|
||||||
|
attr :original_count, :integer, default: nil
|
||||||
|
attr :cpr, :integer, default: nil
|
||||||
|
attr :last_used_date, Date, default: nil
|
||||||
|
attr :show_container, :boolean, default: false
|
||||||
|
slot(:inner_block)
|
||||||
|
|
||||||
|
def ammo_group_card(assigns)
|
||||||
|
|
||||||
|
@spec display_currency(float()) :: String.t()
|
||||||
|
defp display_currency(float), do: :erlang.float_to_binary(float, decimals: 2)
|
||||||
|
|
||||||
|
attr :user, User, required: true
|
||||||
|
slot(:inner_block, required: true)
|
||||||
|
|
||||||
|
def user_card(assigns)
|
||||||
|
|
||||||
|
attr :invite, Invite, required: true
|
||||||
|
attr :use_count, :integer, default: nil
|
||||||
|
attr :current_user, User, required: true
|
||||||
|
slot(:inner_block)
|
||||||
|
slot(:code_actions)
|
||||||
|
|
||||||
|
def invite_card(assigns)
|
||||||
|
|
||||||
|
attr :content, :string, required: true
|
||||||
|
attr :filename, :string, default: "qrcode", doc: "filename without .png extension"
|
||||||
|
attr :image_class, :string, default: "w-64 h-max"
|
||||||
|
attr :width, :integer, default: 384, doc: "width of png to generate"
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Creates a downloadable QR Code element
|
||||||
|
"""
|
||||||
|
def qr_code(assigns)
|
||||||
|
|
||||||
|
attr :id, :string, required: true
|
||||||
|
attr :date, :any, required: true, doc: "A `Date` struct or nil"
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Phoenix.Component for a <date> element that renders the Date in the user's
|
||||||
|
local timezone
|
||||||
|
"""
|
||||||
|
def date(assigns)
|
||||||
|
|
||||||
|
attr :id, :string, required: true
|
||||||
|
attr :datetime, :any, required: true, doc: "A `DateTime` struct or nil"
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Phoenix.Component for a <time> element that renders the naivedatetime in the
|
||||||
|
user's local timezone
|
||||||
|
"""
|
||||||
|
def datetime(assigns)
|
||||||
|
|
||||||
|
@spec cast_datetime(NaiveDateTime.t() | nil) :: String.t()
|
||||||
|
defp cast_datetime(%NaiveDateTime{} = datetime) do
|
||||||
|
datetime |> DateTime.from_naive!("Etc/UTC") |> DateTime.to_iso8601(:extended)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp cast_datetime(_datetime), do: ""
|
||||||
|
end
|
@ -0,0 +1,71 @@
|
|||||||
|
<div
|
||||||
|
id={"ammo_group-#{@ammo_group.id}"}
|
||||||
|
class="mx-4 my-2 px-8 py-4
|
||||||
|
flex flex-col justify-center items-center
|
||||||
|
border border-gray-400 rounded-lg shadow-lg hover:shadow-md
|
||||||
|
transition-all duration-300 ease-in-out"
|
||||||
|
>
|
||||||
|
<.link navigate={Routes.ammo_group_show_path(Endpoint, :show, @ammo_group)} class="mb-2 link">
|
||||||
|
<h1 class="title text-xl title-primary-500">
|
||||||
|
<%= @ammo_group.ammo_type.name %>
|
||||||
|
</h1>
|
||||||
|
</.link>
|
||||||
|
|
||||||
|
<div class="flex flex-col justify-center items-center">
|
||||||
|
<span class="rounded-lg title text-lg">
|
||||||
|
<%= gettext("Count:") %>
|
||||||
|
<%= if @ammo_group.count == 0, do: gettext("Empty"), else: @ammo_group.count %>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span
|
||||||
|
:if={@original_count && @original_count != @ammo_group.count}
|
||||||
|
class="rounded-lg title text-lg"
|
||||||
|
>
|
||||||
|
<%= gettext("Original Count:") %>
|
||||||
|
<%= @original_count %>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span :if={@ammo_group.notes} class="rounded-lg title text-lg">
|
||||||
|
<%= gettext("Notes:") %>
|
||||||
|
<%= @ammo_group.notes %>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span :if={@ammo_group.purchased_on} class="rounded-lg title text-lg">
|
||||||
|
<%= gettext("Purchased on:") %>
|
||||||
|
<.date id={"#{@ammo_group.id}-purchased-on"} date={@ammo_group.purchased_on} />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span :if={@last_used_date} class="rounded-lg title text-lg">
|
||||||
|
<%= gettext("Last used on:") %>
|
||||||
|
<.date id={"#{@ammo_group.id}-last-used-on"} date={@last_used_date} />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span :if={@ammo_group.price_paid} class="rounded-lg title text-lg">
|
||||||
|
<%= gettext("Price paid:") %>
|
||||||
|
<%= gettext("$%{amount}", amount: display_currency(@ammo_group.price_paid)) %>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span :if={@cpr} class="rounded-lg title text-lg">
|
||||||
|
<%= gettext("CPR:") %>
|
||||||
|
<%= gettext("$%{amount}", amount: display_currency(@cpr)) %>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span
|
||||||
|
:if={@show_container && Containers.get_container!(@ammo_group.container_id, @current_user)}
|
||||||
|
class="rounded-lg title text-lg"
|
||||||
|
>
|
||||||
|
<%= gettext("Container:") %>
|
||||||
|
|
||||||
|
<.link
|
||||||
|
navigate={Routes.container_show_path(Endpoint, :show, @ammo_group.container_id)}
|
||||||
|
class="link"
|
||||||
|
>
|
||||||
|
<%= Containers.get_container!(@ammo_group.container_id, @current_user).name %>
|
||||||
|
</.link>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div :if={@inner_block} class="mt-4 flex space-x-4 justify-center items-center">
|
||||||
|
<%= render_slot(@inner_block) %>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,58 @@
|
|||||||
|
<div
|
||||||
|
id={"container-#{@container.id}"}
|
||||||
|
class="overflow-hidden max-w-full mx-4 mb-4 px-8 py-4
|
||||||
|
flex flex-col justify-around items-center space-y-4
|
||||||
|
border border-gray-400 rounded-lg shadow-lg hover:shadow-md
|
||||||
|
transition-all duration-300 ease-in-out"
|
||||||
|
>
|
||||||
|
<.link navigate={Routes.container_show_path(Endpoint, :show, @container)} class="link">
|
||||||
|
<h1 class="px-4 py-2 rounded-lg title text-xl">
|
||||||
|
<%= @container.name %>
|
||||||
|
</h1>
|
||||||
|
</.link>
|
||||||
|
|
||||||
|
<div class="flex flex-col justify-center items-center space-y-2">
|
||||||
|
<span :if={@container.desc} class="rounded-lg title text-lg">
|
||||||
|
<%= gettext("Description:") %>
|
||||||
|
<%= @container.desc %>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="rounded-lg title text-lg">
|
||||||
|
<%= gettext("Type:") %>
|
||||||
|
<%= @container.type %>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span :if={@container.location} class="rounded-lg title text-lg">
|
||||||
|
<%= gettext("Location:") %>
|
||||||
|
<%= @container.location %>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<%= if @container |> Ammo.get_ammo_groups_count_for_container!(@current_user) != 0 do %>
|
||||||
|
<span class="rounded-lg title text-lg">
|
||||||
|
<%= gettext("Packs:") %>
|
||||||
|
<%= @container |> Ammo.get_ammo_groups_count_for_container!(@current_user) %>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="rounded-lg title text-lg">
|
||||||
|
<%= gettext("Rounds:") %>
|
||||||
|
<%= @container |> Ammo.get_round_count_for_container!(@current_user) %>
|
||||||
|
</span>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<div
|
||||||
|
:if={@tag_actions || @container.tags != []}
|
||||||
|
class="flex flex-wrap justify-center items-center"
|
||||||
|
>
|
||||||
|
<.simple_tag_card :for={tag <- @container.tags} tag={tag} />
|
||||||
|
|
||||||
|
<%= if @tag_actions, do: render_slot(@tag_actions) %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
:if={assigns |> Map.has_key?(:inner_block)}
|
||||||
|
class="flex space-x-4 justify-center items-center"
|
||||||
|
>
|
||||||
|
<%= render_slot(@inner_block) %>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,3 @@
|
|||||||
|
<time :if={@date} id={@id} datetime={Date.to_iso8601(@date, :extended)} phx-hook="Date">
|
||||||
|
<%= Date.to_iso8601(@date, :extended) %>
|
||||||
|
</time>
|
@ -0,0 +1,3 @@
|
|||||||
|
<time :if={@datetime} id={@id} datetime={cast_datetime(@datetime)} phx-hook="DateTime">
|
||||||
|
<%= cast_datetime(@datetime) %>
|
||||||
|
</time>
|
@ -0,0 +1,46 @@
|
|||||||
|
<div class="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
|
||||||
|
transition-all duration-300 ease-in-out">
|
||||||
|
<h1 class="title text-xl">
|
||||||
|
<%= @invite.name %>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<%= if @invite.disabled_at |> is_nil() do %>
|
||||||
|
<h2 class="title text-md">
|
||||||
|
<%= if @invite.uses_left do %>
|
||||||
|
<%= gettext(
|
||||||
|
"Uses Left: %{uses_left_count}",
|
||||||
|
uses_left_count: @invite.uses_left
|
||||||
|
) %>
|
||||||
|
<% else %>
|
||||||
|
<%= gettext("Uses Left: Unlimited") %>
|
||||||
|
<% end %>
|
||||||
|
</h2>
|
||||||
|
<% else %>
|
||||||
|
<h2 class="title text-md">
|
||||||
|
<%= gettext("Invite Disabled") %>
|
||||||
|
</h2>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<.qr_code
|
||||||
|
content={Routes.user_registration_url(Endpoint, :new, invite: @invite.token)}
|
||||||
|
filename={@invite.name}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<h2 :if={@use_count && @use_count != 0} class="title text-md">
|
||||||
|
<%= gettext("Uses: %{uses_count}", uses_count: @use_count) %>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div class="flex flex-row flex-wrap justify-center items-center">
|
||||||
|
<code
|
||||||
|
id={"code-#{@invite.id}"}
|
||||||
|
class="mx-2 my-1 text-xs px-4 py-2 rounded-lg text-center break-all text-gray-100 bg-primary-800"
|
||||||
|
phx-no-format
|
||||||
|
><%= Routes.user_registration_url(Endpoint, :new, invite: @invite.token) %></code>
|
||||||
|
<%= if @code_actions, do: render_slot(@code_actions) %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div :if={@inner_block} class="flex space-x-4 justify-center items-center">
|
||||||
|
<%= render_slot(@inner_block) %>
|
||||||
|
</div>
|
||||||
|
</div>
|
42
lib/cannery_web/components/core_components/modal.html.heex
Normal file
42
lib/cannery_web/components/core_components/modal.html.heex
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<.link
|
||||||
|
patch={@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()}
|
||||||
|
>
|
||||||
|
<span class="hidden"></span>
|
||||||
|
</.link>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="modal"
|
||||||
|
class="fixed z-10 left-0 top-0 pointer-events-none
|
||||||
|
w-full h-full overflow-hidden
|
||||||
|
p-4 sm:p-8 flex flex-col justify-center items-center"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
id="modal-content"
|
||||||
|
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-start items-center
|
||||||
|
bg-white border-2 rounded-lg"
|
||||||
|
>
|
||||||
|
<.link
|
||||||
|
patch={@return_to}
|
||||||
|
id="close"
|
||||||
|
class="absolute top-8 right-10
|
||||||
|
text-gray-500 hover:text-gray-800
|
||||||
|
transition-all duration-500 ease-in-out"
|
||||||
|
phx-remove={hide_modal()}
|
||||||
|
>
|
||||||
|
<i class="fa-fw fa-lg fas fa-times"></i>
|
||||||
|
</.link>
|
||||||
|
|
||||||
|
<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) %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,3 @@
|
|||||||
|
<a href={qr_code_image(@content)} download={@filename <> ".png"}>
|
||||||
|
<img class={@image_class} alt={@filename} src={qr_code_image(@content)} />
|
||||||
|
</a>
|
@ -0,0 +1,6 @@
|
|||||||
|
<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>
|
@ -0,0 +1,9 @@
|
|||||||
|
<div
|
||||||
|
id={"tag-#{@tag.id}"}
|
||||||
|
class="mx-4 mb-4 px-8 py-4 space-x-4 flex justify-center items-center
|
||||||
|
border border-gray-400 rounded-lg shadow-lg hover:shadow-md
|
||||||
|
transition-all duration-300 ease-in-out"
|
||||||
|
>
|
||||||
|
<.simple_tag_card tag={@tag} />
|
||||||
|
<%= render_slot(@inner_block) %>
|
||||||
|
</div>
|
@ -0,0 +1,30 @@
|
|||||||
|
<label for={@id || @action} class="inline-flex relative items-center cursor-pointer">
|
||||||
|
<input
|
||||||
|
id={@id || @action}
|
||||||
|
type="checkbox"
|
||||||
|
value={@value}
|
||||||
|
checked={@value}
|
||||||
|
class="sr-only peer"
|
||||||
|
aria-labelledby={"#{@id || @action}-label"}
|
||||||
|
{
|
||||||
|
if assigns |> Map.has_key?(:target),
|
||||||
|
do: %{"phx-click": @action, "phx-value-value": @value, "phx-target": @target},
|
||||||
|
else: %{"phx-click": @action, "phx-value-value": @value}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div class="w-11 h-6 bg-gray-300 rounded-full peer
|
||||||
|
peer-focus:ring-4 peer-focus:ring-teal-300 dark:peer-focus:ring-teal-800
|
||||||
|
peer-checked:bg-gray-600
|
||||||
|
peer-checked:after:translate-x-full peer-checked:after:border-white
|
||||||
|
after:content-[''] after:absolute after:top-1 after:left-[2px] after:bg-white after:border-gray-300
|
||||||
|
after:border after:rounded-full after:h-5 after:w-5
|
||||||
|
after:transition-all after:duration-250 after:ease-in-out
|
||||||
|
transition-colors duration-250 ease-in-out">
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
id={"#{@id || @action}-label"}
|
||||||
|
class="ml-3 text-sm font-medium text-gray-900 dark:text-gray-300"
|
||||||
|
>
|
||||||
|
<%= render_slot(@inner_block) %>
|
||||||
|
</span>
|
||||||
|
</label>
|
130
lib/cannery_web/components/core_components/topbar.html.heex
Normal file
130
lib/cannery_web/components/core_components/topbar.html.heex
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
<nav role="navigation" class="mb-8 px-8 py-4 w-full bg-primary-500">
|
||||||
|
<div class="flex flex-col sm:flex-row justify-between items-center">
|
||||||
|
<div class="mb-4 sm:mb-0 sm:mr-8 flex flex-row justify-start items-center space-x-2">
|
||||||
|
<.link
|
||||||
|
navigate={Routes.live_path(Endpoint, HomeLive)}
|
||||||
|
class="inline mx-2 my-1 leading-5 text-xl text-white"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={Routes.static_path(Endpoint, "/images/cannery.svg")}
|
||||||
|
alt={gettext("Cannery logo")}
|
||||||
|
class="inline-block h-8 mx-1"
|
||||||
|
/>
|
||||||
|
<h1 class="inline hover:underline">Cannery</h1>
|
||||||
|
</.link>
|
||||||
|
|
||||||
|
<%= if @title_content do %>
|
||||||
|
<span class="mx-2 my-1">
|
||||||
|
|
|
||||||
|
</span>
|
||||||
|
<%= @title_content %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="mb-2 sm:hidden hr-light" />
|
||||||
|
|
||||||
|
<ul class="flex flex-row flex-wrap justify-center items-center
|
||||||
|
text-lg text-white text-ellipsis">
|
||||||
|
<%= if @current_user do %>
|
||||||
|
<li class="mx-2 my-1">
|
||||||
|
<.link
|
||||||
|
navigate={Routes.tag_index_path(Endpoint, :index)}
|
||||||
|
class="text-white hover:underline"
|
||||||
|
>
|
||||||
|
<%= gettext("Tags") %>
|
||||||
|
</.link>
|
||||||
|
</li>
|
||||||
|
<li class="mx-2 my-1">
|
||||||
|
<.link
|
||||||
|
navigate={Routes.container_index_path(Endpoint, :index)}
|
||||||
|
class="text-white hover:underline"
|
||||||
|
>
|
||||||
|
<%= gettext("Containers") %>
|
||||||
|
</.link>
|
||||||
|
</li>
|
||||||
|
<li class="mx-2 my-1">
|
||||||
|
<.link
|
||||||
|
navigate={Routes.ammo_type_index_path(Endpoint, :index)}
|
||||||
|
class="text-white hover:underline"
|
||||||
|
>
|
||||||
|
<%= gettext("Catalog") %>
|
||||||
|
</.link>
|
||||||
|
</li>
|
||||||
|
<li class="mx-2 my-1">
|
||||||
|
<.link
|
||||||
|
navigate={Routes.ammo_group_index_path(Endpoint, :index)}
|
||||||
|
class="text-white hover:underline"
|
||||||
|
>
|
||||||
|
<%= gettext("Ammo") %>
|
||||||
|
</.link>
|
||||||
|
</li>
|
||||||
|
<li class="mx-2 my-1">
|
||||||
|
<.link
|
||||||
|
navigate={Routes.range_index_path(Endpoint, :index)}
|
||||||
|
class="text-white hover:underline"
|
||||||
|
>
|
||||||
|
<%= gettext("Range") %>
|
||||||
|
</.link>
|
||||||
|
</li>
|
||||||
|
<li :if={@current_user |> Accounts.is_already_admin?()} class="mx-2 my-1">
|
||||||
|
<.link
|
||||||
|
navigate={Routes.invite_index_path(Endpoint, :index)}
|
||||||
|
class="text-white hover:underline"
|
||||||
|
>
|
||||||
|
<%= gettext("Invites") %>
|
||||||
|
</.link>
|
||||||
|
</li>
|
||||||
|
<li class="mx-2 my-1">
|
||||||
|
<.link
|
||||||
|
href={Routes.user_settings_path(Endpoint, :edit)}
|
||||||
|
class="text-white hover:underline truncate"
|
||||||
|
>
|
||||||
|
<%= @current_user.email %>
|
||||||
|
</.link>
|
||||||
|
</li>
|
||||||
|
<li class="mx-2 my-1">
|
||||||
|
<.link
|
||||||
|
href={Routes.user_session_path(Endpoint, :delete)}
|
||||||
|
method="delete"
|
||||||
|
data-confirm={dgettext("prompts", "Are you sure you want to log out?")}
|
||||||
|
aria-label={gettext("Log out")}
|
||||||
|
>
|
||||||
|
<i class="fas fa-sign-out-alt"></i>
|
||||||
|
</.link>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
:if={
|
||||||
|
@current_user |> Accounts.is_already_admin?() and
|
||||||
|
function_exported?(Routes, :live_dashboard_path, 2)
|
||||||
|
}
|
||||||
|
class="mx-2 my-1"
|
||||||
|
>
|
||||||
|
<.link
|
||||||
|
navigate={Routes.live_dashboard_path(Endpoint, :home)}
|
||||||
|
class="text-white hover:underline"
|
||||||
|
aria-label={gettext("Live Dashboard")}
|
||||||
|
>
|
||||||
|
<i class="fas fa-gauge"></i>
|
||||||
|
</.link>
|
||||||
|
</li>
|
||||||
|
<% else %>
|
||||||
|
<li :if={Accounts.allow_registration?()} class="mx-2 my-1">
|
||||||
|
<.link
|
||||||
|
href={Routes.user_registration_path(Endpoint, :new)}
|
||||||
|
class="text-white hover:underline truncate"
|
||||||
|
>
|
||||||
|
<%= dgettext("actions", "Register") %>
|
||||||
|
</.link>
|
||||||
|
</li>
|
||||||
|
<li class="mx-2 my-1">
|
||||||
|
<.link
|
||||||
|
href={Routes.user_session_path(Endpoint, :new)}
|
||||||
|
class="text-white hover:underline truncate"
|
||||||
|
>
|
||||||
|
<%= dgettext("actions", "Log in") %>
|
||||||
|
</.link>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
@ -0,0 +1,36 @@
|
|||||||
|
<div
|
||||||
|
id={"user-#{@user.id}"}
|
||||||
|
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
|
||||||
|
transition-all duration-300 ease-in-out"
|
||||||
|
>
|
||||||
|
<h1 class="px-4 py-2 rounded-lg title text-xl break-all">
|
||||||
|
<%= @user.email %>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<h3 class="px-4 py-2 rounded-lg title text-lg">
|
||||||
|
<p>
|
||||||
|
<%= if @user.confirmed_at do %>
|
||||||
|
<%= gettext(
|
||||||
|
"User was confirmed at%{confirmed_datetime}",
|
||||||
|
confirmed_datetime: ""
|
||||||
|
) %>
|
||||||
|
<.datetime id={"#{@user.id}-confirmed-at"} datetime={@user.confirmed_at} />
|
||||||
|
<% else %>
|
||||||
|
<%= gettext("Email unconfirmed") %>
|
||||||
|
<% end %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<%= gettext(
|
||||||
|
"User registered on%{registered_datetime}",
|
||||||
|
registered_datetime: ""
|
||||||
|
) %>
|
||||||
|
<.datetime id={"#{@user.id}-inserted-at"} datetime={@user.inserted_at} />
|
||||||
|
</p>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div :if={@inner_block} class="px-4 py-2 flex space-x-4 justify-center items-center">
|
||||||
|
<%= render_slot(@inner_block) %>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -1,70 +0,0 @@
|
|||||||
defmodule CanneryWeb.Components.InviteCard do
|
|
||||||
@moduledoc """
|
|
||||||
Display card for an invite
|
|
||||||
"""
|
|
||||||
|
|
||||||
use CanneryWeb, :component
|
|
||||||
alias Cannery.Accounts.{Invite, Invites, User}
|
|
||||||
alias CanneryWeb.Endpoint
|
|
||||||
|
|
||||||
attr :invite, Invite, required: true
|
|
||||||
attr :current_user, User, required: true
|
|
||||||
slot(:inner_block)
|
|
||||||
slot(:code_actions)
|
|
||||||
|
|
||||||
def invite_card(%{invite: invite, current_user: current_user} = assigns) do
|
|
||||||
assigns =
|
|
||||||
assigns
|
|
||||||
|> assign(:use_count, Invites.get_use_count(invite, current_user))
|
|
||||||
|> assign_new(:code_actions, fn -> [] end)
|
|
||||||
|
|
||||||
~H"""
|
|
||||||
<div class="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
|
|
||||||
transition-all duration-300 ease-in-out">
|
|
||||||
<h1 class="title text-xl">
|
|
||||||
<%= @invite.name %>
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<%= if @invite.disabled_at |> is_nil() do %>
|
|
||||||
<h2 class="title text-md">
|
|
||||||
<%= if @invite.uses_left do %>
|
|
||||||
<%= gettext(
|
|
||||||
"Uses Left: %{uses_left_count}",
|
|
||||||
uses_left_count: @invite.uses_left
|
|
||||||
) %>
|
|
||||||
<% else %>
|
|
||||||
<%= gettext("Uses Left: Unlimited") %>
|
|
||||||
<% end %>
|
|
||||||
</h2>
|
|
||||||
<% else %>
|
|
||||||
<h2 class="title text-md">
|
|
||||||
<%= gettext("Invite Disabled") %>
|
|
||||||
</h2>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<.qr_code
|
|
||||||
content={Routes.user_registration_url(Endpoint, :new, invite: @invite.token)}
|
|
||||||
filename={@invite.name}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<h2 :if={@use_count != 0} class="title text-md">
|
|
||||||
<%= gettext("Uses: %{uses_count}", uses_count: @use_count) %>
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<div class="flex flex-row flex-wrap justify-center items-center">
|
|
||||||
<code
|
|
||||||
id={"code-#{@invite.id}"}
|
|
||||||
class="mx-2 my-1 text-xs px-4 py-2 rounded-lg text-center break-all text-gray-100 bg-primary-800"
|
|
||||||
phx-no-format
|
|
||||||
><%= Routes.user_registration_url(Endpoint, :new, invite: @invite.token) %></code>
|
|
||||||
<%= render_slot(@code_actions) %>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div :if={@inner_block} class="flex space-x-4 justify-center items-center">
|
|
||||||
<%= render_slot(@inner_block) %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
end
|
|
@ -6,6 +6,7 @@ defmodule CanneryWeb.Components.MoveAmmoGroupComponent do
|
|||||||
use CanneryWeb, :live_component
|
use CanneryWeb, :live_component
|
||||||
alias Cannery.{Accounts.User, Ammo, Ammo.AmmoGroup, Containers, Containers.Container}
|
alias Cannery.{Accounts.User, Ammo, Ammo.AmmoGroup, Containers, Containers.Container}
|
||||||
alias CanneryWeb.Endpoint
|
alias CanneryWeb.Endpoint
|
||||||
|
alias Ecto.Changeset
|
||||||
alias Phoenix.LiveView.Socket
|
alias Phoenix.LiveView.Socket
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
@ -51,10 +52,9 @@ defmodule CanneryWeb.Components.MoveAmmoGroupComponent do
|
|||||||
|> case do
|
|> case do
|
||||||
{:ok, _ammo_group} ->
|
{:ok, _ammo_group} ->
|
||||||
prompt = dgettext("prompts", "Ammo moved to %{name} successfully", name: container_name)
|
prompt = dgettext("prompts", "Ammo moved to %{name} successfully", name: container_name)
|
||||||
|
|
||||||
socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
|
socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
|
||||||
|
|
||||||
{:error, %Ecto.Changeset{} = changeset} ->
|
{:error, %Changeset{} = changeset} ->
|
||||||
socket |> assign(changeset: changeset)
|
socket |> assign(changeset: changeset)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -64,10 +64,10 @@ defmodule CanneryWeb.Components.MoveAmmoGroupComponent do
|
|||||||
@impl true
|
@impl true
|
||||||
def render(%{containers: containers} = assigns) do
|
def render(%{containers: containers} = assigns) do
|
||||||
columns = [
|
columns = [
|
||||||
%{label: gettext("Container"), key: "name"},
|
%{label: gettext("Container"), key: :name},
|
||||||
%{label: gettext("Type"), key: "type"},
|
%{label: gettext("Type"), key: :type},
|
||||||
%{label: gettext("Location"), key: "location"},
|
%{label: gettext("Location"), key: :location},
|
||||||
%{label: nil, key: "actions", sortable: false}
|
%{label: gettext("Actions"), key: :actions, sortable: false}
|
||||||
]
|
]
|
||||||
|
|
||||||
rows = containers |> get_rows_for_containers(assigns, columns)
|
rows = containers |> get_rows_for_containers(assigns, columns)
|
||||||
@ -110,8 +110,8 @@ defmodule CanneryWeb.Components.MoveAmmoGroupComponent do
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec get_row_value_by_key(String.t(), Container.t(), map()) :: any()
|
@spec get_row_value_by_key(atom(), Container.t(), map()) :: any()
|
||||||
defp get_row_value_by_key("actions", container, assigns) do
|
defp get_row_value_by_key(:actions, container, assigns) do
|
||||||
assigns = assigns |> Map.put(:container, container)
|
assigns = assigns |> Map.put(:container, container)
|
||||||
|
|
||||||
~H"""
|
~H"""
|
||||||
@ -129,6 +129,5 @@ defmodule CanneryWeb.Components.MoveAmmoGroupComponent do
|
|||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_row_value_by_key(key, container, _assigns),
|
defp get_row_value_by_key(key, container, _assigns), do: container |> Map.get(key)
|
||||||
do: container |> Map.get(key |> String.to_existing_atom())
|
|
||||||
end
|
end
|
||||||
|
@ -3,7 +3,7 @@ defmodule CanneryWeb.Components.ShotGroupTableComponent do
|
|||||||
A component that displays a list of shot groups
|
A component that displays a list of shot groups
|
||||||
"""
|
"""
|
||||||
use CanneryWeb, :live_component
|
use CanneryWeb, :live_component
|
||||||
alias Cannery.{Accounts.User, ActivityLog.ShotGroup, Repo}
|
alias Cannery.{Accounts.User, ActivityLog.ShotGroup, Ammo, ComparableDate}
|
||||||
alias Ecto.UUID
|
alias Ecto.UUID
|
||||||
alias Phoenix.LiveView.{Rendered, Socket}
|
alias Phoenix.LiveView.{Rendered, Socket}
|
||||||
|
|
||||||
@ -41,11 +41,16 @@ defmodule CanneryWeb.Components.ShotGroupTableComponent do
|
|||||||
%{label: gettext("Ammo"), key: :name},
|
%{label: gettext("Ammo"), key: :name},
|
||||||
%{label: gettext("Rounds shot"), key: :count},
|
%{label: gettext("Rounds shot"), key: :count},
|
||||||
%{label: gettext("Notes"), key: :notes},
|
%{label: gettext("Notes"), key: :notes},
|
||||||
%{label: gettext("Date"), key: :date},
|
%{label: gettext("Date"), key: :date, type: ComparableDate},
|
||||||
%{label: nil, key: :actions, sortable: false}
|
%{label: gettext("Actions"), key: :actions, sortable: false}
|
||||||
]
|
]
|
||||||
|
|
||||||
extra_data = %{current_user: current_user, actions: actions}
|
ammo_groups =
|
||||||
|
shot_groups
|
||||||
|
|> Enum.map(fn %{ammo_group_id: ammo_group_id} -> ammo_group_id end)
|
||||||
|
|> Ammo.get_ammo_groups(current_user)
|
||||||
|
|
||||||
|
extra_data = %{current_user: current_user, actions: actions, ammo_groups: ammo_groups}
|
||||||
|
|
||||||
rows =
|
rows =
|
||||||
shot_groups
|
shot_groups
|
||||||
@ -79,34 +84,28 @@ defmodule CanneryWeb.Components.ShotGroupTableComponent do
|
|||||||
@spec get_row_data_for_shot_group(ShotGroup.t(), columns :: [map()], extra_data :: map()) ::
|
@spec get_row_data_for_shot_group(ShotGroup.t(), columns :: [map()], extra_data :: map()) ::
|
||||||
map()
|
map()
|
||||||
defp get_row_data_for_shot_group(shot_group, columns, extra_data) do
|
defp get_row_data_for_shot_group(shot_group, columns, extra_data) do
|
||||||
shot_group = shot_group |> Repo.preload(ammo_group: :ammo_type)
|
|
||||||
|
|
||||||
columns
|
columns
|
||||||
|> Map.new(fn %{key: key} ->
|
|> Map.new(fn %{key: key} ->
|
||||||
{key, get_row_value(key, shot_group, extra_data)}
|
{key, get_row_value(key, shot_group, extra_data)}
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_row_value(
|
defp get_row_value(:name, %{ammo_group_id: ammo_group_id}, %{ammo_groups: ammo_groups}) do
|
||||||
:name,
|
assigns = %{ammo_group: ammo_group = Map.fetch!(ammo_groups, ammo_group_id)}
|
||||||
%{ammo_group: %{ammo_type: %{name: ammo_type_name} = ammo_group}},
|
|
||||||
_extra_data
|
|
||||||
) do
|
|
||||||
assigns = %{ammo_group: ammo_group, ammo_type_name: ammo_type_name}
|
|
||||||
|
|
||||||
name_block = ~H"""
|
{ammo_group.ammo_type.name,
|
||||||
<.link navigate={Routes.ammo_group_show_path(Endpoint, :show, @ammo_group)} class="link">
|
~H"""
|
||||||
<%= @ammo_type_name %>
|
<.link navigate={Routes.ammo_group_show_path(Endpoint, :show, @ammo_group)} class="link">
|
||||||
</.link>
|
<%= @ammo_group.ammo_type.name %>
|
||||||
"""
|
</.link>
|
||||||
|
"""}
|
||||||
{ammo_type_name, name_block}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_row_value(:date, %{date: _date} = assigns, _extra_data) do
|
defp get_row_value(:date, %{date: date} = assigns, _extra_data) do
|
||||||
~H"""
|
{date,
|
||||||
<.date date={@date} />
|
~H"""
|
||||||
"""
|
<.date id={"#{@id}-date"} date={@date} />
|
||||||
|
"""}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_row_value(:actions, shot_group, %{actions: actions}) do
|
defp get_row_value(:actions, shot_group, %{actions: actions}) do
|
||||||
|
@ -33,7 +33,8 @@ defmodule CanneryWeb.Components.TableComponent do
|
|||||||
optional(:class) => String.t(),
|
optional(:class) => String.t(),
|
||||||
optional(:row_class) => String.t(),
|
optional(:row_class) => String.t(),
|
||||||
optional(:alternate_row_class) => String.t(),
|
optional(:alternate_row_class) => String.t(),
|
||||||
optional(:sortable) => false
|
optional(:sortable) => false,
|
||||||
|
optional(:type) => module()
|
||||||
}),
|
}),
|
||||||
required(:rows) =>
|
required(:rows) =>
|
||||||
list(%{
|
list(%{
|
||||||
@ -60,7 +61,8 @@ defmodule CanneryWeb.Components.TableComponent do
|
|||||||
:asc
|
:asc
|
||||||
end
|
end
|
||||||
|
|
||||||
rows = rows |> sort_by_custom_sort_value_or_value(initial_key, initial_sort_mode)
|
type = columns |> Enum.find(%{}, fn %{key: key} -> key == initial_key end) |> Map.get(:type)
|
||||||
|
rows = rows |> sort_by_custom_sort_value_or_value(initial_key, initial_sort_mode, type)
|
||||||
|
|
||||||
socket =
|
socket =
|
||||||
socket
|
socket
|
||||||
@ -68,6 +70,7 @@ defmodule CanneryWeb.Components.TableComponent do
|
|||||||
|> assign(
|
|> assign(
|
||||||
columns: columns,
|
columns: columns,
|
||||||
rows: rows,
|
rows: rows,
|
||||||
|
key: initial_key,
|
||||||
last_sort_key: initial_key,
|
last_sort_key: initial_key,
|
||||||
sort_mode: initial_sort_mode
|
sort_mode: initial_sort_mode
|
||||||
)
|
)
|
||||||
@ -81,7 +84,14 @@ defmodule CanneryWeb.Components.TableComponent do
|
|||||||
def handle_event(
|
def handle_event(
|
||||||
"sort_by",
|
"sort_by",
|
||||||
%{"sort-key" => key},
|
%{"sort-key" => key},
|
||||||
%{assigns: %{rows: rows, last_sort_key: last_sort_key, sort_mode: sort_mode}} = socket
|
%{
|
||||||
|
assigns: %{
|
||||||
|
columns: columns,
|
||||||
|
rows: rows,
|
||||||
|
last_sort_key: last_sort_key,
|
||||||
|
sort_mode: sort_mode
|
||||||
|
}
|
||||||
|
} = socket
|
||||||
) do
|
) do
|
||||||
key = key |> String.to_existing_atom()
|
key = key |> String.to_existing_atom()
|
||||||
|
|
||||||
@ -92,11 +102,28 @@ defmodule CanneryWeb.Components.TableComponent do
|
|||||||
{_new_sort_key, _last_sort_mode} -> :asc
|
{_new_sort_key, _last_sort_mode} -> :asc
|
||||||
end
|
end
|
||||||
|
|
||||||
rows = rows |> sort_by_custom_sort_value_or_value(key, sort_mode)
|
type =
|
||||||
|
columns |> Enum.find(%{}, fn %{key: column_key} -> column_key == key end) |> Map.get(:type)
|
||||||
|
|
||||||
|
rows = rows |> sort_by_custom_sort_value_or_value(key, sort_mode, type)
|
||||||
{:noreply, socket |> assign(last_sort_key: key, sort_mode: sort_mode, rows: rows)}
|
{:noreply, socket |> assign(last_sort_key: key, sort_mode: sort_mode, rows: rows)}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp sort_by_custom_sort_value_or_value(rows, key, sort_mode) do
|
defp sort_by_custom_sort_value_or_value(rows, key, sort_mode, type)
|
||||||
|
when type in [Date, DateTime] 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, type}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp sort_by_custom_sort_value_or_value(rows, key, sort_mode, _type) do
|
||||||
rows
|
rows
|
||||||
|> Enum.sort_by(
|
|> Enum.sort_by(
|
||||||
fn row ->
|
fn row ->
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
defmodule CanneryWeb.Components.TagCard do
|
|
||||||
@moduledoc """
|
|
||||||
Display card for a tag
|
|
||||||
"""
|
|
||||||
|
|
||||||
use CanneryWeb, :component
|
|
||||||
alias Cannery.Tags.Tag
|
|
||||||
|
|
||||||
attr :tag, Tag, required: true
|
|
||||||
slot(:inner_block, required: true)
|
|
||||||
|
|
||||||
def tag_card(assigns) do
|
|
||||||
~H"""
|
|
||||||
<div
|
|
||||||
id={"tag-#{@tag.id}"}
|
|
||||||
class="mx-4 mb-4 px-8 py-4 space-x-4 flex justify-center items-center
|
|
||||||
border border-gray-400 rounded-lg shadow-lg hover:shadow-md
|
|
||||||
transition-all duration-300 ease-in-out"
|
|
||||||
>
|
|
||||||
<.simple_tag_card tag={@tag} />
|
|
||||||
<%= render_slot(@inner_block) %>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
attr :tag, Tag, required: true
|
|
||||||
|
|
||||||
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
|
|
@ -1,146 +0,0 @@
|
|||||||
defmodule CanneryWeb.Components.Topbar do
|
|
||||||
@moduledoc """
|
|
||||||
Component that renders a topbar with user functions/links
|
|
||||||
"""
|
|
||||||
|
|
||||||
use CanneryWeb, :component
|
|
||||||
|
|
||||||
alias Cannery.Accounts
|
|
||||||
alias CanneryWeb.HomeLive
|
|
||||||
|
|
||||||
def topbar(assigns) do
|
|
||||||
assigns =
|
|
||||||
%{results: [], title_content: nil, flash: nil, current_user: nil} |> Map.merge(assigns)
|
|
||||||
|
|
||||||
~H"""
|
|
||||||
<nav role="navigation" class="mb-8 px-8 py-4 w-full bg-primary-400">
|
|
||||||
<div class="flex flex-col sm:flex-row justify-between items-center">
|
|
||||||
<div class="mb-4 sm:mb-0 sm:mr-8 flex flex-row justify-start items-center space-x-2">
|
|
||||||
<.link
|
|
||||||
navigate={Routes.live_path(Endpoint, HomeLive)}
|
|
||||||
class="inline mx-2 my-1 leading-5 text-xl text-white"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={Routes.static_path(Endpoint, "/images/cannery.svg")}
|
|
||||||
alt={gettext("Cannery logo")}
|
|
||||||
class="inline-block h-8 mx-1"
|
|
||||||
/>
|
|
||||||
<h1 class="inline hover:underline">Cannery</h1>
|
|
||||||
</.link>
|
|
||||||
|
|
||||||
<%= if @title_content do %>
|
|
||||||
<span class="mx-2 my-1">
|
|
||||||
|
|
|
||||||
</span>
|
|
||||||
<%= @title_content %>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr class="mb-2 sm:hidden hr-light" />
|
|
||||||
|
|
||||||
<ul class="flex flex-row flex-wrap justify-center items-center
|
|
||||||
text-lg text-white text-ellipsis">
|
|
||||||
<%= if @current_user do %>
|
|
||||||
<li class="mx-2 my-1">
|
|
||||||
<.link
|
|
||||||
navigate={Routes.tag_index_path(Endpoint, :index)}
|
|
||||||
class="text-primary-600 text-white hover:underline"
|
|
||||||
>
|
|
||||||
<%= gettext("Tags") %>
|
|
||||||
</.link>
|
|
||||||
</li>
|
|
||||||
<li class="mx-2 my-1">
|
|
||||||
<.link
|
|
||||||
navigate={Routes.container_index_path(Endpoint, :index)}
|
|
||||||
class="text-primary-600 text-white hover:underline"
|
|
||||||
>
|
|
||||||
<%= gettext("Containers") %>
|
|
||||||
</.link>
|
|
||||||
</li>
|
|
||||||
<li class="mx-2 my-1">
|
|
||||||
<.link
|
|
||||||
navigate={Routes.ammo_type_index_path(Endpoint, :index)}
|
|
||||||
class="text-primary-600 text-white hover:underline"
|
|
||||||
>
|
|
||||||
<%= gettext("Catalog") %>
|
|
||||||
</.link>
|
|
||||||
</li>
|
|
||||||
<li class="mx-2 my-1">
|
|
||||||
<.link
|
|
||||||
navigate={Routes.ammo_group_index_path(Endpoint, :index)}
|
|
||||||
class="text-primary-600 text-white hover:underline"
|
|
||||||
>
|
|
||||||
<%= gettext("Ammo") %>
|
|
||||||
</.link>
|
|
||||||
</li>
|
|
||||||
<li class="mx-2 my-1">
|
|
||||||
<.link
|
|
||||||
navigate={Routes.range_index_path(Endpoint, :index)}
|
|
||||||
class="text-primary-600 text-white hover:underline"
|
|
||||||
>
|
|
||||||
<%= gettext("Range") %>
|
|
||||||
</.link>
|
|
||||||
</li>
|
|
||||||
<li :if={@current_user |> Accounts.is_already_admin?()} class="mx-2 my-1">
|
|
||||||
<.link
|
|
||||||
navigate={Routes.invite_index_path(Endpoint, :index)}
|
|
||||||
class="text-primary-600 text-white hover:underline"
|
|
||||||
>
|
|
||||||
<%= gettext("Invites") %>
|
|
||||||
</.link>
|
|
||||||
</li>
|
|
||||||
<li class="mx-2 my-1">
|
|
||||||
<.link
|
|
||||||
href={Routes.user_settings_path(Endpoint, :edit)}
|
|
||||||
class="text-primary-600 text-white hover:underline truncate"
|
|
||||||
>
|
|
||||||
<%= @current_user.email %>
|
|
||||||
</.link>
|
|
||||||
</li>
|
|
||||||
<li class="mx-2 my-1">
|
|
||||||
<.link
|
|
||||||
href={Routes.user_session_path(Endpoint, :delete)}
|
|
||||||
method="delete"
|
|
||||||
data-confirm={dgettext("prompts", "Are you sure you want to log out?")}
|
|
||||||
>
|
|
||||||
<i class="fas fa-sign-out-alt"></i>
|
|
||||||
</.link>
|
|
||||||
</li>
|
|
||||||
<li
|
|
||||||
:if={
|
|
||||||
@current_user |> Accounts.is_already_admin?() and
|
|
||||||
function_exported?(Routes, :live_dashboard_path, 2)
|
|
||||||
}
|
|
||||||
class="mx-2 my-1"
|
|
||||||
>
|
|
||||||
<.link
|
|
||||||
navigate={Routes.live_dashboard_path(Endpoint, :home)}
|
|
||||||
class="text-white text-white hover:underline"
|
|
||||||
>
|
|
||||||
<i class="fas fa-gauge"></i>
|
|
||||||
</.link>
|
|
||||||
</li>
|
|
||||||
<% else %>
|
|
||||||
<li :if={Accounts.allow_registration?()} class="mx-2 my-1">
|
|
||||||
<.link
|
|
||||||
href={Routes.user_registration_path(Endpoint, :new)}
|
|
||||||
class="text-white hover:underline truncate"
|
|
||||||
>
|
|
||||||
<%= dgettext("actions", "Register") %>
|
|
||||||
</.link>
|
|
||||||
</li>
|
|
||||||
<li class="mx-2 my-1">
|
|
||||||
<.link
|
|
||||||
href={Routes.user_session_path(Endpoint, :new)}
|
|
||||||
class="text-white hover:underline truncate"
|
|
||||||
>
|
|
||||||
<%= dgettext("actions", "Log in") %>
|
|
||||||
</.link>
|
|
||||||
</li>
|
|
||||||
<% end %>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,52 +0,0 @@
|
|||||||
defmodule CanneryWeb.Components.UserCard do
|
|
||||||
@moduledoc """
|
|
||||||
Display card for a user
|
|
||||||
"""
|
|
||||||
|
|
||||||
use CanneryWeb, :component
|
|
||||||
alias Cannery.Accounts.User
|
|
||||||
|
|
||||||
attr :user, User, required: true
|
|
||||||
slot(:inner_block, required: true)
|
|
||||||
|
|
||||||
def user_card(assigns) do
|
|
||||||
~H"""
|
|
||||||
<div
|
|
||||||
id={"user-#{@user.id}"}
|
|
||||||
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
|
|
||||||
transition-all duration-300 ease-in-out"
|
|
||||||
>
|
|
||||||
<h1 class="px-4 py-2 rounded-lg title text-xl break-all">
|
|
||||||
<%= @user.email %>
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<h3 class="px-4 py-2 rounded-lg title text-lg">
|
|
||||||
<p>
|
|
||||||
<%= if @user.confirmed_at do %>
|
|
||||||
<%= gettext(
|
|
||||||
"User was confirmed at%{confirmed_datetime}",
|
|
||||||
confirmed_datetime: ""
|
|
||||||
) %>
|
|
||||||
<.datetime datetime={@user.confirmed_at} />
|
|
||||||
<% else %>
|
|
||||||
<%= gettext("Email unconfirmed") %>
|
|
||||||
<% end %>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<%= gettext(
|
|
||||||
"User registered on%{registered_datetime}",
|
|
||||||
registered_datetime: ""
|
|
||||||
) %>
|
|
||||||
<.datetime datetime={@user.inserted_at} />
|
|
||||||
</p>
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<div :if={@inner_block} class="px-4 py-2 flex space-x-4 justify-center items-center">
|
|
||||||
<%= render_slot(@inner_block) %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
end
|
|
@ -3,41 +3,49 @@ defmodule CanneryWeb.ExportController do
|
|||||||
alias Cannery.{ActivityLog, Ammo, Containers}
|
alias Cannery.{ActivityLog, Ammo, Containers}
|
||||||
|
|
||||||
def export(%{assigns: %{current_user: current_user}} = conn, %{"mode" => "json"}) do
|
def export(%{assigns: %{current_user: current_user}} = conn, %{"mode" => "json"}) do
|
||||||
ammo_types =
|
ammo_types = Ammo.list_ammo_types(current_user)
|
||||||
Ammo.list_ammo_types(current_user)
|
used_counts = ammo_types |> ActivityLog.get_used_count_for_ammo_types(current_user)
|
||||||
|> Enum.map(fn ammo_type ->
|
round_counts = ammo_types |> Ammo.get_round_count_for_ammo_types(current_user)
|
||||||
average_cost = ammo_type |> Ammo.get_average_cost_for_ammo_type!(current_user)
|
ammo_group_counts = ammo_types |> Ammo.get_ammo_groups_count_for_types(current_user)
|
||||||
round_count = ammo_type |> Ammo.get_round_count_for_ammo_type(current_user)
|
|
||||||
used_count = ammo_type |> Ammo.get_used_count_for_ammo_type(current_user)
|
|
||||||
ammo_group_count = ammo_type |> Ammo.get_ammo_groups_count_for_type(current_user, true)
|
|
||||||
|
|
||||||
|
total_ammo_group_counts =
|
||||||
|
ammo_types |> Ammo.get_ammo_groups_count_for_types(current_user, true)
|
||||||
|
|
||||||
|
average_costs = ammo_types |> Ammo.get_average_cost_for_ammo_types(current_user)
|
||||||
|
|
||||||
|
ammo_types =
|
||||||
|
ammo_types
|
||||||
|
|> Enum.map(fn %{id: ammo_type_id} = ammo_type ->
|
||||||
ammo_type
|
ammo_type
|
||||||
|> Jason.encode!()
|
|> Jason.encode!()
|
||||||
|> Jason.decode!()
|
|> Jason.decode!()
|
||||||
|> Map.merge(%{
|
|> Map.merge(%{
|
||||||
"average_cost" => average_cost,
|
"average_cost" => Map.get(average_costs, ammo_type_id),
|
||||||
"round_count" => round_count,
|
"round_count" => Map.get(round_counts, ammo_type_id, 0),
|
||||||
"used_count" => used_count,
|
"used_count" => Map.get(used_counts, ammo_type_id, 0),
|
||||||
"ammo_group_count" => ammo_group_count
|
"ammo_group_count" => Map.get(ammo_group_counts, ammo_type_id, 0),
|
||||||
|
"total_ammo_group_count" => Map.get(total_ammo_group_counts, ammo_type_id, 0)
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
ammo_groups = Ammo.list_ammo_groups(nil, true, current_user)
|
||||||
|
used_counts = ammo_groups |> ActivityLog.get_used_counts(current_user)
|
||||||
|
original_counts = ammo_groups |> Ammo.get_original_counts(current_user)
|
||||||
|
cprs = ammo_groups |> Ammo.get_cprs(current_user)
|
||||||
|
|
||||||
ammo_groups =
|
ammo_groups =
|
||||||
Ammo.list_ammo_groups(nil, true, current_user)
|
ammo_groups
|
||||||
|> Enum.map(fn ammo_group ->
|
|> Enum.map(fn %{id: ammo_group_id} = ammo_group ->
|
||||||
cpr = ammo_group |> Ammo.get_cpr()
|
percentage_remaining = ammo_group |> Ammo.get_percentage_remaining(current_user)
|
||||||
used_count = ammo_group |> Ammo.get_used_count()
|
|
||||||
original_count = ammo_group |> Ammo.get_original_count()
|
|
||||||
percentage_remaining = ammo_group |> Ammo.get_percentage_remaining()
|
|
||||||
|
|
||||||
ammo_group
|
ammo_group
|
||||||
|> Jason.encode!()
|
|> Jason.encode!()
|
||||||
|> Jason.decode!()
|
|> Jason.decode!()
|
||||||
|> Map.merge(%{
|
|> Map.merge(%{
|
||||||
"used_count" => used_count,
|
"used_count" => Map.get(used_counts, ammo_group_id),
|
||||||
"percentage_remaining" => percentage_remaining,
|
"percentage_remaining" => percentage_remaining,
|
||||||
"original_count" => original_count,
|
"original_count" => Map.get(original_counts, ammo_group_id),
|
||||||
"cpr" => cpr
|
"cpr" => Map.get(cprs, ammo_group_id)
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@ -46,8 +54,8 @@ defmodule CanneryWeb.ExportController do
|
|||||||
containers =
|
containers =
|
||||||
Containers.list_containers(current_user)
|
Containers.list_containers(current_user)
|
||||||
|> Enum.map(fn container ->
|
|> Enum.map(fn container ->
|
||||||
ammo_group_count = container |> Containers.get_container_ammo_group_count!()
|
ammo_group_count = container |> Ammo.get_ammo_groups_count_for_container!(current_user)
|
||||||
round_count = container |> Containers.get_container_rounds!()
|
round_count = container |> Ammo.get_round_count_for_container!(current_user)
|
||||||
|
|
||||||
container
|
container
|
||||||
|> Jason.encode!()
|
|> Jason.encode!()
|
||||||
|
@ -3,6 +3,7 @@ defmodule CanneryWeb.UserRegistrationController do
|
|||||||
import CanneryWeb.Gettext
|
import CanneryWeb.Gettext
|
||||||
alias Cannery.{Accounts, Accounts.Invites}
|
alias Cannery.{Accounts, Accounts.Invites}
|
||||||
alias CanneryWeb.{Endpoint, HomeLive}
|
alias CanneryWeb.{Endpoint, HomeLive}
|
||||||
|
alias Ecto.Changeset
|
||||||
|
|
||||||
def new(conn, %{"invite" => invite_token}) do
|
def new(conn, %{"invite" => invite_token}) do
|
||||||
if Invites.valid_invite_token?(invite_token) do
|
if Invites.valid_invite_token?(invite_token) do
|
||||||
@ -70,7 +71,7 @@ defmodule CanneryWeb.UserRegistrationController do
|
|||||||
|> put_flash(:error, dgettext("errors", "Sorry, this invite was not found or expired"))
|
|> put_flash(:error, dgettext("errors", "Sorry, this invite was not found or expired"))
|
||||||
|> redirect(to: Routes.live_path(Endpoint, HomeLive))
|
|> redirect(to: Routes.live_path(Endpoint, HomeLive))
|
||||||
|
|
||||||
{:error, %Ecto.Changeset{} = changeset} ->
|
{:error, %Changeset{} = changeset} ->
|
||||||
conn |> render("new.html", changeset: changeset, invite_token: invite_token)
|
conn |> render("new.html", changeset: changeset, invite_token: invite_token)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -44,7 +44,7 @@ defmodule CanneryWeb.AmmoGroupLive.FormComponent do
|
|||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("validate", %{"ammo_group" => ammo_group_params}, socket) do
|
def handle_event("validate", %{"ammo_group" => ammo_group_params}, socket) do
|
||||||
{:noreply, socket |> assign_changeset(ammo_group_params)}
|
{:noreply, socket |> assign_changeset(ammo_group_params, :validate)}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_event(
|
def handle_event(
|
||||||
@ -56,6 +56,7 @@ defmodule CanneryWeb.AmmoGroupLive.FormComponent do
|
|||||||
end
|
end
|
||||||
|
|
||||||
# HTML Helpers
|
# HTML Helpers
|
||||||
|
|
||||||
@spec container_options([Container.t()]) :: [{String.t(), Container.id()}]
|
@spec container_options([Container.t()]) :: [{String.t(), Container.id()}]
|
||||||
defp container_options(containers) do
|
defp container_options(containers) do
|
||||||
containers |> Enum.map(fn %{id: id, name: name} -> {name, id} end)
|
containers |> Enum.map(fn %{id: id, name: name} -> {name, id} end)
|
||||||
@ -70,35 +71,28 @@ defmodule CanneryWeb.AmmoGroupLive.FormComponent do
|
|||||||
|
|
||||||
defp assign_changeset(
|
defp assign_changeset(
|
||||||
%{assigns: %{action: action, ammo_group: ammo_group, current_user: user}} = socket,
|
%{assigns: %{action: action, ammo_group: ammo_group, current_user: user}} = socket,
|
||||||
ammo_group_params
|
ammo_group_params,
|
||||||
|
changeset_action \\ nil
|
||||||
) do
|
) do
|
||||||
changeset_action =
|
default_action =
|
||||||
cond do
|
case action do
|
||||||
action in [:new, :clone] -> :insert
|
create when create in [:new, :clone] -> :insert
|
||||||
action == :edit -> :update
|
:edit -> :update
|
||||||
end
|
end
|
||||||
|
|
||||||
changeset =
|
changeset =
|
||||||
cond do
|
case default_action do
|
||||||
action in [:new, :clone] ->
|
:insert ->
|
||||||
ammo_type =
|
ammo_type = maybe_get_ammo_type(ammo_group_params, user)
|
||||||
if ammo_group_params |> Map.has_key?("ammo_type_id"),
|
container = maybe_get_container(ammo_group_params, user)
|
||||||
do: ammo_group_params |> Map.get("ammo_type_id") |> Ammo.get_ammo_type!(user),
|
|
||||||
else: nil
|
|
||||||
|
|
||||||
container =
|
|
||||||
if ammo_group_params |> Map.has_key?("container_id"),
|
|
||||||
do: ammo_group_params |> Map.get("container_id") |> Containers.get_container!(user),
|
|
||||||
else: nil
|
|
||||||
|
|
||||||
ammo_group |> AmmoGroup.create_changeset(ammo_type, container, user, ammo_group_params)
|
ammo_group |> AmmoGroup.create_changeset(ammo_type, container, user, ammo_group_params)
|
||||||
|
|
||||||
action == :edit ->
|
:update ->
|
||||||
ammo_group |> AmmoGroup.update_changeset(ammo_group_params, user)
|
ammo_group |> AmmoGroup.update_changeset(ammo_group_params, user)
|
||||||
end
|
end
|
||||||
|
|
||||||
changeset =
|
changeset =
|
||||||
case changeset |> Changeset.apply_action(changeset_action) do
|
case changeset |> Changeset.apply_action(changeset_action || default_action) do
|
||||||
{:ok, _data} -> changeset
|
{:ok, _data} -> changeset
|
||||||
{:error, changeset} -> changeset
|
{:error, changeset} -> changeset
|
||||||
end
|
end
|
||||||
@ -106,6 +100,20 @@ defmodule CanneryWeb.AmmoGroupLive.FormComponent do
|
|||||||
socket |> assign(:changeset, changeset)
|
socket |> assign(:changeset, changeset)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp maybe_get_container(%{"container_id" => container_id}, user)
|
||||||
|
when is_binary(container_id) do
|
||||||
|
container_id |> Containers.get_container!(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_get_container(_params_not_found, _user), do: nil
|
||||||
|
|
||||||
|
defp maybe_get_ammo_type(%{"ammo_type_id" => ammo_type_id}, user)
|
||||||
|
when is_binary(ammo_type_id) do
|
||||||
|
ammo_type_id |> Ammo.get_ammo_type!(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_get_ammo_type(_params_not_found, _user), do: nil
|
||||||
|
|
||||||
defp save_ammo_group(
|
defp save_ammo_group(
|
||||||
%{assigns: %{ammo_group: ammo_group, current_user: current_user, return_to: return_to}} =
|
%{assigns: %{ammo_group: ammo_group, current_user: current_user, return_to: return_to}} =
|
||||||
socket,
|
socket,
|
||||||
@ -146,27 +154,26 @@ defmodule CanneryWeb.AmmoGroupLive.FormComponent do
|
|||||||
multiplier: multiplier
|
multiplier: multiplier
|
||||||
)
|
)
|
||||||
|
|
||||||
{:error, changeset} =
|
save_multiplier_error(socket, changeset, error_msg)
|
||||||
changeset
|
|
||||||
|> Changeset.add_error(:multiplier, error_msg)
|
|
||||||
|> Changeset.apply_action(:insert)
|
|
||||||
|
|
||||||
socket |> assign(:changeset, changeset)
|
|
||||||
|
|
||||||
:error ->
|
:error ->
|
||||||
error_msg = dgettext("errors", "Could not parse number of copies")
|
error_msg = dgettext("errors", "Could not parse number of copies")
|
||||||
|
save_multiplier_error(socket, changeset, error_msg)
|
||||||
{:error, changeset} =
|
|
||||||
changeset
|
|
||||||
|> Changeset.add_error(:multiplier, error_msg)
|
|
||||||
|> Changeset.apply_action(:insert)
|
|
||||||
|
|
||||||
socket |> assign(:changeset, changeset)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec save_multiplier_error(Socket.t(), Changeset.t(), String.t()) :: Socket.t()
|
||||||
|
defp save_multiplier_error(socket, changeset, error_msg) do
|
||||||
|
{:error, changeset} =
|
||||||
|
changeset
|
||||||
|
|> Changeset.add_error(:multiplier, error_msg)
|
||||||
|
|> Changeset.apply_action(:insert)
|
||||||
|
|
||||||
|
socket |> assign(:changeset, changeset)
|
||||||
|
end
|
||||||
|
|
||||||
defp create_multiple(
|
defp create_multiple(
|
||||||
%{assigns: %{current_user: current_user, return_to: return_to}} = socket,
|
%{assigns: %{current_user: current_user, return_to: return_to}} = socket,
|
||||||
ammo_group_params,
|
ammo_group_params,
|
||||||
|
@ -49,8 +49,10 @@
|
|||||||
|
|
||||||
<%= label(f, :notes, gettext("Notes"), class: "title text-lg text-primary-600") %>
|
<%= label(f, :notes, gettext("Notes"), class: "title text-lg text-primary-600") %>
|
||||||
<%= textarea(f, :notes,
|
<%= textarea(f, :notes,
|
||||||
|
id: "ammo-group-form-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",
|
||||||
|
phx_update: "ignore"
|
||||||
) %>
|
) %>
|
||||||
<%= error_tag(f, :notes, "col-span-3 text-center") %>
|
<%= error_tag(f, :notes, "col-span-3 text-center") %>
|
||||||
|
|
||||||
|
@ -91,7 +91,6 @@ defmodule CanneryWeb.AmmoGroupLive.Index do
|
|||||||
{:noreply, socket |> put_flash(:info, prompt) |> display_ammo_groups()}
|
{:noreply, socket |> put_flash(:info, prompt) |> display_ammo_groups()}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event(
|
def handle_event(
|
||||||
"toggle_staged",
|
"toggle_staged",
|
||||||
%{"ammo_group_id" => id},
|
%{"ammo_group_id" => id},
|
||||||
@ -105,12 +104,10 @@ defmodule CanneryWeb.AmmoGroupLive.Index do
|
|||||||
{:noreply, socket |> display_ammo_groups()}
|
{:noreply, socket |> display_ammo_groups()}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event("toggle_show_used", _params, %{assigns: %{show_used: show_used}} = socket) do
|
def handle_event("toggle_show_used", _params, %{assigns: %{show_used: show_used}} = socket) do
|
||||||
{:noreply, socket |> assign(:show_used, !show_used) |> display_ammo_groups()}
|
{:noreply, socket |> assign(:show_used, !show_used) |> display_ammo_groups()}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event("search", %{"search" => %{"search_term" => ""}}, socket) do
|
def handle_event("search", %{"search" => %{"search_term" => ""}}, socket) do
|
||||||
{:noreply, socket |> push_patch(to: Routes.ammo_group_index_path(Endpoint, :index))}
|
{:noreply, socket |> push_patch(to: Routes.ammo_group_index_path(Endpoint, :index))}
|
||||||
end
|
end
|
||||||
|
@ -45,15 +45,16 @@
|
|||||||
<div class="w-full flex flex-col sm:flex-row justify-center items-center space-y-4 sm:space-y-0 sm:space-x-4 max-w-xl">
|
<div class="w-full flex flex-col sm:flex-row justify-center items-center space-y-4 sm:space-y-0 sm:space-x-4 max-w-xl">
|
||||||
<.form
|
<.form
|
||||||
:let={f}
|
:let={f}
|
||||||
for={:search}
|
for={%{}}
|
||||||
|
as={:search}
|
||||||
phx-change="search"
|
phx-change="search"
|
||||||
phx-submit="search"
|
phx-submit="search"
|
||||||
class="grow self-stretch flex flex-col items-stretch"
|
class="grow self-stretch flex flex-col items-stretch"
|
||||||
data-qa="ammo_group_search"
|
|
||||||
>
|
>
|
||||||
<%= text_input(f, :search_term,
|
<%= text_input(f, :search_term,
|
||||||
class: "input input-primary",
|
class: "input input-primary",
|
||||||
value: @search,
|
value: @search,
|
||||||
|
role: "search",
|
||||||
phx_debounce: 300,
|
phx_debounce: 300,
|
||||||
placeholder: gettext("Search ammo")
|
placeholder: gettext("Search ammo")
|
||||||
) %>
|
) %>
|
||||||
@ -91,7 +92,9 @@
|
|||||||
phx-click="toggle_staged"
|
phx-click="toggle_staged"
|
||||||
phx-value-ammo_group_id={ammo_group.id}
|
phx-value-ammo_group_id={ammo_group.id}
|
||||||
>
|
>
|
||||||
<%= if ammo_group.staged, do: gettext("Unstage"), else: gettext("Stage") %>
|
<%= if ammo_group.staged,
|
||||||
|
do: dgettext("actions", "Unstage"),
|
||||||
|
else: dgettext("actions", "Stage") %>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<.link
|
<.link
|
||||||
@ -102,7 +105,7 @@
|
|||||||
</.link>
|
</.link>
|
||||||
</div>
|
</div>
|
||||||
</:range>
|
</:range>
|
||||||
<:container :let={%{container: %{name: container_name} = container} = ammo_group}>
|
<:container :let={{ammo_group, %{name: container_name} = container}}>
|
||||||
<div class="min-w-20 py-2 px-4 h-full flex flew-wrap justify-center items-center">
|
<div class="min-w-20 py-2 px-4 h-full flex flew-wrap justify-center items-center">
|
||||||
<.link
|
<.link
|
||||||
navigate={Routes.container_show_path(Endpoint, :show, container)}
|
navigate={Routes.container_show_path(Endpoint, :show, container)}
|
||||||
@ -115,16 +118,20 @@
|
|||||||
patch={Routes.ammo_group_index_path(Endpoint, :move, ammo_group)}
|
patch={Routes.ammo_group_index_path(Endpoint, :move, ammo_group)}
|
||||||
class="mx-2 my-1 text-sm btn btn-primary"
|
class="mx-2 my-1 text-sm btn btn-primary"
|
||||||
>
|
>
|
||||||
<%= gettext("Move ammo") %>
|
<%= dgettext("actions", "Move ammo") %>
|
||||||
</.link>
|
</.link>
|
||||||
</div>
|
</div>
|
||||||
</:container>
|
</:container>
|
||||||
<:actions :let={ammo_group}>
|
<:actions :let={%{count: ammo_group_count} = ammo_group}>
|
||||||
<div class="py-2 px-4 h-full space-x-4 flex justify-center items-center">
|
<div class="py-2 px-4 h-full space-x-4 flex justify-center items-center">
|
||||||
<.link
|
<.link
|
||||||
navigate={Routes.ammo_group_show_path(Endpoint, :show, ammo_group)}
|
navigate={Routes.ammo_group_show_path(Endpoint, :show, ammo_group)}
|
||||||
class="text-primary-600 link"
|
class="text-primary-600 link"
|
||||||
data-qa={"view-#{ammo_group.id}"}
|
aria-label={
|
||||||
|
dgettext("actions", "View ammo group of %{ammo_group_count} bullets",
|
||||||
|
ammo_group_count: ammo_group_count
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-eye"></i>
|
<i class="fa-fw fa-lg fas fa-eye"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -132,7 +139,11 @@
|
|||||||
<.link
|
<.link
|
||||||
patch={Routes.ammo_group_index_path(Endpoint, :edit, ammo_group)}
|
patch={Routes.ammo_group_index_path(Endpoint, :edit, ammo_group)}
|
||||||
class="text-primary-600 link"
|
class="text-primary-600 link"
|
||||||
data-qa={"edit-#{ammo_group.id}"}
|
aria-label={
|
||||||
|
dgettext("actions", "Edit ammo group of %{ammo_group_count} bullets",
|
||||||
|
ammo_group_count: ammo_group_count
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-edit"></i>
|
<i class="fa-fw fa-lg fas fa-edit"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -140,7 +151,11 @@
|
|||||||
<.link
|
<.link
|
||||||
patch={Routes.ammo_group_index_path(Endpoint, :clone, ammo_group)}
|
patch={Routes.ammo_group_index_path(Endpoint, :clone, ammo_group)}
|
||||||
class="text-primary-600 link"
|
class="text-primary-600 link"
|
||||||
data-qa={"clone-#{ammo_group.id}"}
|
aria-label={
|
||||||
|
dgettext("actions", "Clone ammo group of %{ammo_group_count} bullets",
|
||||||
|
ammo_group_count: ammo_group_count
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-copy"></i>
|
<i class="fa-fw fa-lg fas fa-copy"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -151,7 +166,11 @@
|
|||||||
phx-click="delete"
|
phx-click="delete"
|
||||||
phx-value-id={ammo_group.id}
|
phx-value-id={ammo_group.id}
|
||||||
data-confirm={dgettext("prompts", "Are you sure you want to delete this ammo?")}
|
data-confirm={dgettext("prompts", "Are you sure you want to delete this ammo?")}
|
||||||
data-qa={"delete-#{ammo_group.id}"}
|
aria-label={
|
||||||
|
dgettext("actions", "Delete ammo group of %{ammo_group_count} bullets",
|
||||||
|
ammo_group_count: ammo_group_count
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-trash"></i>
|
<i class="fa-fw fa-lg fas fa-trash"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -161,8 +180,8 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%= cond do %>
|
<%= case @live_action do %>
|
||||||
<% @live_action in [:new, :edit, :clone] -> %>
|
<% create when create in [:new, :edit, :clone] -> %>
|
||||||
<.modal return_to={Routes.ammo_group_index_path(Endpoint, :index)}>
|
<.modal return_to={Routes.ammo_group_index_path(Endpoint, :index)}>
|
||||||
<.live_component
|
<.live_component
|
||||||
module={CanneryWeb.AmmoGroupLive.FormComponent}
|
module={CanneryWeb.AmmoGroupLive.FormComponent}
|
||||||
@ -174,7 +193,7 @@
|
|||||||
current_user={@current_user}
|
current_user={@current_user}
|
||||||
/>
|
/>
|
||||||
</.modal>
|
</.modal>
|
||||||
<% @live_action == :add_shot_group -> %>
|
<% :add_shot_group -> %>
|
||||||
<.modal return_to={Routes.ammo_group_index_path(Endpoint, :index)}>
|
<.modal return_to={Routes.ammo_group_index_path(Endpoint, :index)}>
|
||||||
<.live_component
|
<.live_component
|
||||||
module={CanneryWeb.Components.AddShotGroupComponent}
|
module={CanneryWeb.Components.AddShotGroupComponent}
|
||||||
@ -186,7 +205,7 @@
|
|||||||
current_user={@current_user}
|
current_user={@current_user}
|
||||||
/>
|
/>
|
||||||
</.modal>
|
</.modal>
|
||||||
<% @live_action == :move -> %>
|
<% :move -> %>
|
||||||
<.modal return_to={Routes.ammo_group_index_path(Endpoint, :index)}>
|
<.modal return_to={Routes.ammo_group_index_path(Endpoint, :index)}>
|
||||||
<.live_component
|
<.live_component
|
||||||
module={CanneryWeb.Components.MoveAmmoGroupComponent}
|
module={CanneryWeb.Components.MoveAmmoGroupComponent}
|
||||||
@ -198,6 +217,5 @@
|
|||||||
current_user={@current_user}
|
current_user={@current_user}
|
||||||
/>
|
/>
|
||||||
</.modal>
|
</.modal>
|
||||||
<% true -> %>
|
<% _ -> %>
|
||||||
<%= nil %>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
|
@ -4,8 +4,9 @@ defmodule CanneryWeb.AmmoGroupLive.Show do
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
use CanneryWeb, :live_view
|
use CanneryWeb, :live_view
|
||||||
import CanneryWeb.Components.ContainerCard
|
alias Cannery.{ActivityLog, ActivityLog.ShotGroup}
|
||||||
alias Cannery.{ActivityLog, ActivityLog.ShotGroup, Ammo, Ammo.AmmoGroup, Repo}
|
alias Cannery.{Ammo, Ammo.AmmoGroup}
|
||||||
|
alias Cannery.{ComparableDate, Containers}
|
||||||
alias CanneryWeb.Endpoint
|
alias CanneryWeb.Endpoint
|
||||||
alias Phoenix.LiveView.Socket
|
alias Phoenix.LiveView.Socket
|
||||||
|
|
||||||
@ -28,7 +29,6 @@ defmodule CanneryWeb.AmmoGroupLive.Show do
|
|||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_params(%{"id" => id}, _url, %{assigns: %{live_action: live_action}} = socket) do
|
def handle_params(%{"id" => id}, _url, %{assigns: %{live_action: live_action}} = socket) do
|
||||||
socket =
|
socket =
|
||||||
socket
|
socket
|
||||||
@ -58,7 +58,6 @@ defmodule CanneryWeb.AmmoGroupLive.Show do
|
|||||||
{:noreply, socket |> put_flash(:info, prompt) |> push_navigate(to: redirect_to)}
|
{:noreply, socket |> put_flash(:info, prompt) |> push_navigate(to: redirect_to)}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event(
|
def handle_event(
|
||||||
"toggle_staged",
|
"toggle_staged",
|
||||||
_params,
|
_params,
|
||||||
@ -70,7 +69,6 @@ defmodule CanneryWeb.AmmoGroupLive.Show do
|
|||||||
{:noreply, socket |> display_ammo_group(ammo_group)}
|
{:noreply, socket |> display_ammo_group(ammo_group)}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event(
|
def handle_event(
|
||||||
"delete_shot_group",
|
"delete_shot_group",
|
||||||
%{"id" => id},
|
%{"id" => id},
|
||||||
@ -85,30 +83,45 @@ defmodule CanneryWeb.AmmoGroupLive.Show do
|
|||||||
end
|
end
|
||||||
|
|
||||||
@spec display_ammo_group(Socket.t(), AmmoGroup.t() | AmmoGroup.id()) :: Socket.t()
|
@spec display_ammo_group(Socket.t(), AmmoGroup.t() | AmmoGroup.id()) :: Socket.t()
|
||||||
defp display_ammo_group(socket, %AmmoGroup{} = ammo_group) do
|
defp display_ammo_group(
|
||||||
ammo_group = ammo_group |> Repo.preload([:container, :ammo_type, :shot_groups], force: true)
|
%{assigns: %{current_user: current_user}} = socket,
|
||||||
|
%AmmoGroup{container_id: container_id} = ammo_group
|
||||||
|
) do
|
||||||
columns = [
|
columns = [
|
||||||
%{label: gettext("Rounds shot"), key: :count},
|
%{label: gettext("Rounds shot"), key: :count},
|
||||||
%{label: gettext("Notes"), key: :notes},
|
%{label: gettext("Notes"), key: :notes},
|
||||||
%{label: gettext("Date"), key: :date},
|
%{label: gettext("Date"), key: :date, type: ComparableDate},
|
||||||
%{label: nil, key: :actions, sortable: false}
|
%{label: gettext("Actions"), key: :actions, sortable: false}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
shot_groups = ActivityLog.list_shot_groups_for_ammo_group(ammo_group, current_user)
|
||||||
|
|
||||||
rows =
|
rows =
|
||||||
ammo_group.shot_groups
|
shot_groups
|
||||||
|> Enum.map(fn shot_group ->
|
|> Enum.map(fn shot_group ->
|
||||||
ammo_group |> get_table_row_for_shot_group(shot_group, columns)
|
ammo_group |> get_table_row_for_shot_group(shot_group, columns)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
socket |> assign(ammo_group: ammo_group, columns: columns, rows: rows)
|
socket
|
||||||
|
|> assign(
|
||||||
|
ammo_group: ammo_group,
|
||||||
|
original_count: Ammo.get_original_count(ammo_group, current_user),
|
||||||
|
percentage_remaining: Ammo.get_percentage_remaining(ammo_group, current_user),
|
||||||
|
container: container_id && Containers.get_container!(container_id, current_user),
|
||||||
|
shot_groups: shot_groups,
|
||||||
|
columns: columns,
|
||||||
|
rows: rows
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp display_ammo_group(%{assigns: %{current_user: current_user}} = socket, id),
|
defp display_ammo_group(%{assigns: %{current_user: current_user}} = socket, id),
|
||||||
do: display_ammo_group(socket, Ammo.get_ammo_group!(id, current_user))
|
do: display_ammo_group(socket, Ammo.get_ammo_group!(id, current_user))
|
||||||
|
|
||||||
|
@spec display_currency(float()) :: String.t()
|
||||||
|
defp display_currency(float), do: :erlang.float_to_binary(float, decimals: 2)
|
||||||
|
|
||||||
@spec get_table_row_for_shot_group(AmmoGroup.t(), ShotGroup.t(), [map()]) :: map()
|
@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
|
defp get_table_row_for_shot_group(ammo_group, %{id: id, date: date} = shot_group, columns) do
|
||||||
assigns = %{ammo_group: ammo_group, shot_group: shot_group}
|
assigns = %{ammo_group: ammo_group, shot_group: shot_group}
|
||||||
|
|
||||||
columns
|
columns
|
||||||
@ -116,11 +129,11 @@ defmodule CanneryWeb.AmmoGroupLive.Show do
|
|||||||
value =
|
value =
|
||||||
case key do
|
case key do
|
||||||
:date ->
|
:date ->
|
||||||
assigns = %{date: date}
|
assigns = %{id: id, date: date}
|
||||||
|
|
||||||
{date,
|
{date,
|
||||||
~H"""
|
~H"""
|
||||||
<.date date={@date} />
|
<.date id={"#{@id}-date"} date={@date} />
|
||||||
"""}
|
"""}
|
||||||
|
|
||||||
:actions ->
|
:actions ->
|
||||||
@ -129,7 +142,11 @@ defmodule CanneryWeb.AmmoGroupLive.Show do
|
|||||||
<.link
|
<.link
|
||||||
patch={Routes.ammo_group_show_path(Endpoint, :edit_shot_group, @ammo_group, @shot_group)}
|
patch={Routes.ammo_group_show_path(Endpoint, :edit_shot_group, @ammo_group, @shot_group)}
|
||||||
class="text-primary-600 link"
|
class="text-primary-600 link"
|
||||||
data-qa={"edit-#{@shot_group.id}"}
|
aria-label={
|
||||||
|
dgettext("actions", "Edit shot group of %{shot_group_count} shots",
|
||||||
|
shot_group_count: @shot_group.count
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-edit"></i>
|
<i class="fa-fw fa-lg fas fa-edit"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -140,7 +157,11 @@ defmodule CanneryWeb.AmmoGroupLive.Show do
|
|||||||
phx-click="delete_shot_group"
|
phx-click="delete_shot_group"
|
||||||
phx-value-id={@shot_group.id}
|
phx-value-id={@shot_group.id}
|
||||||
data-confirm={dgettext("prompts", "Are you sure you want to delete this shot record?")}
|
data-confirm={dgettext("prompts", "Are you sure you want to delete this shot record?")}
|
||||||
data-qa={"delete-#{@shot_group.id}"}
|
aria-label={
|
||||||
|
dgettext("actions", "Delete shot record of %{shot_group_count} shots",
|
||||||
|
shot_group_count: @shot_group.count
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-trash"></i>
|
<i class="fa-fw fa-lg fas fa-trash"></i>
|
||||||
</.link>
|
</.link>
|
||||||
|
@ -11,12 +11,12 @@
|
|||||||
|
|
||||||
<span class="rounded-lg title text-lg">
|
<span class="rounded-lg title text-lg">
|
||||||
<%= gettext("Original count:") %>
|
<%= gettext("Original count:") %>
|
||||||
<%= Ammo.get_original_count(@ammo_group) %>
|
<%= @original_count %>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="rounded-lg title text-lg">
|
<span class="rounded-lg title text-lg">
|
||||||
<%= gettext("Percentage left:") %>
|
<%= gettext("Percentage left:") %>
|
||||||
<%= gettext("%{percentage}%", percentage: @ammo_group |> Ammo.get_percentage_remaining()) %>
|
<%= gettext("%{percentage}%", percentage: @percentage_remaining) %>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<%= if @ammo_group.notes do %>
|
<%= if @ammo_group.notes do %>
|
||||||
@ -28,23 +28,19 @@
|
|||||||
|
|
||||||
<span class="rounded-lg title text-lg">
|
<span class="rounded-lg title text-lg">
|
||||||
<%= gettext("Purchased on:") %>
|
<%= gettext("Purchased on:") %>
|
||||||
<.date date={@ammo_group.purchased_on} />
|
<.date id={"#{@ammo_group.id}-purchased-on"} date={@ammo_group.purchased_on} />
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<%= 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("Original cost:") %>
|
<%= gettext("Original cost:") %>
|
||||||
<%= gettext("$%{amount}",
|
<%= gettext("$%{amount}", amount: display_currency(@ammo_group.price_paid)) %>
|
||||||
amount: @ammo_group.price_paid |> :erlang.float_to_binary(decimals: 2)
|
|
||||||
) %>
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="rounded-lg title text-lg">
|
<span class="rounded-lg title text-lg">
|
||||||
<%= gettext("Current value:") %>
|
<%= gettext("Current value:") %>
|
||||||
<%= gettext("$%{amount}",
|
<%= gettext("$%{amount}",
|
||||||
amount:
|
amount: display_currency(@ammo_group.price_paid * @percentage_remaining / 100)
|
||||||
(@ammo_group.price_paid * Ammo.get_percentage_remaining(@ammo_group) / 100)
|
|
||||||
|> :erlang.float_to_binary(decimals: 2)
|
|
||||||
) %>
|
) %>
|
||||||
</span>
|
</span>
|
||||||
<% end %>
|
<% end %>
|
||||||
@ -55,7 +51,6 @@
|
|||||||
<.link
|
<.link
|
||||||
navigate={Routes.ammo_type_show_path(Endpoint, :show, @ammo_group.ammo_type)}
|
navigate={Routes.ammo_type_show_path(Endpoint, :show, @ammo_group.ammo_type)}
|
||||||
class="mx-4 my-2 btn btn-primary"
|
class="mx-4 my-2 btn btn-primary"
|
||||||
data-qa="details"
|
|
||||||
>
|
>
|
||||||
<%= dgettext("actions", "View in Catalog") %>
|
<%= dgettext("actions", "View in Catalog") %>
|
||||||
</.link>
|
</.link>
|
||||||
@ -63,7 +58,11 @@
|
|||||||
<.link
|
<.link
|
||||||
patch={Routes.ammo_group_show_path(Endpoint, :edit, @ammo_group)}
|
patch={Routes.ammo_group_show_path(Endpoint, :edit, @ammo_group)}
|
||||||
class="mx-4 my-2 text-primary-600 link"
|
class="mx-4 my-2 text-primary-600 link"
|
||||||
data-qa="edit"
|
aria-label={
|
||||||
|
dgettext("actions", "Edit ammo group of %{ammo_group_count} bullets",
|
||||||
|
ammo_group_count: @ammo_group.count
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-edit"></i>
|
<i class="fa-fw fa-lg fas fa-edit"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -73,7 +72,11 @@
|
|||||||
class="mx-4 my-2 text-primary-600 link"
|
class="mx-4 my-2 text-primary-600 link"
|
||||||
phx-click="delete"
|
phx-click="delete"
|
||||||
data-confirm={dgettext("prompts", "Are you sure you want to delete this ammo?")}
|
data-confirm={dgettext("prompts", "Are you sure you want to delete this ammo?")}
|
||||||
data-qa="delete"
|
aria-label={
|
||||||
|
dgettext("actions", "Delete ammo group of %{ammo_group_count} bullets",
|
||||||
|
ammo_group_count: @ammo_group.count
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-trash"></i>
|
<i class="fa-fw fa-lg fas fa-trash"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -89,9 +92,8 @@
|
|||||||
<.link
|
<.link
|
||||||
patch={Routes.ammo_group_show_path(Endpoint, :move, @ammo_group)}
|
patch={Routes.ammo_group_show_path(Endpoint, :move, @ammo_group)}
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
data-qa="move"
|
|
||||||
>
|
>
|
||||||
<%= dgettext("actions", "Move containers") %>
|
<%= dgettext("actions", "Move ammo") %>
|
||||||
</.link>
|
</.link>
|
||||||
|
|
||||||
<.link
|
<.link
|
||||||
@ -106,18 +108,18 @@
|
|||||||
<hr class="mb-4 w-full" />
|
<hr class="mb-4 w-full" />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<%= if @ammo_group.container do %>
|
<%= if @container do %>
|
||||||
<h1 class="mb-4 px-4 py-2 text-center rounded-lg title text-xl">
|
<h1 class="mb-4 px-4 py-2 text-center rounded-lg title text-xl">
|
||||||
<%= gettext("Stored in") %>
|
<%= gettext("Stored in") %>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<.container_card container={@ammo_group.container} />
|
<.container_card container={@container} current_user={@current_user} />
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= gettext("This ammo is not in a container") %>
|
<%= gettext("This ammo is not in a container") %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%= unless @ammo_group.shot_groups |> Enum.empty?() do %>
|
<%= unless @shot_groups |> Enum.empty?() do %>
|
||||||
<hr class="mb-4 w-full" />
|
<hr class="mb-4 w-full" />
|
||||||
|
|
||||||
<h1 class="mb-4 px-4 py-2 text-center rounded-lg title text-xl">
|
<h1 class="mb-4 px-4 py-2 text-center rounded-lg title text-xl">
|
||||||
|
@ -35,15 +35,18 @@ defmodule CanneryWeb.AmmoTypeLive.FormComponent do
|
|||||||
ammo_type_params
|
ammo_type_params
|
||||||
) do
|
) do
|
||||||
changeset_action =
|
changeset_action =
|
||||||
cond do
|
case action do
|
||||||
action in [:new, :clone] -> :insert
|
create when create in [:new, :clone] -> :insert
|
||||||
action == :edit -> :update
|
:edit -> :update
|
||||||
end
|
end
|
||||||
|
|
||||||
changeset =
|
changeset =
|
||||||
cond do
|
case action do
|
||||||
action in [:new, :clone] -> ammo_type |> AmmoType.create_changeset(user, ammo_type_params)
|
create when create in [:new, :clone] ->
|
||||||
action == :edit -> ammo_type |> AmmoType.update_changeset(ammo_type_params)
|
ammo_type |> AmmoType.create_changeset(user, ammo_type_params)
|
||||||
|
|
||||||
|
:edit ->
|
||||||
|
ammo_type |> AmmoType.update_changeset(ammo_type_params)
|
||||||
end
|
end
|
||||||
|
|
||||||
changeset =
|
changeset =
|
||||||
|
@ -24,17 +24,19 @@
|
|||||||
|
|
||||||
<%= label(f, :desc, gettext("Description"), class: "title text-lg text-primary-600") %>
|
<%= label(f, :desc, gettext("Description"), class: "title text-lg text-primary-600") %>
|
||||||
<%= textarea(f, :desc,
|
<%= textarea(f, :desc,
|
||||||
|
id: "ammo-type-form-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",
|
||||||
|
phx_update: "ignore"
|
||||||
) %>
|
) %>
|
||||||
<%= error_tag(f, :desc, "col-span-3 text-center") %>
|
<%= error_tag(f, :desc, "col-span-3 text-center") %>
|
||||||
|
|
||||||
<a
|
<.link
|
||||||
href="https://en.wikipedia.org/wiki/Bullet#Abbreviations"
|
href="https://shootersreference.com/reloadingdata/bullet_abbreviations/"
|
||||||
class="col-span-3 text-center link title text-md text-primary-600"
|
class="col-span-3 text-center link title text-md text-primary-600"
|
||||||
>
|
>
|
||||||
<%= gettext("Example bullet type abbreviations") %>
|
<%= gettext("Example bullet type abbreviations") %>
|
||||||
</a>
|
</.link>
|
||||||
<%= label(f, :bullet_type, gettext("Bullet type"), class: "title text-lg text-primary-600") %>
|
<%= label(f, :bullet_type, gettext("Bullet type"), class: "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",
|
||||||
@ -52,14 +54,14 @@
|
|||||||
<%= label(f, :cartridge, gettext("Cartridge"), class: "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: gettext("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: "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: gettext(".223")
|
||||||
) %>
|
) %>
|
||||||
<%= error_tag(f, :caliber, "col-span-3 text-center") %>
|
<%= error_tag(f, :caliber, "col-span-3 text-center") %>
|
||||||
|
|
||||||
@ -112,21 +114,21 @@
|
|||||||
<%= label(f, :pressure, gettext("Pressure"), class: "title text-lg text-primary-600") %>
|
<%= label(f, :pressure, gettext("Pressure"), class: "title text-lg text-primary-600") %>
|
||||||
<%= text_input(f, :pressure,
|
<%= text_input(f, :pressure,
|
||||||
class: "text-center col-span-2 input input-primary",
|
class: "text-center col-span-2 input input-primary",
|
||||||
placeholder: "+P"
|
placeholder: gettext("+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"), class: "title text-lg text-primary-600") %>
|
<%= label(f, :primer_type, gettext("Primer type"), class: "title text-lg text-primary-600") %>
|
||||||
<%= text_input(f, :primer_type,
|
<%= text_input(f, :primer_type,
|
||||||
class: "text-center col-span-2 input input-primary",
|
class: "text-center col-span-2 input input-primary",
|
||||||
placeholder: "Boxer"
|
placeholder: gettext("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"), class: "title text-lg text-primary-600") %>
|
<%= label(f, :firing_type, gettext("Firing type"), class: "title text-lg text-primary-600") %>
|
||||||
<%= text_input(f, :firing_type,
|
<%= text_input(f, :firing_type,
|
||||||
class: "text-center col-span-2 input input-primary",
|
class: "text-center col-span-2 input input-primary",
|
||||||
placeholder: "Centerfire"
|
placeholder: gettext("Centerfire")
|
||||||
) %>
|
) %>
|
||||||
<%= error_tag(f, :firing_type, "col-span-3 text-center") %>
|
<%= error_tag(f, :firing_type, "col-span-3 text-center") %>
|
||||||
|
|
||||||
|
@ -69,25 +69,21 @@ defmodule CanneryWeb.AmmoTypeLive.Index do
|
|||||||
@impl true
|
@impl true
|
||||||
def handle_event("delete", %{"id" => id}, %{assigns: %{current_user: current_user}} = socket) do
|
def handle_event("delete", %{"id" => id}, %{assigns: %{current_user: current_user}} = socket) do
|
||||||
%{name: name} = Ammo.get_ammo_type!(id, current_user) |> Ammo.delete_ammo_type!(current_user)
|
%{name: name} = Ammo.get_ammo_type!(id, current_user) |> Ammo.delete_ammo_type!(current_user)
|
||||||
|
|
||||||
prompt = dgettext("prompts", "%{name} deleted succesfully", name: name)
|
prompt = dgettext("prompts", "%{name} deleted succesfully", name: name)
|
||||||
|
|
||||||
{:noreply, socket |> put_flash(:info, prompt) |> list_ammo_types()}
|
{:noreply, socket |> put_flash(:info, prompt) |> list_ammo_types()}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event("toggle_show_used", _params, %{assigns: %{show_used: show_used}} = socket) do
|
def handle_event("toggle_show_used", _params, %{assigns: %{show_used: show_used}} = socket) do
|
||||||
{:noreply, socket |> assign(:show_used, !show_used) |> list_ammo_types()}
|
{:noreply, socket |> assign(:show_used, !show_used) |> list_ammo_types()}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event("search", %{"search" => %{"search_term" => ""}}, socket) do
|
def handle_event("search", %{"search" => %{"search_term" => ""}}, socket) do
|
||||||
{:noreply, socket |> push_patch(to: Routes.ammo_type_index_path(Endpoint, :index))}
|
{:noreply, socket |> push_patch(to: Routes.ammo_type_index_path(Endpoint, :index))}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_event("search", %{"search" => %{"search_term" => search_term}}, socket) do
|
def handle_event("search", %{"search" => %{"search_term" => search_term}}, socket) do
|
||||||
{:noreply,
|
search_path = Routes.ammo_type_index_path(Endpoint, :search, search_term)
|
||||||
socket |> push_patch(to: Routes.ammo_type_index_path(Endpoint, :search, search_term))}
|
{:noreply, socket |> push_patch(to: search_path)}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp list_ammo_types(%{assigns: %{search: search, current_user: current_user}} = socket) do
|
defp list_ammo_types(%{assigns: %{search: search, current_user: current_user}} = socket) do
|
||||||
|
@ -20,15 +20,16 @@
|
|||||||
<div class="w-full flex flex-col sm:flex-row justify-center items-center space-y-4 sm:space-y-0 sm:space-x-4 max-w-xl">
|
<div class="w-full flex flex-col sm:flex-row justify-center items-center space-y-4 sm:space-y-0 sm:space-x-4 max-w-xl">
|
||||||
<.form
|
<.form
|
||||||
:let={f}
|
:let={f}
|
||||||
for={:search}
|
for={%{}}
|
||||||
|
as={:search}
|
||||||
phx-change="search"
|
phx-change="search"
|
||||||
phx-submit="search"
|
phx-submit="search"
|
||||||
class="grow self-stretch flex flex-col items-stretch"
|
class="grow self-stretch flex flex-col items-stretch"
|
||||||
data-qa="ammo_type_search"
|
|
||||||
>
|
>
|
||||||
<%= text_input(f, :search_term,
|
<%= text_input(f, :search_term,
|
||||||
class: "input input-primary",
|
class: "input input-primary",
|
||||||
value: @search,
|
value: @search,
|
||||||
|
role: "search",
|
||||||
phx_debounce: 300,
|
phx_debounce: 300,
|
||||||
placeholder: gettext("Search catalog")
|
placeholder: gettext("Search catalog")
|
||||||
) %>
|
) %>
|
||||||
@ -60,7 +61,9 @@
|
|||||||
<.link
|
<.link
|
||||||
navigate={Routes.ammo_type_show_path(Endpoint, :show, ammo_type)}
|
navigate={Routes.ammo_type_show_path(Endpoint, :show, ammo_type)}
|
||||||
class="text-primary-600 link"
|
class="text-primary-600 link"
|
||||||
data-qa={"view-#{ammo_type.id}"}
|
aria-label={
|
||||||
|
dgettext("actions", "View %{ammo_type_name}", ammo_type_name: ammo_type.name)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-eye"></i>
|
<i class="fa-fw fa-lg fas fa-eye"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -68,7 +71,9 @@
|
|||||||
<.link
|
<.link
|
||||||
patch={Routes.ammo_type_index_path(Endpoint, :edit, ammo_type)}
|
patch={Routes.ammo_type_index_path(Endpoint, :edit, ammo_type)}
|
||||||
class="text-primary-600 link"
|
class="text-primary-600 link"
|
||||||
data-qa={"edit-#{ammo_type.id}"}
|
aria-label={
|
||||||
|
dgettext("actions", "Edit %{ammo_type_name}", ammo_type_name: ammo_type.name)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-edit"></i>
|
<i class="fa-fw fa-lg fas fa-edit"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -76,7 +81,9 @@
|
|||||||
<.link
|
<.link
|
||||||
patch={Routes.ammo_type_index_path(Endpoint, :clone, ammo_type)}
|
patch={Routes.ammo_type_index_path(Endpoint, :clone, ammo_type)}
|
||||||
class="text-primary-600 link"
|
class="text-primary-600 link"
|
||||||
data-qa={"clone-#{ammo_type.id}"}
|
aria-label={
|
||||||
|
dgettext("actions", "Clone %{ammo_type_name}", ammo_type_name: ammo_type.name)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-copy"></i>
|
<i class="fa-fw fa-lg fas fa-copy"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -93,7 +100,9 @@
|
|||||||
name: ammo_type.name
|
name: ammo_type.name
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
data-qa={"delete-#{ammo_type.id}"}
|
aria-label={
|
||||||
|
dgettext("actions", "Delete %{ammo_type_name}", ammo_type_name: ammo_type.name)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-lg fas fa-trash"></i>
|
<i class="fa-lg fas fa-trash"></i>
|
||||||
</.link>
|
</.link>
|
||||||
|
@ -4,8 +4,7 @@ defmodule CanneryWeb.AmmoTypeLive.Show do
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
use CanneryWeb, :live_view
|
use CanneryWeb, :live_view
|
||||||
import CanneryWeb.Components.AmmoGroupCard
|
alias Cannery.{ActivityLog, Ammo, Ammo.AmmoType}
|
||||||
alias Cannery.{Ammo, Ammo.AmmoType}
|
|
||||||
alias CanneryWeb.Endpoint
|
alias CanneryWeb.Endpoint
|
||||||
|
|
||||||
@fields_list [
|
@fields_list [
|
||||||
@ -58,12 +57,10 @@ defmodule CanneryWeb.AmmoTypeLive.Show do
|
|||||||
{:noreply, socket |> put_flash(:info, prompt) |> push_navigate(to: redirect_to)}
|
{:noreply, socket |> put_flash(:info, prompt) |> push_navigate(to: redirect_to)}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event("toggle_show_used", _params, %{assigns: %{show_used: show_used}} = socket) do
|
def handle_event("toggle_show_used", _params, %{assigns: %{show_used: show_used}} = socket) do
|
||||||
{:noreply, socket |> assign(:show_used, !show_used) |> display_ammo_type()}
|
{:noreply, socket |> assign(:show_used, !show_used) |> display_ammo_type()}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event(
|
def handle_event(
|
||||||
"toggle_table",
|
"toggle_table",
|
||||||
_params,
|
_params,
|
||||||
@ -94,12 +91,42 @@ defmodule CanneryWeb.AmmoTypeLive.Show do
|
|||||||
ammo_type |> Map.get(field) != default_value
|
ammo_type |> Map.get(field) != default_value
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
ammo_groups = ammo_type |> Ammo.list_ammo_groups_for_type(current_user, show_used)
|
||||||
|
|
||||||
|
[
|
||||||
|
original_counts,
|
||||||
|
used_packs_count,
|
||||||
|
historical_packs_count,
|
||||||
|
used_rounds,
|
||||||
|
historical_round_count
|
||||||
|
] =
|
||||||
|
if show_used do
|
||||||
|
[
|
||||||
|
ammo_groups |> Ammo.get_original_counts(current_user),
|
||||||
|
ammo_type |> Ammo.get_used_ammo_groups_count_for_type(current_user),
|
||||||
|
ammo_type |> Ammo.get_ammo_groups_count_for_type(current_user, true),
|
||||||
|
ammo_type |> ActivityLog.get_used_count_for_ammo_type(current_user),
|
||||||
|
ammo_type |> Ammo.get_historical_count_for_ammo_type(current_user)
|
||||||
|
]
|
||||||
|
else
|
||||||
|
[nil, nil, nil, nil, nil]
|
||||||
|
end
|
||||||
|
|
||||||
socket
|
socket
|
||||||
|> assign(
|
|> assign(
|
||||||
page_title: page_title(live_action, ammo_type),
|
page_title: page_title(live_action, ammo_type),
|
||||||
ammo_type: ammo_type,
|
ammo_type: ammo_type,
|
||||||
ammo_groups: ammo_type |> Ammo.list_ammo_groups_for_type(current_user, show_used),
|
ammo_groups: ammo_groups,
|
||||||
avg_cost_per_round: ammo_type |> Ammo.get_average_cost_for_ammo_type!(current_user),
|
cprs: ammo_groups |> Ammo.get_cprs(current_user),
|
||||||
|
last_used_dates: ammo_groups |> ActivityLog.get_last_used_dates(current_user),
|
||||||
|
avg_cost_per_round: ammo_type |> Ammo.get_average_cost_for_ammo_type(current_user),
|
||||||
|
rounds: ammo_type |> Ammo.get_round_count_for_ammo_type(current_user),
|
||||||
|
original_counts: original_counts,
|
||||||
|
used_rounds: used_rounds,
|
||||||
|
historical_round_count: historical_round_count,
|
||||||
|
packs_count: ammo_type |> Ammo.get_ammo_groups_count_for_type(current_user),
|
||||||
|
used_packs_count: used_packs_count,
|
||||||
|
historical_packs_count: historical_packs_count,
|
||||||
fields_list: @fields_list,
|
fields_list: @fields_list,
|
||||||
fields_to_display: fields_to_display
|
fields_to_display: fields_to_display
|
||||||
)
|
)
|
||||||
@ -113,6 +140,9 @@ defmodule CanneryWeb.AmmoTypeLive.Show do
|
|||||||
socket |> display_ammo_type(ammo_type)
|
socket |> display_ammo_type(ammo_type)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec display_currency(float()) :: String.t()
|
||||||
|
defp display_currency(float), do: :erlang.float_to_binary(float, decimals: 2)
|
||||||
|
|
||||||
defp page_title(action, %{name: ammo_type_name}) when action in [:show, :table],
|
defp page_title(action, %{name: ammo_type_name}) when action in [:show, :table],
|
||||||
do: ammo_type_name
|
do: ammo_type_name
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
<span
|
<span
|
||||||
:if={@ammo_type.desc}
|
:if={@ammo_type.desc}
|
||||||
class="max-w-2xl w-full px-8 py-4 rounded-lg
|
class="max-w-2xl w-full px-8 py-4 rounded-lg
|
||||||
text-center title text-lg
|
text-center title text-lg
|
||||||
border border-primary-600"
|
border border-primary-600"
|
||||||
>
|
>
|
||||||
<%= @ammo_type.desc %>
|
<%= @ammo_type.desc %>
|
||||||
</span>
|
</span>
|
||||||
@ -16,7 +16,7 @@
|
|||||||
<.link
|
<.link
|
||||||
patch={Routes.ammo_type_show_path(Endpoint, :edit, @ammo_type)}
|
patch={Routes.ammo_type_show_path(Endpoint, :edit, @ammo_type)}
|
||||||
class="text-primary-600 link"
|
class="text-primary-600 link"
|
||||||
data-qa="edit"
|
aria-label={dgettext("actions", "Edit %{ammo_type_name}", ammo_type_name: @ammo_type.name)}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-edit"></i>
|
<i class="fa-fw fa-lg fas fa-edit"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -32,7 +32,9 @@
|
|||||||
name: @ammo_type.name
|
name: @ammo_type.name
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
data-qa="delete"
|
aria-label={
|
||||||
|
dgettext("actions", "Delete %{ammo_type_name}", ammo_type_name: @ammo_type.name)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-trash"></i>
|
<i class="fa-fw fa-lg fas fa-trash"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -69,63 +71,59 @@
|
|||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<span class="text-primary-600">
|
<span class="text-primary-600">
|
||||||
<%= @ammo_type |> Ammo.get_round_count_for_ammo_type(@current_user) %>
|
<%= @rounds %>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<h3 class="title text-lg">
|
<%= if @show_used do %>
|
||||||
<%= gettext("Used rounds:") %>
|
<h3 class="title text-lg">
|
||||||
</h3>
|
<%= gettext("Used rounds:") %>
|
||||||
|
</h3>
|
||||||
|
|
||||||
<span class="text-primary-600">
|
<span class="text-primary-600">
|
||||||
<%= @ammo_type |> Ammo.get_used_count_for_ammo_type(@current_user) %>
|
<%= @used_rounds %>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<h3 class="title text-lg">
|
<h3 class="title text-lg">
|
||||||
<%= gettext("Total ever rounds:") %>
|
<%= gettext("Total ever rounds:") %>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<span class="text-primary-600">
|
<span class="text-primary-600">
|
||||||
<%= @ammo_type |> Ammo.get_historical_count_for_ammo_type(@current_user) %>
|
<%= @historical_round_count %>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
<% end %>
|
||||||
|
|
||||||
<hr class="hr" />
|
|
||||||
|
|
||||||
<div class="grid sm:grid-cols-2 gap-4 text-center justify-center items-center">
|
|
||||||
<h3 class="title text-lg">
|
<h3 class="title text-lg">
|
||||||
<%= gettext("Packs:") %>
|
<%= gettext("Packs:") %>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<span class="text-primary-600">
|
<span class="text-primary-600">
|
||||||
<%= @ammo_type |> Ammo.get_ammo_groups_count_for_type(@current_user) %>
|
<%= @packs_count %>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<h3 class="title text-lg">
|
<%= if @show_used do %>
|
||||||
<%= gettext("Used packs:") %>
|
<h3 class="title text-lg">
|
||||||
</h3>
|
<%= gettext("Used packs:") %>
|
||||||
|
</h3>
|
||||||
|
|
||||||
<span class="text-primary-600">
|
<span class="text-primary-600">
|
||||||
<%= @ammo_type |> Ammo.get_used_ammo_groups_count_for_type(@current_user) %>
|
<%= @used_packs_count %>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<h3 class="title text-lg">
|
<h3 class="title text-lg">
|
||||||
<%= gettext("Total ever packs:") %>
|
<%= gettext("Total ever packs:") %>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<span class="text-primary-600">
|
<span class="text-primary-600">
|
||||||
<%= @ammo_type |> Ammo.get_ammo_groups_count_for_type(@current_user, true) %>
|
<%= @historical_packs_count %>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
<% end %>
|
||||||
|
|
||||||
<hr class="hr" />
|
|
||||||
|
|
||||||
<div class="grid sm:grid-cols-2 gap-4 text-center justify-center items-center">
|
|
||||||
<h3 class="title text-lg">
|
<h3 class="title text-lg">
|
||||||
<%= gettext("Added on:") %>
|
<%= gettext("Added on:") %>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<span class="text-primary-600">
|
<span class="text-primary-600">
|
||||||
<.datetime datetime={@ammo_type.inserted_at} />
|
<.datetime id={"#{@ammo_type.id}-inserted-at"} datetime={@ammo_type.inserted_at} />
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<%= if @avg_cost_per_round do %>
|
<%= if @avg_cost_per_round do %>
|
||||||
@ -134,9 +132,7 @@
|
|||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<span class="text-primary-600">
|
<span class="text-primary-600">
|
||||||
<%= gettext("$%{amount}",
|
<%= gettext("$%{amount}", amount: display_currency(@avg_cost_per_round)) %>
|
||||||
amount: @avg_cost_per_round |> :erlang.float_to_binary(decimals: 2)
|
|
||||||
) %>
|
|
||||||
</span>
|
</span>
|
||||||
<% else %>
|
<% else %>
|
||||||
<h3 class="mx-8 my-4 title text-lg text-primary-600 col-span-2">
|
<h3 class="mx-8 my-4 title text-lg text-primary-600 col-span-2">
|
||||||
@ -175,7 +171,7 @@
|
|||||||
ammo_groups={@ammo_groups}
|
ammo_groups={@ammo_groups}
|
||||||
current_user={@current_user}
|
current_user={@current_user}
|
||||||
>
|
>
|
||||||
<:container :let={%{container: %{name: container_name} = container}}>
|
<:container :let={{_ammo_group, %{name: container_name} = container}}>
|
||||||
<.link
|
<.link
|
||||||
navigate={Routes.container_show_path(Endpoint, :show, container)}
|
navigate={Routes.container_show_path(Endpoint, :show, container)}
|
||||||
class="mx-2 my-1 link"
|
class="mx-2 my-1 link"
|
||||||
@ -187,8 +183,12 @@
|
|||||||
<% else %>
|
<% else %>
|
||||||
<div class="flex flex-wrap justify-center items-stretch">
|
<div class="flex flex-wrap justify-center items-stretch">
|
||||||
<.ammo_group_card
|
<.ammo_group_card
|
||||||
:for={ammo_group <- @ammo_groups}
|
:for={%{id: ammo_group_id} = ammo_group <- @ammo_groups}
|
||||||
ammo_group={ammo_group}
|
ammo_group={ammo_group}
|
||||||
|
original_count={@original_counts && Map.fetch!(@original_counts, ammo_group_id)}
|
||||||
|
cpr={Map.get(@cprs, ammo_group_id)}
|
||||||
|
last_used_date={Map.get(@last_used_dates, ammo_group_id)}
|
||||||
|
current_user={@current_user}
|
||||||
show_container={true}
|
show_container={true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,25 +4,41 @@ defmodule CanneryWeb.ContainerLive.EditTagsComponent do
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
use CanneryWeb, :live_component
|
use CanneryWeb, :live_component
|
||||||
alias Cannery.{Accounts.User, Containers, Containers.Container, Repo, Tags, Tags.Tag}
|
alias Cannery.{Accounts.User, Containers}
|
||||||
|
alias Cannery.Containers.{Container, Tag}
|
||||||
alias Phoenix.LiveView.Socket
|
alias Phoenix.LiveView.Socket
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
@spec update(
|
@spec update(
|
||||||
%{:container => Container.t(), :current_user => User.t(), optional(any) => any},
|
%{
|
||||||
|
:container => Container.t(),
|
||||||
|
:current_path => String.t(),
|
||||||
|
:current_user => User.t(),
|
||||||
|
optional(any) => any
|
||||||
|
},
|
||||||
Socket.t()
|
Socket.t()
|
||||||
) :: {:ok, Socket.t()}
|
) :: {:ok, Socket.t()}
|
||||||
def update(%{container: container, current_user: current_user} = assigns, socket) do
|
def update(
|
||||||
tags = Tags.list_tags(current_user)
|
%{container: _container, current_path: _current_path, current_user: current_user} =
|
||||||
container = container |> Repo.preload(:tags)
|
assigns,
|
||||||
{:ok, socket |> assign(assigns) |> assign(tags: tags, container: container)}
|
socket
|
||||||
|
) do
|
||||||
|
tags = Containers.list_tags(current_user)
|
||||||
|
{:ok, socket |> assign(assigns) |> assign(:tags, tags)}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event(
|
def handle_event(
|
||||||
"save",
|
"save",
|
||||||
%{"tag" => %{"tag_id" => tag_id}},
|
%{"tag" => %{"tag_id" => tag_id}},
|
||||||
%{assigns: %{tags: tags, container: container, current_user: current_user}} = socket
|
%{
|
||||||
|
assigns: %{
|
||||||
|
tags: tags,
|
||||||
|
container: container,
|
||||||
|
current_user: current_user,
|
||||||
|
current_path: current_path
|
||||||
|
}
|
||||||
|
} = socket
|
||||||
) do
|
) do
|
||||||
socket =
|
socket =
|
||||||
case tags |> Enum.find(fn %{id: id} -> tag_id == id end) do
|
case tags |> Enum.find(fn %{id: id} -> tag_id == id end) do
|
||||||
@ -32,19 +48,24 @@ defmodule CanneryWeb.ContainerLive.EditTagsComponent do
|
|||||||
|
|
||||||
%{name: tag_name} = tag ->
|
%{name: tag_name} = tag ->
|
||||||
_container_tag = Containers.add_tag!(container, tag, current_user)
|
_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)
|
prompt = dgettext("prompts", "%{name} added successfully", name: tag_name)
|
||||||
socket |> put_flash(:info, prompt) |> assign(container: container)
|
socket |> put_flash(:info, prompt) |> push_patch(to: current_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event(
|
def handle_event(
|
||||||
"delete",
|
"delete",
|
||||||
%{"tag-id" => tag_id},
|
%{"tag-id" => tag_id},
|
||||||
%{assigns: %{tags: tags, container: container, current_user: current_user}} = socket
|
%{
|
||||||
|
assigns: %{
|
||||||
|
tags: tags,
|
||||||
|
container: container,
|
||||||
|
current_user: current_user,
|
||||||
|
current_path: current_path
|
||||||
|
}
|
||||||
|
} = socket
|
||||||
) do
|
) do
|
||||||
socket =
|
socket =
|
||||||
case tags |> Enum.find(fn %{id: id} -> tag_id == id end) do
|
case tags |> Enum.find(fn %{id: id} -> tag_id == id end) do
|
||||||
@ -54,9 +75,8 @@ defmodule CanneryWeb.ContainerLive.EditTagsComponent do
|
|||||||
|
|
||||||
%{name: tag_name} = tag ->
|
%{name: tag_name} = tag ->
|
||||||
_container_tag = Containers.remove_tag!(container, tag, current_user)
|
_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)
|
prompt = dgettext("prompts", "%{name} removed successfully", name: tag_name)
|
||||||
socket |> put_flash(:info, prompt) |> assign(container: container)
|
socket |> put_flash(:info, prompt) |> push_patch(to: current_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
|
@ -36,7 +36,8 @@
|
|||||||
|
|
||||||
<.form
|
<.form
|
||||||
:let={f}
|
:let={f}
|
||||||
for={:tag}
|
for={%{}}
|
||||||
|
as={:tag}
|
||||||
id="add-tag-to-container-form"
|
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"
|
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}
|
||||||
|
@ -35,17 +35,17 @@ defmodule CanneryWeb.ContainerLive.FormComponent do
|
|||||||
container_params
|
container_params
|
||||||
) do
|
) do
|
||||||
changeset_action =
|
changeset_action =
|
||||||
cond do
|
case action do
|
||||||
action in [:new, :clone] -> :insert
|
create when create in [:new, :clone] -> :insert
|
||||||
action == :edit -> :update
|
:edit -> :update
|
||||||
end
|
end
|
||||||
|
|
||||||
changeset =
|
changeset =
|
||||||
cond do
|
case action do
|
||||||
action in [:new, :clone] ->
|
create when create in [:new, :clone] ->
|
||||||
container |> Container.create_changeset(user, container_params)
|
container |> Container.create_changeset(user, container_params)
|
||||||
|
|
||||||
action == :edit ->
|
:edit ->
|
||||||
container |> Container.update_changeset(container_params)
|
container |> Container.update_changeset(container_params)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -27,9 +27,11 @@
|
|||||||
|
|
||||||
<%= label(f, :desc, gettext("Description"), class: "title text-lg text-primary-600") %>
|
<%= label(f, :desc, gettext("Description"), class: "title text-lg text-primary-600") %>
|
||||||
<%= textarea(f, :desc,
|
<%= textarea(f, :desc,
|
||||||
|
id: "container-form-desc",
|
||||||
class: "input input-primary col-span-2",
|
class: "input input-primary col-span-2",
|
||||||
|
placeholder: gettext("Metal ammo can with the anime girl sticker"),
|
||||||
phx_hook: "MaintainAttrs",
|
phx_hook: "MaintainAttrs",
|
||||||
placeholder: gettext("Metal ammo can with the anime girl sticker")
|
phx_update: "ignore"
|
||||||
) %>
|
) %>
|
||||||
<%= error_tag(f, :desc, "col-span-3 text-center") %>
|
<%= error_tag(f, :desc, "col-span-3 text-center") %>
|
||||||
|
|
||||||
@ -42,9 +44,11 @@
|
|||||||
|
|
||||||
<%= label(f, :location, gettext("Location"), class: "title text-lg text-primary-600") %>
|
<%= label(f, :location, gettext("Location"), class: "title text-lg text-primary-600") %>
|
||||||
<%= textarea(f, :location,
|
<%= textarea(f, :location,
|
||||||
|
id: "container-form-location",
|
||||||
class: "input input-primary col-span-2",
|
class: "input input-primary col-span-2",
|
||||||
|
placeholder: gettext("On the bookshelf"),
|
||||||
phx_hook: "MaintainAttrs",
|
phx_hook: "MaintainAttrs",
|
||||||
placeholder: gettext("On the bookshelf")
|
phx_update: "ignore"
|
||||||
) %>
|
) %>
|
||||||
<%= error_tag(f, :location, "col-span-3 text-center") %>
|
<%= error_tag(f, :location, "col-span-3 text-center") %>
|
||||||
|
|
||||||
|
@ -4,8 +4,7 @@ defmodule CanneryWeb.ContainerLive.Index do
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
use CanneryWeb, :live_view
|
use CanneryWeb, :live_view
|
||||||
import CanneryWeb.Components.ContainerCard
|
alias Cannery.{Containers, Containers.Container}
|
||||||
alias Cannery.{Containers, Containers.Container, Repo}
|
|
||||||
alias Ecto.Changeset
|
alias Ecto.Changeset
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
@ -23,10 +22,7 @@ defmodule CanneryWeb.ContainerLive.Index do
|
|||||||
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} =
|
%{name: container_name} = container = Containers.get_container!(id, current_user)
|
||||||
container =
|
|
||||||
Containers.get_container!(id, current_user)
|
|
||||||
|> Repo.preload([:tags, :ammo_groups])
|
|
||||||
|
|
||||||
socket
|
socket
|
||||||
|> assign(page_title: gettext("Edit %{name}", name: container_name), container: container)
|
|> assign(page_title: gettext("Edit %{name}", name: container_name), container: container)
|
||||||
@ -62,9 +58,7 @@ defmodule CanneryWeb.ContainerLive.Index do
|
|||||||
end
|
end
|
||||||
|
|
||||||
defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit_tags, %{"id" => id}) do
|
defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit_tags, %{"id" => id}) do
|
||||||
%{name: container_name} =
|
%{name: container_name} = container = Containers.get_container!(id, current_user)
|
||||||
container =
|
|
||||||
Containers.get_container!(id, current_user) |> Repo.preload([:tags, :ammo_groups])
|
|
||||||
|
|
||||||
page_title = gettext("Edit %{name} tags", name: container_name)
|
page_title = gettext("Edit %{name} tags", name: container_name)
|
||||||
socket |> assign(page_title: page_title, container: container)
|
socket |> assign(page_title: page_title, container: container)
|
||||||
@ -106,12 +100,10 @@ defmodule CanneryWeb.ContainerLive.Index do
|
|||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event("toggle_table", _params, %{assigns: %{view_table: view_table}} = socket) do
|
def handle_event("toggle_table", _params, %{assigns: %{view_table: view_table}} = socket) do
|
||||||
{:noreply, socket |> assign(:view_table, !view_table) |> display_containers()}
|
{:noreply, socket |> assign(:view_table, !view_table) |> display_containers()}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event("search", %{"search" => %{"search_term" => ""}}, socket) do
|
def handle_event("search", %{"search" => %{"search_term" => ""}}, socket) do
|
||||||
{:noreply, socket |> push_patch(to: Routes.container_index_path(Endpoint, :index))}
|
{:noreply, socket |> push_patch(to: Routes.container_index_path(Endpoint, :index))}
|
||||||
end
|
end
|
||||||
@ -122,10 +114,6 @@ defmodule CanneryWeb.ContainerLive.Index do
|
|||||||
end
|
end
|
||||||
|
|
||||||
defp display_containers(%{assigns: %{search: search, current_user: current_user}} = socket) do
|
defp display_containers(%{assigns: %{search: search, current_user: current_user}} = socket) do
|
||||||
containers =
|
socket |> assign(:containers, Containers.list_containers(search, current_user))
|
||||||
Containers.list_containers(search, current_user)
|
|
||||||
|> Repo.preload([:tags, :ammo_groups])
|
|
||||||
|
|
||||||
socket |> assign(:containers, containers)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -20,15 +20,16 @@
|
|||||||
<div class="w-full flex flex-col sm:flex-row justify-center items-center space-y-4 sm:space-y-0 sm:space-x-4 max-w-xl">
|
<div class="w-full flex flex-col sm:flex-row justify-center items-center space-y-4 sm:space-y-0 sm:space-x-4 max-w-xl">
|
||||||
<.form
|
<.form
|
||||||
:let={f}
|
:let={f}
|
||||||
for={:search}
|
for={%{}}
|
||||||
|
as={:search}
|
||||||
phx-change="search"
|
phx-change="search"
|
||||||
phx-submit="search"
|
phx-submit="search"
|
||||||
class="grow self-stretch flex flex-col items-stretch"
|
class="grow self-stretch flex flex-col items-stretch"
|
||||||
data-qa="container_search"
|
|
||||||
>
|
>
|
||||||
<%= text_input(f, :search_term,
|
<%= text_input(f, :search_term,
|
||||||
class: "input input-primary",
|
class: "input input-primary",
|
||||||
value: @search,
|
value: @search,
|
||||||
|
role: "search",
|
||||||
phx_debounce: 300,
|
phx_debounce: 300,
|
||||||
placeholder: gettext("Search containers")
|
placeholder: gettext("Search containers")
|
||||||
) %>
|
) %>
|
||||||
@ -61,6 +62,9 @@
|
|||||||
<.link
|
<.link
|
||||||
patch={Routes.container_index_path(Endpoint, :edit_tags, container)}
|
patch={Routes.container_index_path(Endpoint, :edit_tags, container)}
|
||||||
class="text-primary-600 link"
|
class="text-primary-600 link"
|
||||||
|
aria-label={
|
||||||
|
dgettext("actions", "Tag %{container_name}", container_name: container.name)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-tags"></i>
|
<i class="fa-fw fa-lg fas fa-tags"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -70,7 +74,9 @@
|
|||||||
<.link
|
<.link
|
||||||
patch={Routes.container_index_path(Endpoint, :edit, container)}
|
patch={Routes.container_index_path(Endpoint, :edit, container)}
|
||||||
class="text-primary-600 link"
|
class="text-primary-600 link"
|
||||||
data-qa={"edit-#{container.id}"}
|
aria-label={
|
||||||
|
dgettext("actions", "Edit %{container_name}", container_name: container.name)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-edit"></i>
|
<i class="fa-fw fa-lg fas fa-edit"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -78,7 +84,9 @@
|
|||||||
<.link
|
<.link
|
||||||
patch={Routes.container_index_path(Endpoint, :clone, container)}
|
patch={Routes.container_index_path(Endpoint, :clone, container)}
|
||||||
class="text-primary-600 link"
|
class="text-primary-600 link"
|
||||||
data-qa={"clone-#{container.id}"}
|
aria-label={
|
||||||
|
dgettext("actions", "Clone %{container_name}", container_name: container.name)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-copy"></i>
|
<i class="fa-fw fa-lg fas fa-copy"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -91,7 +99,9 @@
|
|||||||
data-confirm={
|
data-confirm={
|
||||||
dgettext("prompts", "Are you sure you want to delete %{name}?", name: container.name)
|
dgettext("prompts", "Are you sure you want to delete %{name}?", name: container.name)
|
||||||
}
|
}
|
||||||
data-qa={"delete-#{container.id}"}
|
aria-label={
|
||||||
|
dgettext("actions", "Delete %{container_name}", container_name: container.name)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-trash"></i>
|
<i class="fa-fw fa-lg fas fa-trash"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -99,12 +109,19 @@
|
|||||||
</.live_component>
|
</.live_component>
|
||||||
<% else %>
|
<% else %>
|
||||||
<div class="w-full flex flex-row flex-wrap justify-center items-stretch">
|
<div class="w-full flex flex-row flex-wrap justify-center items-stretch">
|
||||||
<.container_card :for={container <- @containers} container={container}>
|
<.container_card
|
||||||
|
:for={container <- @containers}
|
||||||
|
container={container}
|
||||||
|
current_user={@current_user}
|
||||||
|
>
|
||||||
<:tag_actions>
|
<:tag_actions>
|
||||||
<div class="mx-4 my-2">
|
<div class="mx-4 my-2">
|
||||||
<.link
|
<.link
|
||||||
patch={Routes.container_index_path(Endpoint, :edit_tags, container)}
|
patch={Routes.container_index_path(Endpoint, :edit_tags, container)}
|
||||||
class="text-primary-600 link"
|
class="text-primary-600 link"
|
||||||
|
aria-label={
|
||||||
|
dgettext("actions", "Tag %{container_name}", container_name: container.name)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-tags"></i>
|
<i class="fa-fw fa-lg fas fa-tags"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -113,7 +130,9 @@
|
|||||||
<.link
|
<.link
|
||||||
patch={Routes.container_index_path(Endpoint, :edit, container)}
|
patch={Routes.container_index_path(Endpoint, :edit, container)}
|
||||||
class="text-primary-600 link"
|
class="text-primary-600 link"
|
||||||
data-qa={"edit-#{container.id}"}
|
aria-label={
|
||||||
|
dgettext("actions", "Edit %{container_name}", container_name: container.name)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-edit"></i>
|
<i class="fa-fw fa-lg fas fa-edit"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -121,7 +140,9 @@
|
|||||||
<.link
|
<.link
|
||||||
patch={Routes.container_index_path(Endpoint, :clone, container)}
|
patch={Routes.container_index_path(Endpoint, :clone, container)}
|
||||||
class="text-primary-600 link"
|
class="text-primary-600 link"
|
||||||
data-qa={"clone-#{container.id}"}
|
aria-label={
|
||||||
|
dgettext("actions", "Clone %{container_name}", container_name: container.name)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-copy"></i>
|
<i class="fa-fw fa-lg fas fa-copy"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -134,7 +155,9 @@
|
|||||||
data-confirm={
|
data-confirm={
|
||||||
dgettext("prompts", "Are you sure you want to delete %{name}?", name: container.name)
|
dgettext("prompts", "Are you sure you want to delete %{name}?", name: container.name)
|
||||||
}
|
}
|
||||||
data-qa={"delete-#{container.id}"}
|
aria-label={
|
||||||
|
dgettext("actions", "Delete %{container_name}", container_name: container.name)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-trash"></i>
|
<i class="fa-fw fa-lg fas fa-trash"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -144,28 +167,30 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<.modal
|
<%= case @live_action do %>
|
||||||
:if={@live_action in [:new, :edit, :clone]}
|
<% modifying when modifying in [:new, :edit, :clone] -> %>
|
||||||
return_to={Routes.container_index_path(Endpoint, :index)}
|
<.modal return_to={Routes.container_index_path(Endpoint, :index)}>
|
||||||
>
|
<.live_component
|
||||||
<.live_component
|
module={CanneryWeb.ContainerLive.FormComponent}
|
||||||
module={CanneryWeb.ContainerLive.FormComponent}
|
id={@container.id || :new}
|
||||||
id={@container.id || :new}
|
title={@page_title}
|
||||||
title={@page_title}
|
action={@live_action}
|
||||||
action={@live_action}
|
container={@container}
|
||||||
container={@container}
|
return_to={Routes.container_index_path(Endpoint, :index)}
|
||||||
return_to={Routes.container_index_path(Endpoint, :index)}
|
current_user={@current_user}
|
||||||
current_user={@current_user}
|
/>
|
||||||
/>
|
</.modal>
|
||||||
</.modal>
|
<% :edit_tags -> %>
|
||||||
|
<.modal return_to={Routes.container_index_path(Endpoint, :index)}>
|
||||||
<.modal :if={@live_action == :edit_tags} return_to={Routes.container_index_path(Endpoint, :index)}>
|
<.live_component
|
||||||
<.live_component
|
module={CanneryWeb.ContainerLive.EditTagsComponent}
|
||||||
module={CanneryWeb.ContainerLive.EditTagsComponent}
|
id={@container.id}
|
||||||
id={@container.id}
|
title={@page_title}
|
||||||
title={@page_title}
|
action={@live_action}
|
||||||
action={@live_action}
|
container={@container}
|
||||||
container={@container}
|
current_path={Routes.container_index_path(Endpoint, :edit_tags, @container)}
|
||||||
current_user={@current_user}
|
current_user={@current_user}
|
||||||
/>
|
/>
|
||||||
</.modal>
|
</.modal>
|
||||||
|
<% _ -> %>
|
||||||
|
<% end %>
|
||||||
|
@ -4,8 +4,7 @@ defmodule CanneryWeb.ContainerLive.Show do
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
use CanneryWeb, :live_view
|
use CanneryWeb, :live_view
|
||||||
import CanneryWeb.Components.{AmmoGroupCard, TagCard}
|
alias Cannery.{Accounts.User, ActivityLog, Ammo, Containers, Containers.Container}
|
||||||
alias Cannery.{Accounts.User, Ammo, Containers, Containers.Container, Repo, Tags}
|
|
||||||
alias CanneryWeb.Endpoint
|
alias CanneryWeb.Endpoint
|
||||||
alias Ecto.Changeset
|
alias Ecto.Changeset
|
||||||
alias Phoenix.LiveView.Socket
|
alias Phoenix.LiveView.Socket
|
||||||
@ -31,7 +30,7 @@ defmodule CanneryWeb.ContainerLive.Show do
|
|||||||
%{assigns: %{container: container, current_user: current_user}} = socket
|
%{assigns: %{container: container, current_user: current_user}} = socket
|
||||||
) do
|
) do
|
||||||
socket =
|
socket =
|
||||||
case Tags.get_tag(tag_id, current_user) do
|
case Containers.get_tag(tag_id, current_user) do
|
||||||
{:ok, tag} ->
|
{:ok, tag} ->
|
||||||
_count = Containers.remove_tag!(container, tag, current_user)
|
_count = Containers.remove_tag!(container, tag, current_user)
|
||||||
|
|
||||||
@ -43,14 +42,13 @@ defmodule CanneryWeb.ContainerLive.Show do
|
|||||||
|
|
||||||
socket |> put_flash(:info, prompt) |> render_container()
|
socket |> put_flash(:info, prompt) |> render_container()
|
||||||
|
|
||||||
{:error, error_string} ->
|
{:error, :not_found} ->
|
||||||
socket |> put_flash(:error, error_string)
|
socket |> put_flash(:error, dgettext("errors", "Tag not found"))
|
||||||
end
|
end
|
||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event(
|
def handle_event(
|
||||||
"delete_container",
|
"delete_container",
|
||||||
_params,
|
_params,
|
||||||
@ -84,12 +82,10 @@ defmodule CanneryWeb.ContainerLive.Show do
|
|||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event("toggle_show_used", _params, %{assigns: %{show_used: show_used}} = socket) do
|
def handle_event("toggle_show_used", _params, %{assigns: %{show_used: show_used}} = socket) do
|
||||||
{:noreply, socket |> assign(:show_used, !show_used) |> render_container()}
|
{:noreply, socket |> assign(:show_used, !show_used) |> render_container()}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event("toggle_table", _params, %{assigns: %{view_table: view_table}} = socket) do
|
def handle_event("toggle_table", _params, %{assigns: %{view_table: view_table}} = socket) do
|
||||||
{:noreply, socket |> assign(:view_table, !view_table) |> render_container()}
|
{:noreply, socket |> assign(:view_table, !view_table) |> render_container()}
|
||||||
end
|
end
|
||||||
@ -100,12 +96,11 @@ defmodule CanneryWeb.ContainerLive.Show do
|
|||||||
id,
|
id,
|
||||||
current_user
|
current_user
|
||||||
) do
|
) do
|
||||||
%{name: container_name} =
|
%{name: container_name} = container = Containers.get_container!(id, current_user)
|
||||||
container =
|
|
||||||
Containers.get_container!(id, current_user)
|
|
||||||
|> Repo.preload([:tags], force: true)
|
|
||||||
|
|
||||||
ammo_groups = Ammo.list_ammo_groups_for_container(container, current_user, show_used)
|
ammo_groups = Ammo.list_ammo_groups_for_container(container, current_user, show_used)
|
||||||
|
original_counts = ammo_groups |> Ammo.get_original_counts(current_user)
|
||||||
|
cprs = ammo_groups |> Ammo.get_cprs(current_user)
|
||||||
|
last_used_dates = ammo_groups |> ActivityLog.get_last_used_dates(current_user)
|
||||||
|
|
||||||
page_title =
|
page_title =
|
||||||
case live_action do
|
case live_action do
|
||||||
@ -114,7 +109,16 @@ defmodule CanneryWeb.ContainerLive.Show do
|
|||||||
:edit_tags -> gettext("Edit %{name} tags", name: container_name)
|
:edit_tags -> gettext("Edit %{name} tags", name: container_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
socket |> assign(container: container, ammo_groups: ammo_groups, page_title: page_title)
|
socket
|
||||||
|
|> assign(
|
||||||
|
container: container,
|
||||||
|
round_count: Ammo.get_round_count_for_container!(container, current_user),
|
||||||
|
ammo_groups: ammo_groups,
|
||||||
|
original_counts: original_counts,
|
||||||
|
cprs: cprs,
|
||||||
|
last_used_dates: last_used_dates,
|
||||||
|
page_title: page_title
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec render_container(Socket.t()) :: Socket.t()
|
@spec render_container(Socket.t()) :: Socket.t()
|
||||||
|
@ -20,21 +20,18 @@
|
|||||||
|
|
||||||
<%= unless @ammo_groups |> Enum.empty?() do %>
|
<%= unless @ammo_groups |> Enum.empty?() do %>
|
||||||
<span class="rounded-lg title text-lg">
|
<span class="rounded-lg title text-lg">
|
||||||
<%= if @show_used do %>
|
<%= gettext("Packs:") %>
|
||||||
<%= gettext("Total packs:") %>
|
<%= @ammo_groups |> Enum.reject(fn %{count: count} -> count in [0, nil] end) |> Enum.count() %>
|
||||||
<% else %>
|
</span>
|
||||||
<%= gettext("Packs:") %>
|
|
||||||
<% end %>
|
<span :if={@show_used} class="rounded-lg title text-lg">
|
||||||
|
<%= gettext("Total packs:") %>
|
||||||
<%= Enum.count(@ammo_groups) %>
|
<%= Enum.count(@ammo_groups) %>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="rounded-lg title text-lg">
|
<span class="rounded-lg title text-lg">
|
||||||
<%= if @show_used do %>
|
<%= gettext("Rounds:") %>
|
||||||
<%= gettext("Total rounds:") %>
|
<%= @round_count %>
|
||||||
<% else %>
|
|
||||||
<%= gettext("Rounds:") %>
|
|
||||||
<% end %>
|
|
||||||
<%= @container |> Containers.get_container_rounds!() %>
|
|
||||||
</span>
|
</span>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
@ -42,7 +39,7 @@
|
|||||||
<.link
|
<.link
|
||||||
patch={Routes.container_show_path(Endpoint, :edit, @container)}
|
patch={Routes.container_show_path(Endpoint, :edit, @container)}
|
||||||
class="text-primary-600 link"
|
class="text-primary-600 link"
|
||||||
data-qa="edit"
|
aria-label={dgettext("actions", "Edit %{container_name}", container_name: @container.name)}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-edit"></i>
|
<i class="fa-fw fa-lg fas fa-edit"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -54,7 +51,9 @@
|
|||||||
data-confirm={
|
data-confirm={
|
||||||
dgettext("prompts", "Are you sure you want to delete %{name}?", name: @container.name)
|
dgettext("prompts", "Are you sure you want to delete %{name}?", name: @container.name)
|
||||||
}
|
}
|
||||||
data-qa="delete"
|
aria-label={
|
||||||
|
dgettext("actions", "Delete %{container_name}", container_name: @container.name)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-trash"></i>
|
<i class="fa-fw fa-lg fas fa-trash"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -128,39 +127,45 @@
|
|||||||
</.live_component>
|
</.live_component>
|
||||||
<% else %>
|
<% else %>
|
||||||
<div class="flex flex-wrap justify-center items-stretch">
|
<div class="flex flex-wrap justify-center items-stretch">
|
||||||
<.ammo_group_card :for={ammo_group <- @ammo_groups} ammo_group={ammo_group} />
|
<.ammo_group_card
|
||||||
|
:for={%{id: ammo_group_id} = ammo_group <- @ammo_groups}
|
||||||
|
ammo_group={ammo_group}
|
||||||
|
original_count={Map.fetch!(@original_counts, ammo_group_id)}
|
||||||
|
cpr={Map.get(@cprs, ammo_group_id)}
|
||||||
|
last_used_date={Map.get(@last_used_dates, ammo_group_id)}
|
||||||
|
current_user={@current_user}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<.modal
|
<%= case @live_action do %>
|
||||||
:if={@live_action == :edit}
|
<% :edit -> %>
|
||||||
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.FormComponent}
|
||||||
module={CanneryWeb.ContainerLive.FormComponent}
|
id={@container.id}
|
||||||
id={@container.id}
|
title={@page_title}
|
||||||
title={@page_title}
|
action={@live_action}
|
||||||
action={@live_action}
|
container={@container}
|
||||||
container={@container}
|
return_to={Routes.container_show_path(Endpoint, :show, @container)}
|
||||||
return_to={Routes.container_show_path(Endpoint, :show, @container)}
|
current_user={@current_user}
|
||||||
current_user={@current_user}
|
/>
|
||||||
/>
|
</.modal>
|
||||||
</.modal>
|
<% :edit_tags -> %>
|
||||||
|
<.modal return_to={Routes.container_show_path(Endpoint, :show, @container)}>
|
||||||
<.modal
|
<.live_component
|
||||||
:if={@live_action == :edit_tags}
|
module={CanneryWeb.ContainerLive.EditTagsComponent}
|
||||||
return_to={Routes.container_show_path(Endpoint, :show, @container)}
|
id={@container.id}
|
||||||
>
|
title={@page_title}
|
||||||
<.live_component
|
action={@live_action}
|
||||||
module={CanneryWeb.ContainerLive.EditTagsComponent}
|
container={@container}
|
||||||
id={@container.id}
|
return_to={Routes.container_show_path(Endpoint, :show, @container)}
|
||||||
title={@page_title}
|
current_path={Routes.container_show_path(Endpoint, :edit_tags, @container)}
|
||||||
action={@live_action}
|
current_user={@current_user}
|
||||||
container={@container}
|
/>
|
||||||
return_to={Routes.container_show_path(Endpoint, :show, @container)}
|
</.modal>
|
||||||
current_user={@current_user}
|
<% _ -> %>
|
||||||
/>
|
<% end %>
|
||||||
</.modal>
|
|
||||||
|
@ -12,7 +12,6 @@ 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)
|
||||||
socket = socket |> assign(page_title: gettext("Home"), admins: admins, version: @version)
|
{:ok, socket |> assign(page_title: gettext("Home"), admins: admins, version: @version)}
|
||||||
{:ok, socket}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -17,8 +17,7 @@
|
|||||||
<hr class="hr" />
|
<hr class="hr" />
|
||||||
|
|
||||||
<ul class="flex flex-col space-y-4 text-center">
|
<ul class="flex flex-col space-y-4 text-center">
|
||||||
<li class="flex flex-col justify-center items-center
|
<li class="flex flex-col justify-center items-center space-y-2">
|
||||||
space-y-2">
|
|
||||||
<b class="whitespace-nowrap">
|
<b class="whitespace-nowrap">
|
||||||
<%= gettext("Easy to Use:") %>
|
<%= gettext("Easy to Use:") %>
|
||||||
</b>
|
</b>
|
||||||
@ -37,8 +36,7 @@
|
|||||||
<%= gettext("Your data stays with you, period") %>
|
<%= gettext("Your data stays with you, period") %>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
<li class="flex flex-col justify-center items-center
|
<li class="flex flex-col justify-center items-center space-y-2">
|
||||||
space-y-2">
|
|
||||||
<b class="whitespace-nowrap">
|
<b class="whitespace-nowrap">
|
||||||
<%= gettext("Simple:") %>
|
<%= gettext("Simple:") %>
|
||||||
</b>
|
</b>
|
||||||
@ -66,9 +64,13 @@
|
|||||||
</.link>
|
</.link>
|
||||||
<% else %>
|
<% else %>
|
||||||
<div class="flex flex-wrap justify-center space-x-2">
|
<div class="flex flex-wrap justify-center space-x-2">
|
||||||
<a :for={%{email: email} <- @admins} class="hover:underline" href={"mailto:#{email}"}>
|
<.link
|
||||||
|
:for={%{email: email} <- @admins}
|
||||||
|
class="hover:underline"
|
||||||
|
href={"mailto:#{email}"}
|
||||||
|
>
|
||||||
<%= email %>
|
<%= email %>
|
||||||
</a>
|
</.link>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
</p>
|
</p>
|
||||||
@ -77,9 +79,9 @@
|
|||||||
<li class="flex flex-row justify-center space-x-2">
|
<li class="flex flex-row justify-center space-x-2">
|
||||||
<b><%= gettext("Registration:") %></b>
|
<b><%= gettext("Registration:") %></b>
|
||||||
<p>
|
<p>
|
||||||
<%= case Application.get_env(:cannery, Cannery.Accounts)[:registration] do
|
<%= case Accounts.registration_mode() do
|
||||||
"public" -> gettext("Public Signups")
|
:public -> gettext("Public Signups")
|
||||||
_ -> gettext("Invite Only")
|
:invite_only -> gettext("Invite Only")
|
||||||
end %>
|
end %>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
@ -4,24 +4,13 @@ defmodule CanneryWeb.InviteLive.Index do
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
use CanneryWeb, :live_view
|
use CanneryWeb, :live_view
|
||||||
import CanneryWeb.Components.{InviteCard, UserCard}
|
|
||||||
alias Cannery.Accounts
|
alias Cannery.Accounts
|
||||||
alias Cannery.Accounts.{Invite, Invites}
|
alias Cannery.Accounts.{Invite, Invites}
|
||||||
alias CanneryWeb.HomeLive
|
|
||||||
alias Phoenix.LiveView.JS
|
alias Phoenix.LiveView.JS
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def mount(_params, _session, %{assigns: %{current_user: current_user}} = socket) do
|
def mount(_params, _session, socket) do
|
||||||
socket =
|
{:ok, socket |> display_invites()}
|
||||||
if current_user |> Map.get(:role) == :admin do
|
|
||||||
socket |> display_invites()
|
|
||||||
else
|
|
||||||
prompt = dgettext("errors", "You are not authorized to view this page")
|
|
||||||
return_to = Routes.live_path(Endpoint, HomeLive)
|
|
||||||
socket |> put_flash(:error, prompt) |> push_redirect(to: return_to)
|
|
||||||
end
|
|
||||||
|
|
||||||
{:ok, socket}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
@ -30,8 +19,8 @@ defmodule CanneryWeb.InviteLive.Index do
|
|||||||
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
|
||||||
socket
|
invite = Invites.get_invite!(id, current_user)
|
||||||
|> assign(page_title: gettext("Edit Invite"), invite: Invites.get_invite!(id, current_user))
|
socket |> assign(page_title: gettext("Edit Invite"), invite: invite)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp apply_action(socket, :new, _params) do
|
defp apply_action(socket, :new, _params) do
|
||||||
@ -123,22 +112,17 @@ defmodule CanneryWeb.InviteLive.Index do
|
|||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event("copy_to_clipboard", _params, socket) do
|
def handle_event("copy_to_clipboard", _params, socket) do
|
||||||
prompt = dgettext("prompts", "Copied to clipboard")
|
{:noreply, socket |> put_flash(:info, dgettext("prompts", "Copied to clipboard"))}
|
||||||
{:noreply, socket |> put_flash(:info, prompt)}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event(
|
def handle_event(
|
||||||
"delete_user",
|
"delete_user",
|
||||||
%{"id" => id},
|
%{"id" => id},
|
||||||
%{assigns: %{current_user: current_user}} = socket
|
%{assigns: %{current_user: current_user}} = socket
|
||||||
) do
|
) do
|
||||||
%{email: user_email} = Accounts.get_user!(id) |> Accounts.delete_user!(current_user)
|
%{email: user_email} = Accounts.get_user!(id) |> Accounts.delete_user!(current_user)
|
||||||
|
|
||||||
prompt = dgettext("prompts", "%{user_email} deleted succesfully", user_email: user_email)
|
prompt = dgettext("prompts", "%{user_email} deleted succesfully", user_email: user_email)
|
||||||
|
|
||||||
{:noreply, socket |> put_flash(:info, prompt) |> display_invites()}
|
{:noreply, socket |> put_flash(:info, prompt) |> display_invites()}
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -151,7 +135,8 @@ defmodule CanneryWeb.InviteLive.Index do
|
|||||||
|> Map.get(:admin, [])
|
|> Map.get(:admin, [])
|
||||||
|> Enum.reject(fn %{id: user_id} -> user_id == current_user.id end)
|
|> Enum.reject(fn %{id: user_id} -> user_id == current_user.id end)
|
||||||
|
|
||||||
|
use_counts = invites |> Invites.get_use_counts(current_user)
|
||||||
users = all_users |> Map.get(:user, [])
|
users = all_users |> Map.get(:user, [])
|
||||||
socket |> assign(invites: invites, admins: admins, users: users)
|
socket |> assign(invites: invites, use_counts: use_counts, admins: admins, users: users)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -19,13 +19,21 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="flex flex-col justify-center items-stretch space-y-4">
|
<div class="flex flex-col justify-center items-stretch space-y-4">
|
||||||
<.invite_card :for={invite <- @invites} invite={invite} current_user={@current_user}>
|
<.invite_card
|
||||||
|
:for={invite <- @invites}
|
||||||
|
invite={invite}
|
||||||
|
current_user={@current_user}
|
||||||
|
use_count={Map.get(@use_counts, invite.id)}
|
||||||
|
>
|
||||||
<:code_actions>
|
<:code_actions>
|
||||||
<form phx-submit="copy_to_clipboard">
|
<form phx-submit="copy_to_clipboard">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="mx-2 my-1 btn btn-primary"
|
class="mx-2 my-1 btn btn-primary"
|
||||||
phx-click={JS.dispatch("cannery:clipcopy", to: "#code-#{invite.id}")}
|
phx-click={JS.dispatch("cannery:clipcopy", to: "#code-#{invite.id}")}
|
||||||
|
aria-label={
|
||||||
|
dgettext("actions", "Copy invite link for %{invite_name}", invite_name: invite.name)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<%= dgettext("actions", "Copy to clipboard") %>
|
<%= dgettext("actions", "Copy to clipboard") %>
|
||||||
</button>
|
</button>
|
||||||
@ -34,7 +42,9 @@
|
|||||||
<.link
|
<.link
|
||||||
patch={Routes.invite_index_path(Endpoint, :edit, invite)}
|
patch={Routes.invite_index_path(Endpoint, :edit, invite)}
|
||||||
class="text-primary-600 link"
|
class="text-primary-600 link"
|
||||||
data-qa={"edit-#{invite.id}"}
|
aria-label={
|
||||||
|
dgettext("actions", "Edit invite for %{invite_name}", invite_name: invite.name)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-edit"></i>
|
<i class="fa-fw fa-lg fas fa-edit"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -49,21 +59,23 @@
|
|||||||
invite_name: invite.name
|
invite_name: invite.name
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
data-qa={"delete-#{invite.id}"}
|
aria-label={
|
||||||
|
dgettext("actions", "Delete invite for %{invite_name}", invite_name: invite.name)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-trash"></i>
|
<i class="fa-fw fa-lg fas fa-trash"></i>
|
||||||
</.link>
|
</.link>
|
||||||
|
|
||||||
<a
|
<.link
|
||||||
href="#"
|
href="#"
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
phx-click={if invite.disabled_at, do: "enable_invite", else: "disable_invite"}
|
phx-click={if invite.disabled_at, do: "enable_invite", else: "disable_invite"}
|
||||||
phx-value-id={invite.id}
|
phx-value-id={invite.id}
|
||||||
>
|
>
|
||||||
<%= if invite.disabled_at, do: gettext("Enable"), else: gettext("Disable") %>
|
<%= if invite.disabled_at, do: gettext("Enable"), else: gettext("Disable") %>
|
||||||
</a>
|
</.link>
|
||||||
|
|
||||||
<a
|
<.link
|
||||||
:if={invite.disabled_at |> is_nil() and not (invite.uses_left |> is_nil())}
|
:if={invite.disabled_at |> is_nil() and not (invite.uses_left |> is_nil())}
|
||||||
href="#"
|
href="#"
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
@ -76,7 +88,7 @@
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<%= dgettext("actions", "Set Unlimited") %>
|
<%= dgettext("actions", "Set Unlimited") %>
|
||||||
</a>
|
</.link>
|
||||||
</.invite_card>
|
</.invite_card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,128 +0,0 @@
|
|||||||
defmodule CanneryWeb.LiveHelpers do
|
|
||||||
@moduledoc """
|
|
||||||
Contains common helper functions for liveviews
|
|
||||||
"""
|
|
||||||
|
|
||||||
import Phoenix.Component
|
|
||||||
alias Phoenix.LiveView.JS
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Renders a live component inside a modal.
|
|
||||||
|
|
||||||
The rendered modal receives a `:return_to` option to properly update
|
|
||||||
the URL when the modal is closed.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
<.modal return_to={Routes.<%= schema.singular %>_index_path(Endpoint, :index)}>
|
|
||||||
<.live_component
|
|
||||||
module={<%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Live.FormComponent}
|
|
||||||
id={@<%= schema.singular %>.id || :new}
|
|
||||||
title={@page_title}
|
|
||||||
action={@live_action}
|
|
||||||
return_to={Routes.<%= schema.singular %>_index_path(Endpoint, :index)}
|
|
||||||
<%= schema.singular %>: @<%= schema.singular %>
|
|
||||||
/>
|
|
||||||
</.modal>
|
|
||||||
"""
|
|
||||||
def modal(assigns) do
|
|
||||||
~H"""
|
|
||||||
<.link
|
|
||||||
patch={@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()}
|
|
||||||
>
|
|
||||||
<span class="hidden"></span>
|
|
||||||
</.link>
|
|
||||||
|
|
||||||
<div
|
|
||||||
id="modal"
|
|
||||||
class="fixed z-10 left-0 top-0 pointer-events-none
|
|
||||||
w-full h-full overflow-hidden
|
|
||||||
p-4 sm:p-8 flex flex-col justify-center items-center"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
id="modal-content"
|
|
||||||
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-start items-center
|
|
||||||
bg-white border-2 rounded-lg"
|
|
||||||
>
|
|
||||||
<.link
|
|
||||||
patch={@return_to}
|
|
||||||
id="close"
|
|
||||||
class="absolute top-8 right-10
|
|
||||||
text-gray-500 hover:text-gray-800
|
|
||||||
transition-all duration-500 ease-in-out"
|
|
||||||
phx-remove={hide_modal()}
|
|
||||||
>
|
|
||||||
<i class="fa-fw fa-lg fas fa-times"></i>
|
|
||||||
</.link>
|
|
||||||
|
|
||||||
<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) %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
defp hide_modal(js \\ %JS{}) do
|
|
||||||
js
|
|
||||||
|> JS.hide(to: "#modal", transition: "fade-out")
|
|
||||||
|> JS.hide(to: "#modal-bg", transition: "fade-out")
|
|
||||||
|> JS.hide(to: "#modal-content", transition: "fade-out-scale")
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
A toggle button element that can be directed to a liveview or a
|
|
||||||
live_component's `handle_event/3`.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
<.toggle_button action="my_liveview_action" value={@some_value}>
|
|
||||||
<span>Toggle me!</span>
|
|
||||||
</.toggle_button>
|
|
||||||
<.toggle_button action="my_live_component_action" target={@myself} value={@some_value}>
|
|
||||||
<span>Whatever you want</span>
|
|
||||||
</.toggle_button>
|
|
||||||
"""
|
|
||||||
def toggle_button(assigns) do
|
|
||||||
assigns = assigns |> assign_new(:id, fn -> assigns.action end)
|
|
||||||
|
|
||||||
~H"""
|
|
||||||
<label for={@id} class="inline-flex relative items-center cursor-pointer">
|
|
||||||
<input
|
|
||||||
id={@id}
|
|
||||||
type="checkbox"
|
|
||||||
value={@value}
|
|
||||||
checked={@value}
|
|
||||||
class="sr-only peer"
|
|
||||||
data-qa={@id}
|
|
||||||
{
|
|
||||||
if assigns |> Map.has_key?(:target),
|
|
||||||
do: %{"phx-click": @action, "phx-value-value": @value, "phx-target": @target},
|
|
||||||
else: %{"phx-click": @action, "phx-value-value": @value}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<div class="w-11 h-6 bg-gray-300 rounded-full peer
|
|
||||||
peer-focus:ring-4 peer-focus:ring-teal-300 dark:peer-focus:ring-teal-800
|
|
||||||
peer-checked:bg-gray-600
|
|
||||||
peer-checked:after:translate-x-full peer-checked:after:border-white
|
|
||||||
after:content-[''] after:absolute after:top-1 after:left-[2px] after:bg-white after:border-gray-300
|
|
||||||
after:border after:rounded-full after:h-5 after:w-5
|
|
||||||
after:transition-all after:duration-250 after:ease-in-out
|
|
||||||
transition-colors duration-250 ease-in-out">
|
|
||||||
</div>
|
|
||||||
<span class="ml-3 text-sm font-medium text-gray-900 dark:text-gray-300">
|
|
||||||
<%= render_slot(@inner_block) %>
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
end
|
|
@ -5,8 +5,12 @@ defmodule CanneryWeb.RangeLive.FormComponent do
|
|||||||
|
|
||||||
use CanneryWeb, :live_component
|
use CanneryWeb, :live_component
|
||||||
alias Cannery.{Accounts.User, ActivityLog, ActivityLog.ShotGroup, Ammo, Ammo.AmmoGroup}
|
alias Cannery.{Accounts.User, ActivityLog, ActivityLog.ShotGroup, Ammo, Ammo.AmmoGroup}
|
||||||
|
alias Ecto.Changeset
|
||||||
alias Phoenix.LiveView.Socket
|
alias Phoenix.LiveView.Socket
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def mount(socket), do: {:ok, socket |> assign(:ammo_group, nil)}
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
@spec update(
|
@spec update(
|
||||||
%{
|
%{
|
||||||
@ -19,28 +23,23 @@ defmodule CanneryWeb.RangeLive.FormComponent do
|
|||||||
) :: {:ok, Socket.t()}
|
) :: {:ok, Socket.t()}
|
||||||
def update(
|
def update(
|
||||||
%{
|
%{
|
||||||
shot_group: %ShotGroup{ammo_group_id: ammo_group_id} = shot_group,
|
shot_group: %ShotGroup{ammo_group_id: ammo_group_id},
|
||||||
current_user: current_user
|
current_user: current_user
|
||||||
} = assigns,
|
} = assigns,
|
||||||
socket
|
socket
|
||||||
) do
|
)
|
||||||
changeset = shot_group |> ShotGroup.update_changeset(current_user, %{})
|
when is_binary(ammo_group_id) do
|
||||||
ammo_group = Ammo.get_ammo_group!(ammo_group_id, current_user)
|
ammo_group = Ammo.get_ammo_group!(ammo_group_id, current_user)
|
||||||
{:ok, socket |> assign(assigns) |> assign(ammo_group: ammo_group, changeset: changeset)}
|
{:ok, socket |> assign(assigns) |> assign(:ammo_group, ammo_group) |> assign_changeset(%{})}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update(%{shot_group: %ShotGroup{}} = assigns, socket) do
|
||||||
|
{:ok, socket |> assign(assigns) |> assign_changeset(%{})}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event(
|
def handle_event("validate", %{"shot_group" => shot_group_params}, socket) do
|
||||||
"validate",
|
{:noreply, socket |> assign_changeset(shot_group_params, :validate)}
|
||||||
%{"shot_group" => shot_group_params},
|
|
||||||
%{assigns: %{current_user: current_user, shot_group: shot_group}} = socket
|
|
||||||
) do
|
|
||||||
changeset =
|
|
||||||
shot_group
|
|
||||||
|> ShotGroup.update_changeset(current_user, shot_group_params)
|
|
||||||
|> Map.put(:action, :validate)
|
|
||||||
|
|
||||||
{:noreply, assign(socket, :changeset, changeset)}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_event(
|
def handle_event(
|
||||||
@ -61,4 +60,37 @@ defmodule CanneryWeb.RangeLive.FormComponent do
|
|||||||
|
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp assign_changeset(
|
||||||
|
%{
|
||||||
|
assigns: %{
|
||||||
|
action: live_action,
|
||||||
|
current_user: user,
|
||||||
|
ammo_group: ammo_group,
|
||||||
|
shot_group: shot_group
|
||||||
|
}
|
||||||
|
} = socket,
|
||||||
|
shot_group_params,
|
||||||
|
action \\ nil
|
||||||
|
) do
|
||||||
|
default_action =
|
||||||
|
case live_action do
|
||||||
|
:add_shot_group -> :insert
|
||||||
|
editing when editing in [:edit, :edit_shot_group] -> :update
|
||||||
|
end
|
||||||
|
|
||||||
|
changeset =
|
||||||
|
case default_action do
|
||||||
|
:insert -> shot_group |> ShotGroup.create_changeset(user, ammo_group, shot_group_params)
|
||||||
|
:update -> shot_group |> ShotGroup.update_changeset(user, shot_group_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
changeset =
|
||||||
|
case changeset |> Changeset.apply_action(action || default_action) do
|
||||||
|
{:ok, _data} -> changeset
|
||||||
|
{:error, changeset} -> changeset
|
||||||
|
end
|
||||||
|
|
||||||
|
socket |> assign(:changeset, changeset)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -29,8 +29,11 @@
|
|||||||
|
|
||||||
<%= label(f, :notes, gettext("Notes"), class: "title text-lg text-primary-600") %>
|
<%= label(f, :notes, gettext("Notes"), class: "title text-lg text-primary-600") %>
|
||||||
<%= textarea(f, :notes,
|
<%= textarea(f, :notes,
|
||||||
|
id: "shot-group-form-notes",
|
||||||
class: "input input-primary col-span-2",
|
class: "input input-primary col-span-2",
|
||||||
phx_hook: "MaintainAttrs"
|
placeholder: gettext("Really great weather"),
|
||||||
|
phx_hook: "MaintainAttrs",
|
||||||
|
phx_update: "ignore"
|
||||||
) %>
|
) %>
|
||||||
<%= error_tag(f, :notes, "col-span-3") %>
|
<%= error_tag(f, :notes, "col-span-3") %>
|
||||||
|
|
||||||
|
@ -4,8 +4,7 @@ defmodule CanneryWeb.RangeLive.Index do
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
use CanneryWeb, :live_view
|
use CanneryWeb, :live_view
|
||||||
import CanneryWeb.Components.AmmoGroupCard
|
alias Cannery.{ActivityLog, ActivityLog.ShotGroup, Ammo}
|
||||||
alias Cannery.{ActivityLog, ActivityLog.ShotGroup, Ammo, Repo}
|
|
||||||
alias CanneryWeb.Endpoint
|
alias CanneryWeb.Endpoint
|
||||||
alias Phoenix.LiveView.Socket
|
alias Phoenix.LiveView.Socket
|
||||||
|
|
||||||
@ -81,7 +80,6 @@ defmodule CanneryWeb.RangeLive.Index do
|
|||||||
{:noreply, socket |> put_flash(:info, prompt) |> display_shot_groups()}
|
{:noreply, socket |> put_flash(:info, prompt) |> display_shot_groups()}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event(
|
def handle_event(
|
||||||
"toggle_staged",
|
"toggle_staged",
|
||||||
%{"ammo_group_id" => ammo_group_id},
|
%{"ammo_group_id" => ammo_group_id},
|
||||||
@ -96,7 +94,6 @@ defmodule CanneryWeb.RangeLive.Index do
|
|||||||
{:noreply, socket |> put_flash(:info, prompt) |> display_shot_groups()}
|
{:noreply, socket |> put_flash(:info, prompt) |> display_shot_groups()}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event("search", %{"search" => %{"search_term" => ""}}, socket) do
|
def handle_event("search", %{"search" => %{"search_term" => ""}}, socket) do
|
||||||
{:noreply, socket |> push_patch(to: Routes.range_index_path(Endpoint, :index))}
|
{:noreply, socket |> push_patch(to: Routes.range_index_path(Endpoint, :index))}
|
||||||
end
|
end
|
||||||
@ -107,16 +104,19 @@ defmodule CanneryWeb.RangeLive.Index do
|
|||||||
|
|
||||||
@spec display_shot_groups(Socket.t()) :: Socket.t()
|
@spec display_shot_groups(Socket.t()) :: Socket.t()
|
||||||
defp display_shot_groups(%{assigns: %{search: search, current_user: current_user}} = socket) do
|
defp display_shot_groups(%{assigns: %{search: search, current_user: current_user}} = socket) do
|
||||||
shot_groups =
|
shot_groups = ActivityLog.list_shot_groups(search, current_user)
|
||||||
ActivityLog.list_shot_groups(search, 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)
|
||||||
chart_data = shot_groups |> get_chart_data_for_shot_group()
|
chart_data = shot_groups |> get_chart_data_for_shot_group()
|
||||||
|
original_counts = ammo_groups |> Ammo.get_original_counts(current_user)
|
||||||
|
cprs = ammo_groups |> Ammo.get_cprs(current_user)
|
||||||
|
last_used_dates = ammo_groups |> ActivityLog.get_last_used_dates(current_user)
|
||||||
|
|
||||||
socket
|
socket
|
||||||
|> assign(
|
|> assign(
|
||||||
ammo_groups: ammo_groups,
|
ammo_groups: ammo_groups,
|
||||||
|
original_counts: original_counts,
|
||||||
|
cprs: cprs,
|
||||||
|
last_used_dates: last_used_dates,
|
||||||
chart_data: chart_data,
|
chart_data: chart_data,
|
||||||
shot_groups: shot_groups
|
shot_groups: shot_groups
|
||||||
)
|
)
|
||||||
@ -125,7 +125,6 @@ defmodule CanneryWeb.RangeLive.Index do
|
|||||||
@spec get_chart_data_for_shot_group([ShotGroup.t()]) :: [map()]
|
@spec get_chart_data_for_shot_group([ShotGroup.t()]) :: [map()]
|
||||||
defp get_chart_data_for_shot_group(shot_groups) do
|
defp get_chart_data_for_shot_group(shot_groups) do
|
||||||
shot_groups
|
shot_groups
|
||||||
|> Repo.preload(ammo_group: :ammo_type)
|
|
||||||
|> Enum.group_by(fn %{date: date} -> date end, fn %{count: count} -> count end)
|
|> Enum.group_by(fn %{date: date} -> date end, fn %{count: count} -> count end)
|
||||||
|> Enum.map(fn {date, rounds} ->
|
|> Enum.map(fn {date, rounds} ->
|
||||||
sum = Enum.sum(rounds)
|
sum = Enum.sum(rounds)
|
||||||
|
@ -18,7 +18,14 @@
|
|||||||
</.link>
|
</.link>
|
||||||
|
|
||||||
<div class="w-full flex flex-row flex-wrap justify-center items-stretch">
|
<div class="w-full flex flex-row flex-wrap justify-center items-stretch">
|
||||||
<.ammo_group_card :for={ammo_group <- @ammo_groups} ammo_group={ammo_group}>
|
<.ammo_group_card
|
||||||
|
:for={%{id: ammo_group_id} = ammo_group <- @ammo_groups}
|
||||||
|
ammo_group={ammo_group}
|
||||||
|
original_count={Map.fetch!(@original_counts, ammo_group_id)}
|
||||||
|
cpr={Map.get(@cprs, ammo_group_id)}
|
||||||
|
last_used_date={Map.get(@last_used_dates, ammo_group_id)}
|
||||||
|
current_user={@current_user}
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
@ -70,15 +77,16 @@
|
|||||||
<div class="w-full flex flex-col sm:flex-row justify-center items-center space-y-4 sm:space-y-0 sm:space-x-4 max-w-xl">
|
<div class="w-full flex flex-col sm:flex-row justify-center items-center space-y-4 sm:space-y-0 sm:space-x-4 max-w-xl">
|
||||||
<.form
|
<.form
|
||||||
:let={f}
|
:let={f}
|
||||||
for={:search}
|
for={%{}}
|
||||||
|
as={:search}
|
||||||
phx-change="search"
|
phx-change="search"
|
||||||
phx-submit="search"
|
phx-submit="search"
|
||||||
class="grow self-stretch flex flex-col items-stretch"
|
class="grow self-stretch flex flex-col items-stretch"
|
||||||
data-qa="shot_group_search"
|
|
||||||
>
|
>
|
||||||
<%= text_input(f, :search_term,
|
<%= text_input(f, :search_term,
|
||||||
class: "input input-primary",
|
class: "input input-primary",
|
||||||
value: @search,
|
value: @search,
|
||||||
|
role: "search",
|
||||||
phx_debounce: 300,
|
phx_debounce: 300,
|
||||||
placeholder: gettext("Search shot records")
|
placeholder: gettext("Search shot records")
|
||||||
) %>
|
) %>
|
||||||
@ -102,7 +110,11 @@
|
|||||||
<.link
|
<.link
|
||||||
patch={Routes.range_index_path(Endpoint, :edit, shot_group)}
|
patch={Routes.range_index_path(Endpoint, :edit, shot_group)}
|
||||||
class="text-primary-600 link"
|
class="text-primary-600 link"
|
||||||
data-qa={"edit-#{shot_group.id}"}
|
aria-label={
|
||||||
|
dgettext("actions", "Edit shot record of %{shot_group_count} shots",
|
||||||
|
shot_group_count: shot_group.count
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-edit"></i>
|
<i class="fa-fw fa-lg fas fa-edit"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -115,7 +127,11 @@
|
|||||||
data-confirm={
|
data-confirm={
|
||||||
dgettext("prompts", "Are you sure you want to delete this shot record?")
|
dgettext("prompts", "Are you sure you want to delete this shot record?")
|
||||||
}
|
}
|
||||||
data-qa={"delete-#{shot_group.id}"}
|
aria-label={
|
||||||
|
dgettext("actions", "Delete shot record of %{shot_group_count} shots",
|
||||||
|
shot_group_count: shot_group.count
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-trash"></i>
|
<i class="fa-fw fa-lg fas fa-trash"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -126,29 +142,30 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<.modal :if={@live_action == :edit} return_to={Routes.range_index_path(Endpoint, :index)}>
|
<%= case @live_action do %>
|
||||||
<.live_component
|
<% :edit -> %>
|
||||||
module={CanneryWeb.RangeLive.FormComponent}
|
<.modal return_to={Routes.range_index_path(Endpoint, :index)}>
|
||||||
id={@shot_group.id}
|
<.live_component
|
||||||
title={@page_title}
|
module={CanneryWeb.RangeLive.FormComponent}
|
||||||
action={@live_action}
|
id={@shot_group.id}
|
||||||
shot_group={@shot_group}
|
title={@page_title}
|
||||||
return_to={Routes.range_index_path(Endpoint, :index)}
|
action={@live_action}
|
||||||
current_user={@current_user}
|
shot_group={@shot_group}
|
||||||
/>
|
return_to={Routes.range_index_path(Endpoint, :index)}
|
||||||
</.modal>
|
current_user={@current_user}
|
||||||
|
/>
|
||||||
<.modal
|
</.modal>
|
||||||
:if={@live_action == :add_shot_group}
|
<% :add_shot_group -> %>
|
||||||
return_to={Routes.range_index_path(Endpoint, :index)}
|
<.modal return_to={Routes.range_index_path(Endpoint, :index)}>
|
||||||
>
|
<.live_component
|
||||||
<.live_component
|
module={CanneryWeb.Components.AddShotGroupComponent}
|
||||||
module={CanneryWeb.Components.AddShotGroupComponent}
|
id={:new}
|
||||||
id={:new}
|
title={@page_title}
|
||||||
title={@page_title}
|
action={@live_action}
|
||||||
action={@live_action}
|
ammo_group={@ammo_group}
|
||||||
ammo_group={@ammo_group}
|
return_to={Routes.range_index_path(Endpoint, :index)}
|
||||||
return_to={Routes.range_index_path(Endpoint, :index)}
|
current_user={@current_user}
|
||||||
current_user={@current_user}
|
/>
|
||||||
/>
|
</.modal>
|
||||||
</.modal>
|
<% _ -> %>
|
||||||
|
<% end %>
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
defmodule CanneryWeb.TagLive.FormComponent do
|
defmodule CanneryWeb.TagLive.FormComponent do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
Livecomponent that can update or create an Cannery.Tags.Tag
|
Livecomponent that can update or create an Cannery.Containers.Tag
|
||||||
"""
|
"""
|
||||||
|
|
||||||
use CanneryWeb, :live_component
|
use CanneryWeb, :live_component
|
||||||
alias Cannery.Tags
|
alias Cannery.{Accounts.User, Containers, Containers.Tag}
|
||||||
alias Cannery.{Accounts.User, Tags.Tag}
|
|
||||||
alias Ecto.Changeset
|
alias Ecto.Changeset
|
||||||
alias Phoenix.LiveView.Socket
|
alias Phoenix.LiveView.Socket
|
||||||
|
|
||||||
@ -56,7 +55,7 @@ defmodule CanneryWeb.TagLive.FormComponent do
|
|||||||
tag_params
|
tag_params
|
||||||
) do
|
) do
|
||||||
socket =
|
socket =
|
||||||
case Tags.update_tag(tag, tag_params, current_user) do
|
case Containers.update_tag(tag, tag_params, current_user) do
|
||||||
{:ok, %{name: tag_name}} ->
|
{:ok, %{name: tag_name}} ->
|
||||||
prompt = dgettext("prompts", "%{name} updated successfully", name: tag_name)
|
prompt = dgettext("prompts", "%{name} updated successfully", name: tag_name)
|
||||||
socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
|
socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
|
||||||
@ -74,7 +73,7 @@ defmodule CanneryWeb.TagLive.FormComponent do
|
|||||||
tag_params
|
tag_params
|
||||||
) do
|
) do
|
||||||
socket =
|
socket =
|
||||||
case Tags.create_tag(tag_params, current_user) do
|
case Containers.create_tag(tag_params, current_user) do
|
||||||
{:ok, %{name: tag_name}} ->
|
{:ok, %{name: tag_name}} ->
|
||||||
prompt = dgettext("prompts", "%{name} created successfully", name: tag_name)
|
prompt = dgettext("prompts", "%{name} created successfully", name: tag_name)
|
||||||
socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
|
socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
defmodule CanneryWeb.TagLive.Index do
|
defmodule CanneryWeb.TagLive.Index do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
Liveview to show a Cannery.Tags.Tag index
|
Liveview to show a Cannery.Containers.Tag index
|
||||||
"""
|
"""
|
||||||
|
|
||||||
use CanneryWeb, :live_view
|
use CanneryWeb, :live_view
|
||||||
import CanneryWeb.Components.TagCard
|
alias Cannery.{Containers, Containers.Tag}
|
||||||
alias Cannery.{Tags, Tags.Tag}
|
|
||||||
alias CanneryWeb.ViewHelpers
|
alias CanneryWeb.ViewHelpers
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
@ -26,7 +25,7 @@ defmodule CanneryWeb.TagLive.Index do
|
|||||||
socket
|
socket
|
||||||
|> assign(
|
|> assign(
|
||||||
page_title: gettext("Edit Tag"),
|
page_title: gettext("Edit Tag"),
|
||||||
tag: Tags.get_tag!(id, current_user)
|
tag: Containers.get_tag!(id, current_user)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -60,12 +59,13 @@ defmodule CanneryWeb.TagLive.Index do
|
|||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("delete", %{"id" => id}, %{assigns: %{current_user: current_user}} = socket) do
|
def handle_event("delete", %{"id" => id}, %{assigns: %{current_user: current_user}} = socket) do
|
||||||
%{name: tag_name} = Tags.get_tag!(id, current_user) |> Tags.delete_tag!(current_user)
|
%{name: tag_name} =
|
||||||
|
Containers.get_tag!(id, current_user) |> Containers.delete_tag!(current_user)
|
||||||
|
|
||||||
prompt = dgettext("prompts", "%{name} deleted succesfully", name: tag_name)
|
prompt = dgettext("prompts", "%{name} deleted succesfully", name: tag_name)
|
||||||
{:noreply, socket |> put_flash(:info, prompt) |> display_tags()}
|
{:noreply, socket |> put_flash(:info, prompt) |> display_tags()}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_event("search", %{"search" => %{"search_term" => ""}}, socket) do
|
def handle_event("search", %{"search" => %{"search_term" => ""}}, socket) do
|
||||||
{:noreply, socket |> push_patch(to: Routes.tag_index_path(Endpoint, :index))}
|
{:noreply, socket |> push_patch(to: Routes.tag_index_path(Endpoint, :index))}
|
||||||
end
|
end
|
||||||
@ -75,6 +75,6 @@ defmodule CanneryWeb.TagLive.Index do
|
|||||||
end
|
end
|
||||||
|
|
||||||
defp display_tags(%{assigns: %{search: search, current_user: current_user}} = socket) do
|
defp display_tags(%{assigns: %{search: search, current_user: current_user}} = socket) do
|
||||||
socket |> assign(tags: Tags.list_tags(search, current_user))
|
socket |> assign(tags: Containers.list_tags(search, current_user))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -23,15 +23,16 @@
|
|||||||
<div class="w-full flex flex-col sm:flex-row justify-center items-center space-y-4 sm:space-y-0 sm:space-x-4 max-w-xl">
|
<div class="w-full flex flex-col sm:flex-row justify-center items-center space-y-4 sm:space-y-0 sm:space-x-4 max-w-xl">
|
||||||
<.form
|
<.form
|
||||||
:let={f}
|
:let={f}
|
||||||
for={:search}
|
for={%{}}
|
||||||
|
as={:search}
|
||||||
phx-change="search"
|
phx-change="search"
|
||||||
phx-submit="search"
|
phx-submit="search"
|
||||||
class="grow self-stretch flex flex-col items-stretch"
|
class="grow self-stretch flex flex-col items-stretch"
|
||||||
data-qa="tag_search"
|
|
||||||
>
|
>
|
||||||
<%= text_input(f, :search_term,
|
<%= text_input(f, :search_term,
|
||||||
class: "input input-primary",
|
class: "input input-primary",
|
||||||
value: @search,
|
value: @search,
|
||||||
|
role: "search",
|
||||||
phx_debounce: 300,
|
phx_debounce: 300,
|
||||||
placeholder: gettext("Search tags")
|
placeholder: gettext("Search tags")
|
||||||
) %>
|
) %>
|
||||||
@ -49,7 +50,7 @@
|
|||||||
<.link
|
<.link
|
||||||
patch={Routes.tag_index_path(Endpoint, :edit, tag)}
|
patch={Routes.tag_index_path(Endpoint, :edit, tag)}
|
||||||
class="text-primary-600 link"
|
class="text-primary-600 link"
|
||||||
data-qa={"edit-#{tag.id}"}
|
aria-label={dgettext("actions", "Edit %{tag_name}", tag_name: tag.name)}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-edit"></i>
|
<i class="fa-fw fa-lg fas fa-edit"></i>
|
||||||
</.link>
|
</.link>
|
||||||
@ -62,7 +63,7 @@
|
|||||||
data-confirm={
|
data-confirm={
|
||||||
dgettext("prompts", "Are you sure you want to delete %{name}?", name: tag.name)
|
dgettext("prompts", "Are you sure you want to delete %{name}?", name: tag.name)
|
||||||
}
|
}
|
||||||
data-qa={"delete-#{tag.id}"}
|
aria-label={dgettext("actions", "Delete %{tag_name}", tag_name: tag.name)}
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-lg fas fa-trash"></i>
|
<i class="fa-fw fa-lg fas fa-trash"></i>
|
||||||
</.link>
|
</.link>
|
||||||
|
@ -24,9 +24,12 @@
|
|||||||
|
|
||||||
<hr class="w-full hr" />
|
<hr class="w-full hr" />
|
||||||
|
|
||||||
<a href={Routes.live_path(Endpoint, HomeLive)} class="link title text-primary-600 text-lg">
|
<.link
|
||||||
|
href={Routes.live_path(Endpoint, HomeLive)}
|
||||||
|
class="link title text-primary-600 text-lg"
|
||||||
|
>
|
||||||
<%= dgettext("errors", "Go back home") %>
|
<%= dgettext("errors", "Go back home") %>
|
||||||
</a>
|
</.link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
@ -5,7 +5,8 @@
|
|||||||
|
|
||||||
<.form
|
<.form
|
||||||
:let={f}
|
:let={f}
|
||||||
for={:user}
|
for={%{}}
|
||||||
|
as={:user}
|
||||||
action={Routes.user_confirmation_path(@conn, :create)}
|
action={Routes.user_confirmation_path(@conn, :create)}
|
||||||
class="flex flex-col space-y-4 sm:space-y-0 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"
|
||||||
>
|
>
|
||||||
|
@ -5,7 +5,8 @@
|
|||||||
|
|
||||||
<.form
|
<.form
|
||||||
:let={f}
|
:let={f}
|
||||||
for={:user}
|
for={%{}}
|
||||||
|
as={:user}
|
||||||
action={Routes.user_reset_password_path(@conn, :create)}
|
action={Routes.user_reset_password_path(@conn, :create)}
|
||||||
class="flex flex-col space-y-4 sm:space-y-0 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"
|
||||||
>
|
>
|
||||||
|
@ -107,9 +107,9 @@
|
|||||||
action={Routes.user_settings_path(@conn, :update)}
|
action={Routes.user_settings_path(@conn, :update)}
|
||||||
class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
|
class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
|
||||||
>
|
>
|
||||||
<h3 class="title text-primary-600 text-lg text-center col-span-3">
|
<%= label(f, :locale, dgettext("actions", "Change Language"),
|
||||||
<%= dgettext("actions", "Change Language") %>
|
class: "title text-primary-600 text-lg text-center col-span-3"
|
||||||
</h3>
|
) %>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
:if={@locale_changeset.action && not @locale_changeset.valid?()}
|
:if={@locale_changeset.action && not @locale_changeset.valid?()}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
defmodule CanneryWeb.ErrorView do
|
defmodule CanneryWeb.ErrorView do
|
||||||
use CanneryWeb, :view
|
use CanneryWeb, :view
|
||||||
import CanneryWeb.Components.Topbar
|
|
||||||
alias CanneryWeb.HomeLive
|
alias CanneryWeb.HomeLive
|
||||||
|
|
||||||
def template_not_found(error_path, _assigns) do
|
def template_not_found(error_path, _assigns) do
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
defmodule CanneryWeb.LayoutView do
|
defmodule CanneryWeb.LayoutView do
|
||||||
use CanneryWeb, :view
|
use CanneryWeb, :view
|
||||||
import CanneryWeb.Components.Topbar
|
|
||||||
alias CanneryWeb.HomeLive
|
alias CanneryWeb.HomeLive
|
||||||
|
|
||||||
# Phoenix LiveDashboard is available only in development by default,
|
# Phoenix LiveDashboard is available only in development by default,
|
||||||
|
@ -7,61 +7,6 @@ defmodule CanneryWeb.ViewHelpers do
|
|||||||
|
|
||||||
use Phoenix.Component
|
use Phoenix.Component
|
||||||
|
|
||||||
@doc """
|
|
||||||
Phoenix.Component for a <time> element that renders the naivedatetime in the
|
|
||||||
user's local timezone with Alpine.js
|
|
||||||
"""
|
|
||||||
|
|
||||||
attr :datetime, :any, required: true, doc: "A `DateTime` struct or nil"
|
|
||||||
|
|
||||||
def datetime(assigns) do
|
|
||||||
~H"""
|
|
||||||
<time
|
|
||||||
:if={@datetime}
|
|
||||||
datetime={cast_datetime(@datetime)}
|
|
||||||
x-data={"{
|
|
||||||
datetime:
|
|
||||||
Intl.DateTimeFormat([], {dateStyle: 'short', timeStyle: 'long'})
|
|
||||||
.format(new Date(\"#{cast_datetime(@datetime)}\"))
|
|
||||||
}"}
|
|
||||||
x-text="datetime"
|
|
||||||
>
|
|
||||||
<%= cast_datetime(@datetime) %>
|
|
||||||
</time>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec cast_datetime(NaiveDateTime.t() | nil) :: String.t()
|
|
||||||
defp cast_datetime(%NaiveDateTime{} = datetime) do
|
|
||||||
datetime |> DateTime.from_naive!("Etc/UTC") |> DateTime.to_iso8601(:extended)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp cast_datetime(_datetime), do: ""
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Phoenix.Component for a <date> element that renders the Date in the user's
|
|
||||||
local timezone with Alpine.js
|
|
||||||
"""
|
|
||||||
|
|
||||||
attr :date, :any, required: true, doc: "A `Date` struct or nil"
|
|
||||||
|
|
||||||
def date(assigns) do
|
|
||||||
~H"""
|
|
||||||
<time
|
|
||||||
:if={@date}
|
|
||||||
datetime={@date |> Date.to_iso8601(:extended)}
|
|
||||||
x-data={"{
|
|
||||||
date:
|
|
||||||
Intl.DateTimeFormat([], {timeZone: 'Etc/UTC', dateStyle: 'short'})
|
|
||||||
.format(new Date(\"#{@date |> Date.to_iso8601(:extended)}\"))
|
|
||||||
}"}
|
|
||||||
x-text="date"
|
|
||||||
>
|
|
||||||
<%= @date |> Date.to_iso8601(:extended) %>
|
|
||||||
</time>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Displays emoji as text emoji if SHIBAO_MODE is set to true :)
|
Displays emoji as text emoji if SHIBAO_MODE is set to true :)
|
||||||
"""
|
"""
|
||||||
@ -87,23 +32,6 @@ defmodule CanneryWeb.ViewHelpers do
|
|||||||
"data:image/png;base64," <> img_data
|
"data:image/png;base64," <> img_data
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
|
||||||
Creates a downloadable QR Code element
|
|
||||||
"""
|
|
||||||
|
|
||||||
attr :content, :string, required: true
|
|
||||||
attr :filename, :string, default: "qrcode", doc: "filename without .png extension"
|
|
||||||
attr :image_class, :string, default: "w-64 h-max"
|
|
||||||
attr :width, :integer, default: 384, doc: "width of png to generate"
|
|
||||||
|
|
||||||
def qr_code(assigns) do
|
|
||||||
~H"""
|
|
||||||
<a href={qr_code_image(@content)} download={@filename <> ".png"}>
|
|
||||||
<img class={@image_class} alt={@filename} src={qr_code_image(@content)} />
|
|
||||||
</a>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Get a random color in `#ffffff` hex format
|
Get a random color in `#ffffff` hex format
|
||||||
|
|
||||||
|
6
mix.exs
6
mix.exs
@ -4,7 +4,7 @@ defmodule Cannery.MixProject do
|
|||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
app: :cannery,
|
app: :cannery,
|
||||||
version: "0.8.3",
|
version: "0.8.5",
|
||||||
elixir: "1.14.1",
|
elixir: "1.14.1",
|
||||||
elixirc_paths: elixirc_paths(Mix.env()),
|
elixirc_paths: elixirc_paths(Mix.env()),
|
||||||
compilers: Mix.compilers(),
|
compilers: Mix.compilers(),
|
||||||
@ -47,13 +47,13 @@ defmodule Cannery.MixProject do
|
|||||||
# Type `mix help deps` for examples and options.
|
# Type `mix help deps` for examples and options.
|
||||||
defp deps do
|
defp deps do
|
||||||
[
|
[
|
||||||
{:bcrypt_elixir, "~> 2.0"},
|
{:bcrypt_elixir, "~> 3.0"},
|
||||||
{:phoenix, "~> 1.6.0"},
|
{:phoenix, "~> 1.6.0"},
|
||||||
{:phoenix_ecto, "~> 4.4"},
|
{:phoenix_ecto, "~> 4.4"},
|
||||||
{:phoenix_html, "~> 3.0"},
|
{:phoenix_html, "~> 3.0"},
|
||||||
{:phoenix_live_reload, "~> 1.2", only: :dev},
|
{:phoenix_live_reload, "~> 1.2", only: :dev},
|
||||||
{:phoenix_live_view, "~> 0.18.0"},
|
{:phoenix_live_view, "~> 0.18.0"},
|
||||||
{:phoenix_view, "~> 1.1"},
|
{:phoenix_view, "~> 2.0"},
|
||||||
{:phoenix_live_dashboard, "~> 0.6"},
|
{:phoenix_live_dashboard, "~> 0.6"},
|
||||||
{:ecto_sql, "~> 3.6"},
|
{:ecto_sql, "~> 3.6"},
|
||||||
{:postgrex, ">= 0.0.0"},
|
{:postgrex, ">= 0.0.0"},
|
||||||
|
33
mix.lock
33
mix.lock
@ -1,5 +1,5 @@
|
|||||||
%{
|
%{
|
||||||
"bcrypt_elixir": {:hex, :bcrypt_elixir, "2.3.1", "5114d780459a04f2b4aeef52307de23de961b69e13a5cd98a911e39fda13f420", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "42182d5f46764def15bf9af83739e3bf4ad22661b1c34fc3e88558efced07279"},
|
"bcrypt_elixir": {:hex, :bcrypt_elixir, "3.0.1", "9be815469e6bfefec40fa74658ecbbe6897acfb57614df1416eeccd4903f602c", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "486bb95efb645d1efc6794c1ddd776a186a9a713abf06f45708a6ce324fb96cf"},
|
||||||
"bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
|
"bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
|
||||||
"castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"},
|
"castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"},
|
||||||
"comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"},
|
"comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"},
|
||||||
@ -11,38 +11,39 @@
|
|||||||
"db_connection": {:hex, :db_connection, "2.4.3", "3b9aac9f27347ec65b271847e6baeb4443d8474289bd18c1d6f4de655b70c94d", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c127c15b0fa6cfb32eed07465e05da6c815b032508d4ed7c116122871df73c12"},
|
"db_connection": {:hex, :db_connection, "2.4.3", "3b9aac9f27347ec65b271847e6baeb4443d8474289bd18c1d6f4de655b70c94d", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c127c15b0fa6cfb32eed07465e05da6c815b032508d4ed7c116122871df73c12"},
|
||||||
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
|
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
|
||||||
"dialyxir": {:hex, :dialyxir, "1.2.0", "58344b3e87c2e7095304c81a9ae65cb68b613e28340690dfe1a5597fd08dec37", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "61072136427a851674cab81762be4dbeae7679f85b1272b6d25c3a839aff8463"},
|
"dialyxir": {:hex, :dialyxir, "1.2.0", "58344b3e87c2e7095304c81a9ae65cb68b613e28340690dfe1a5597fd08dec37", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "61072136427a851674cab81762be4dbeae7679f85b1272b6d25c3a839aff8463"},
|
||||||
"earmark_parser": {:hex, :earmark_parser, "1.4.29", "149d50dcb3a93d9f3d6f3ecf18c918fb5a2d3c001b5d3305c926cddfbd33355b", [:mix], [], "hexpm", "4902af1b3eb139016aed210888748db8070b8125c2342ce3dcae4f38dcc63503"},
|
"earmark_parser": {:hex, :earmark_parser, "1.4.31", "a93921cdc6b9b869f519213d5bc79d9e218ba768d7270d46fdcf1c01bacff9e2", [:mix], [], "hexpm", "317d367ee0335ef037a87e46c91a2269fef6306413f731e8ec11fc45a7efd059"},
|
||||||
"ecto": {:hex, :ecto, "3.9.4", "3ee68e25dbe0c36f980f1ba5dd41ee0d3eb0873bccae8aeaf1a2647242bffa35", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "de5f988c142a3aa4ec18b85a4ec34a2390b65b24f02385c1144252ff6ff8ee75"},
|
"ecto": {:hex, :ecto, "3.9.4", "3ee68e25dbe0c36f980f1ba5dd41ee0d3eb0873bccae8aeaf1a2647242bffa35", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "de5f988c142a3aa4ec18b85a4ec34a2390b65b24f02385c1144252ff6ff8ee75"},
|
||||||
"ecto_psql_extras": {:hex, :ecto_psql_extras, "0.7.10", "e14d400930f401ca9f541b3349212634e44027d7f919bbb71224d7ac0d0e8acd", [:mix], [{:ecto_sql, "~> 3.4", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.15.7 or ~> 0.16.0", [hex: :postgrex, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "505e8cd81e4f17c090be0f99e92b1b3f0fd915f98e76965130b8ccfb891e7088"},
|
"ecto_psql_extras": {:hex, :ecto_psql_extras, "0.7.10", "e14d400930f401ca9f541b3349212634e44027d7f919bbb71224d7ac0d0e8acd", [:mix], [{:ecto_sql, "~> 3.4", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.15.7 or ~> 0.16.0", [hex: :postgrex, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "505e8cd81e4f17c090be0f99e92b1b3f0fd915f98e76965130b8ccfb891e7088"},
|
||||||
"ecto_sql": {:hex, :ecto_sql, "3.9.2", "34227501abe92dba10d9c3495ab6770e75e79b836d114c41108a4bf2ce200ad5", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1eb5eeb4358fdbcd42eac11c1fbd87e3affd7904e639d77903c1358b2abd3f70"},
|
"ecto_sql": {:hex, :ecto_sql, "3.9.2", "34227501abe92dba10d9c3495ab6770e75e79b836d114c41108a4bf2ce200ad5", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1eb5eeb4358fdbcd42eac11c1fbd87e3affd7904e639d77903c1358b2abd3f70"},
|
||||||
"elixir_make": {:hex, :elixir_make, "0.7.3", "c37fdae1b52d2cc51069713a58c2314877c1ad40800a57efb213f77b078a460d", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "24ada3e3996adbed1fa024ca14995ef2ba3d0d17b678b0f3f2b1f66e6ce2b274"},
|
"elixir_make": {:hex, :elixir_make, "0.7.5", "784cc00f5fa24239067cc04d449437dcc5f59353c44eb08f188b2b146568738a", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "c3d63e8d5c92fa3880d89ecd41de59473fa2e83eeb68148155e25e8b95aa2887"},
|
||||||
"eqrcode": {:hex, :eqrcode, "0.1.10", "6294fece9d68ad64eef1c3c92cf111cfd6469f4fbf230a2d4cc905a682178f3f", [:mix], [], "hexpm", "da30e373c36a0fd37ab6f58664b16029919896d6c45a68a95cc4d713e81076f1"},
|
"eqrcode": {:hex, :eqrcode, "0.1.10", "6294fece9d68ad64eef1c3c92cf111cfd6469f4fbf230a2d4cc905a682178f3f", [:mix], [], "hexpm", "da30e373c36a0fd37ab6f58664b16029919896d6c45a68a95cc4d713e81076f1"},
|
||||||
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
|
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
|
||||||
"ex_doc": {:hex, :ex_doc, "0.29.1", "b1c652fa5f92ee9cf15c75271168027f92039b3877094290a75abcaac82a9f77", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "b7745fa6374a36daf484e2a2012274950e084815b936b1319aeebcf7809574f6"},
|
"ex_doc": {:hex, :ex_doc, "0.29.2", "dfa97532ba66910b2a3016a4bbd796f41a86fc71dd5227e96f4c8581fdf0fdf0", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "6b5d7139eda18a753e3250e27e4a929f8d2c880dd0d460cb9986305dea3e03af"},
|
||||||
"expo": {:hex, :expo, "0.3.0", "13127c1d5f653b2927f2616a4c9ace5ae372efd67c7c2693b87fd0fdc30c6feb", [:mix], [], "hexpm", "fb3cd4bf012a77bc1608915497dae2ff684a06f0fa633c7afa90c4d72b881823"},
|
"expo": {:hex, :expo, "0.4.0", "bbe4bf455e2eb2ebd2f1e7d83530ce50fb9990eb88fc47855c515bfdf1c6626f", [:mix], [], "hexpm", "a8ed1683ec8b7c7fa53fd7a41b2c6935f539168a6bb0616d7fd6b58a36f3abf2"},
|
||||||
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
|
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
|
||||||
"floki": {:hex, :floki, "0.34.0", "002d0cc194b48794d74711731db004fafeb328fe676976f160685262d43706a8", [:mix], [], "hexpm", "9c3a9f43f40dde00332a589bd9d389b90c1f518aef500364d00636acc5ebc99c"},
|
"floki": {:hex, :floki, "0.34.2", "5fad07ef153b3b8ec110b6b155ec3780c4b2c4906297d0b4be1a7162d04a7e02", [:mix], [], "hexpm", "26b9d50f0f01796bc6be611ca815c5e0de034d2128e39cc9702eee6b66a4d1c8"},
|
||||||
"gen_smtp": {:hex, :gen_smtp, "1.2.0", "9cfc75c72a8821588b9b9fe947ae5ab2aed95a052b81237e0928633a13276fd3", [:rebar3], [{:ranch, ">= 1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "5ee0375680bca8f20c4d85f58c2894441443a743355430ff33a783fe03296779"},
|
"gen_smtp": {:hex, :gen_smtp, "1.2.0", "9cfc75c72a8821588b9b9fe947ae5ab2aed95a052b81237e0928633a13276fd3", [:rebar3], [{:ranch, ">= 1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "5ee0375680bca8f20c4d85f58c2894441443a743355430ff33a783fe03296779"},
|
||||||
"gettext": {:hex, :gettext, "0.22.0", "a25d71ec21b1848957d9207b81fd61cb25161688d282d58bdafef74c2270bdc4", [:mix], [{:expo, "~> 0.3.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "cb0675141576f73720c8e49b4f0fd3f2c69f0cd8c218202724d4aebab8c70ace"},
|
"gettext": {:hex, :gettext, "0.22.1", "e7942988383c3d9eed4bdc22fc63e712b655ae94a672a27e4900e3d4a2c43581", [:mix], [{:expo, "~> 0.4.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "ad105b8dab668ee3f90c0d3d94ba75e9aead27a62495c101d94f2657a190ac5d"},
|
||||||
"jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
|
"jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
|
||||||
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
|
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
|
||||||
"makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"},
|
"makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"},
|
||||||
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
|
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
|
||||||
"mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"},
|
"mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"},
|
||||||
"nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"},
|
"nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"},
|
||||||
"oban": {:hex, :oban, "2.13.6", "a0cb1bce3bd393770512231fb5a3695fa19fd3af10d7575bf73f837aee7abf43", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c1c5eb16f377b3cbbf2ea14be24d20e3d91285af9d1ac86260b7c2af5464887"},
|
"oban": {:hex, :oban, "2.14.2", "ae925d9a33e110addaa59ff7ec1b2fd84270ac7eb00fbb4b4a179d74c407bba3", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "32bf30127c8c44ac42f05f229a50fadc2177b3e799c29499f5daf90d5e5b5d3c"},
|
||||||
"phoenix": {:hex, :phoenix, "1.6.15", "0a1d96bbc10747fd83525370d691953cdb6f3ccbac61aa01b4acb012474b047d", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d70ab9fbf6b394755ea88b644d34d79d8b146e490973151f248cacd122d20672"},
|
"phoenix": {:hex, :phoenix, "1.6.16", "e5bdd18c7a06da5852a25c7befb72246de4ddc289182285f8685a40b7b5f5451", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e15989ff34f670a96b95ef6d1d25bad0d9c50df5df40b671d8f4a669e050ac39"},
|
||||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"},
|
"phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"},
|
||||||
"phoenix_html": {:hex, :phoenix_html, "3.2.0", "1c1219d4b6cb22ac72f12f73dc5fad6c7563104d083f711c3fcd8551a1f4ae11", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "36ec97ba56d25c0136ef1992c37957e4246b649d620958a1f9fa86165f8bc54f"},
|
"phoenix_html": {:hex, :phoenix_html, "3.3.1", "4788757e804a30baac6b3fc9695bf5562465dd3f1da8eb8460ad5b404d9a2178", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "bed1906edd4906a15fd7b412b85b05e521e1f67c9a85418c55999277e553d0d3"},
|
||||||
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.7.1", "b0bf8f3348dec4910907a2ad1453e642f6fe4d444376c1c9b26222d63c73cf97", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "b6c5d744bf4b40692b1b361d3608bdfd05aeab83e17c7bc217d730f007f31abf"},
|
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.7.2", "97cc4ff2dba1ebe504db72cb45098cb8e91f11160528b980bd282cc45c73b29c", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18.3", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "0e5fdf063c7a3b620c566a30fcf68b7ee02e5e46fe48ee46a6ec3ba382dc05b7"},
|
||||||
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.1", "2aff698f5e47369decde4357ba91fc9c37c6487a512b41732818f2204a8ef1d3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "9bffb834e7ddf08467fe54ae58b5785507aaba6255568ae22b4d46e2bb3615ab"},
|
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.1", "2aff698f5e47369decde4357ba91fc9c37c6487a512b41732818f2204a8ef1d3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "9bffb834e7ddf08467fe54ae58b5785507aaba6255568ae22b4d46e2bb3615ab"},
|
||||||
"phoenix_live_view": {:hex, :phoenix_live_view, "0.18.2", "635cf07de947235deb030cd6b776c71a3b790ab04cebf526aa8c879fe17c7784", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "da287a77327e996cc166e4c440c3ad5ab33ccdb151b91c793209b39ebbce5b75"},
|
"phoenix_live_view": {:hex, :phoenix_live_view, "0.18.18", "1f38fbd7c363723f19aad1a04b5490ff3a178e37daaf6999594d5f34796c47fc", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a5810d0472f3189ede6d2a95bda7f31c6113156b91784a3426cb0ab6a6d85214"},
|
||||||
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"},
|
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"},
|
||||||
"phoenix_swoosh": {:hex, :phoenix_swoosh, "1.1.0", "f8e4780705c9f254cc853f7a40e25f7198ba4d91102bcfad2226669b69766b35", [:mix], [{:finch, "~> 0.8", [hex: :finch, repo: "hexpm", optional: true]}, {:hackney, "~> 1.10", [hex: :hackney, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.5", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "aa82f10afd9a4b6080fdf3274dbb9432b25b210d42b4b6b55308f6e59cd87c3d"},
|
"phoenix_swoosh": {:hex, :phoenix_swoosh, "1.2.0", "a544d83fde4a767efb78f45404a74c9e37b2a9c5ea3339692e65a6966731f935", [:mix], [{:finch, "~> 0.8", [hex: :finch, repo: "hexpm", optional: true]}, {:hackney, "~> 1.10", [hex: :hackney, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.5", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "e88d117251e89a16b92222415a6d87b99a96747ddf674fc5c7631de734811dba"},
|
||||||
"phoenix_view": {:hex, :phoenix_view, "1.1.2", "1b82764a065fb41051637872c7bd07ed2fdb6f5c3bd89684d4dca6e10115c95a", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "7ae90ad27b09091266f6adbb61e1d2516a7c3d7062c6789d46a7554ec40f3a56"},
|
"phoenix_template": {:hex, :phoenix_template, "1.0.1", "85f79e3ad1b0180abb43f9725973e3b8c2c3354a87245f91431eec60553ed3ef", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "157dc078f6226334c91cb32c1865bf3911686f8bcd6bcff86736f6253e6993ee"},
|
||||||
|
"phoenix_view": {:hex, :phoenix_view, "2.0.2", "6bd4d2fd595ef80d33b439ede6a19326b78f0f1d8d62b9a318e3d9c1af351098", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "a929e7230ea5c7ee0e149ffcf44ce7cf7f4b6d2bfe1752dd7c084cdff152d36f"},
|
||||||
"plug": {:hex, :plug, "1.14.0", "ba4f558468f69cbd9f6b356d25443d0b796fbdc887e03fa89001384a9cac638f", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bf020432c7d4feb7b3af16a0c2701455cbbbb95e5b6866132cb09eb0c29adc14"},
|
"plug": {:hex, :plug, "1.14.0", "ba4f558468f69cbd9f6b356d25443d0b796fbdc887e03fa89001384a9cac638f", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bf020432c7d4feb7b3af16a0c2701455cbbbb95e5b6866132cb09eb0c29adc14"},
|
||||||
"plug_cowboy": {:hex, :plug_cowboy, "2.6.0", "d1cf12ff96a1ca4f52207c5271a6c351a4733f413803488d75b70ccf44aebec2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "073cf20b753ce6682ed72905cd62a2d4bd9bad1bf9f7feb02a1b8e525bd94fa6"},
|
"plug_cowboy": {:hex, :plug_cowboy, "2.6.1", "9a3bbfceeb65eff5f39dab529e5cd79137ac36e913c02067dba3963a26efe9b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "de36e1a21f451a18b790f37765db198075c25875c64834bcc82d90b309eb6613"},
|
||||||
"plug_crypto": {:hex, :plug_crypto, "1.2.3", "8f77d13aeb32bfd9e654cb68f0af517b371fb34c56c9f2b58fe3df1235c1251a", [:mix], [], "hexpm", "b5672099c6ad5c202c45f5a403f21a3411247f164e4a8fab056e5cd8a290f4a2"},
|
"plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"},
|
||||||
"postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"},
|
"postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"},
|
||||||
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
|
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
|
||||||
"swoosh": {:hex, :swoosh, "1.9.1", "0a5d7bf9954eb41d7e55525bc0940379982b090abbaef67cd8e1fd2ed7f8ca1a", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "76dffff3ffcab80f249d5937a592eaef7cc49ac6f4cdd27e622868326ed6371e"},
|
"swoosh": {:hex, :swoosh, "1.9.1", "0a5d7bf9954eb41d7e55525bc0940379982b090abbaef67cd8e1fd2ed7f8ca1a", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "76dffff3ffcab80f249d5937a592eaef7cc49ac6f4cdd27e622868326ed6371e"},
|
||||||
|
@ -66,11 +66,11 @@ msgstr ""
|
|||||||
msgid "Invite someone new!"
|
msgid "Invite someone new!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/components/topbar.ex:137
|
#: lib/cannery_web/components/core_components/topbar.html.heex:124
|
||||||
#: lib/cannery_web/templates/user_confirmation/new.html.heex:31
|
#: lib/cannery_web/templates/user_confirmation/new.html.heex:32
|
||||||
#: lib/cannery_web/templates/user_registration/new.html.heex:44
|
#: lib/cannery_web/templates/user_registration/new.html.heex:44
|
||||||
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:45
|
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:45
|
||||||
#: lib/cannery_web/templates/user_reset_password/new.html.heex:31
|
#: lib/cannery_web/templates/user_reset_password/new.html.heex:32
|
||||||
#: 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:28
|
#: lib/cannery_web/templates/user_session/new.html.heex:28
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
@ -97,19 +97,19 @@ msgstr ""
|
|||||||
msgid "New Tag"
|
msgid "New Tag"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/components/topbar.ex:129
|
#: lib/cannery_web/components/core_components/topbar.html.heex:116
|
||||||
#: lib/cannery_web/templates/user_confirmation/new.html.heex:28
|
#: lib/cannery_web/templates/user_confirmation/new.html.heex:29
|
||||||
#: lib/cannery_web/templates/user_registration/new.html.heex:3
|
#: lib/cannery_web/templates/user_registration/new.html.heex:3
|
||||||
#: lib/cannery_web/templates/user_registration/new.html.heex:37
|
#: lib/cannery_web/templates/user_registration/new.html.heex:37
|
||||||
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:42
|
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:42
|
||||||
#: lib/cannery_web/templates/user_reset_password/new.html.heex:28
|
#: lib/cannery_web/templates/user_reset_password/new.html.heex:29
|
||||||
#: lib/cannery_web/templates/user_session/new.html.heex:39
|
#: lib/cannery_web/templates/user_session/new.html.heex:39
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Register"
|
msgid "Register"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: 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:15
|
#: lib/cannery_web/templates/user_confirmation/new.html.heex:16
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Resend confirmation instructions"
|
msgid "Resend confirmation instructions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -120,28 +120,28 @@ msgstr ""
|
|||||||
msgid "Reset password"
|
msgid "Reset password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/components/add_shot_group_component.html.heex:54
|
#: lib/cannery_web/components/add_shot_group_component.html.heex:56
|
||||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:82
|
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:84
|
||||||
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:157
|
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:159
|
||||||
#: lib/cannery_web/live/container_live/form_component.html.heex:51
|
#: lib/cannery_web/live/container_live/form_component.html.heex:55
|
||||||
#: lib/cannery_web/live/invite_live/form_component.html.heex:32
|
#: lib/cannery_web/live/invite_live/form_component.html.heex:32
|
||||||
#: lib/cannery_web/live/range_live/form_component.html.heex:41
|
#: lib/cannery_web/live/range_live/form_component.html.heex:44
|
||||||
#: lib/cannery_web/live/tag_live/form_component.html.heex:37
|
#: lib/cannery_web/live/tag_live/form_component.html.heex:37
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/templates/user_reset_password/new.html.heex:15
|
#: lib/cannery_web/templates/user_reset_password/new.html.heex:16
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Send instructions to reset password"
|
msgid "Send instructions to reset password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/container_live/show.html.heex:76
|
#: lib/cannery_web/live/container_live/show.html.heex:75
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Why not add one?"
|
msgid "Why not add one?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/container_live/edit_tags_component.html.heex:50
|
#: lib/cannery_web/live/container_live/edit_tags_component.html.heex:51
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Add"
|
msgid "Add"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -156,9 +156,9 @@ msgstr ""
|
|||||||
msgid "Why not get some ready to shoot?"
|
msgid "Why not get some ready to shoot?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:101
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:104
|
||||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:101
|
#: lib/cannery_web/live/ammo_group_live/show.html.heex:103
|
||||||
#: lib/cannery_web/live/range_live/index.html.heex:38
|
#: lib/cannery_web/live/range_live/index.html.heex:45
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Record shots"
|
msgid "Record shots"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -168,17 +168,12 @@ msgstr ""
|
|||||||
msgid "Add another container!"
|
msgid "Add another container!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:94
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Move containers"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/cannery_web/components/move_ammo_group_component.ex:126
|
#: lib/cannery_web/components/move_ammo_group_component.ex:126
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Select"
|
msgid "Select"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/invite_live/index.html.heex:30
|
#: lib/cannery_web/live/invite_live/index.html.heex:38
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Copy to clipboard"
|
msgid "Copy to clipboard"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -188,12 +183,12 @@ msgstr ""
|
|||||||
msgid "add a container first"
|
msgid "add a container first"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:75
|
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:77
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Create"
|
msgid "Create"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/templates/user_settings/edit.html.heex:111
|
#: lib/cannery_web/templates/user_settings/edit.html.heex:110
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Change Language"
|
msgid "Change Language"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -203,7 +198,7 @@ msgstr ""
|
|||||||
msgid "Change language"
|
msgid "Change language"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:60
|
#: lib/cannery_web/live/ammo_group_live/show.html.heex:55
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "View in Catalog"
|
msgid "View in Catalog"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -214,23 +209,25 @@ msgid "add an ammo type first"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/components/move_ammo_group_component.ex:80
|
#: lib/cannery_web/components/move_ammo_group_component.ex:80
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:121
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/show.html.heex:96
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Move ammo"
|
msgid "Move ammo"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/invite_live/index.html.heex:78
|
#: lib/cannery_web/live/invite_live/index.html.heex:90
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Set Unlimited"
|
msgid "Set Unlimited"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:86
|
#: lib/cannery_web/live/ammo_group_live/show.html.heex:89
|
||||||
#: lib/cannery_web/live/range_live/index.html.heex:31
|
#: lib/cannery_web/live/range_live/index.html.heex:38
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Stage for range"
|
msgid "Stage for range"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:85
|
#: lib/cannery_web/live/ammo_group_live/show.html.heex:88
|
||||||
#: lib/cannery_web/live/range_live/index.html.heex:30
|
#: lib/cannery_web/live/range_live/index.html.heex:37
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Unstage from range"
|
msgid "Unstage from range"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -239,3 +236,124 @@ msgstr ""
|
|||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Export Data as JSON"
|
msgid "Export Data as JSON"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_type_live/index.html.heex:85
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Clone %{ammo_type_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:88
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:144
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Clone %{container_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/invite_live/index.html.heex:35
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Copy invite link for %{invite_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_type_live/index.html.heex:104
|
||||||
|
#: lib/cannery_web/live/ammo_type_live/show.html.heex:36
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Delete %{ammo_type_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:103
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:159
|
||||||
|
#: lib/cannery_web/live/container_live/show.html.heex:55
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Delete %{container_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/tag_live/index.html.heex:66
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Delete %{tag_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/invite_live/index.html.heex:63
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Delete invite for %{invite_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/show.ex:161
|
||||||
|
#: lib/cannery_web/live/range_live/index.html.heex:131
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Delete shot record of %{shot_group_count} shots"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_type_live/index.html.heex:75
|
||||||
|
#: lib/cannery_web/live/ammo_type_live/show.html.heex:19
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit %{ammo_type_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:78
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:134
|
||||||
|
#: lib/cannery_web/live/container_live/show.html.heex:42
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit %{container_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/tag_live/index.html.heex:53
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit %{tag_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:143
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/show.html.heex:62
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit ammo group of %{ammo_group_count} bullets"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/invite_live/index.html.heex:46
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit invite for %{invite_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/show.ex:146
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit shot group of %{shot_group_count} shots"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/range_live/index.html.heex:114
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit shot record of %{shot_group_count} shots"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:97
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Stage"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:66
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:123
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Tag %{container_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:96
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Unstage"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_type_live/index.html.heex:65
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "View %{ammo_type_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:155
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Clone ammo group of %{ammo_group_count} bullets"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:170
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/show.html.heex:76
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Delete ammo group of %{ammo_group_count} bullets"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:131
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "View ammo group of %{ammo_group_count} bullets"
|
||||||
|
msgstr ""
|
||||||
|
@ -79,11 +79,11 @@ msgstr "Passwort vergessen?"
|
|||||||
msgid "Invite someone new!"
|
msgid "Invite someone new!"
|
||||||
msgstr "Laden Sie jemanden ein!"
|
msgstr "Laden Sie jemanden ein!"
|
||||||
|
|
||||||
#: lib/cannery_web/components/topbar.ex:137
|
#: lib/cannery_web/components/core_components/topbar.html.heex:124
|
||||||
#: lib/cannery_web/templates/user_confirmation/new.html.heex:31
|
#: lib/cannery_web/templates/user_confirmation/new.html.heex:32
|
||||||
#: lib/cannery_web/templates/user_registration/new.html.heex:44
|
#: lib/cannery_web/templates/user_registration/new.html.heex:44
|
||||||
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:45
|
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:45
|
||||||
#: lib/cannery_web/templates/user_reset_password/new.html.heex:31
|
#: lib/cannery_web/templates/user_reset_password/new.html.heex:32
|
||||||
#: 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:28
|
#: lib/cannery_web/templates/user_session/new.html.heex:28
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
@ -110,19 +110,19 @@ msgstr "Neuer Behälter"
|
|||||||
msgid "New Tag"
|
msgid "New Tag"
|
||||||
msgstr "Neuer Tag"
|
msgstr "Neuer Tag"
|
||||||
|
|
||||||
#: lib/cannery_web/components/topbar.ex:129
|
#: lib/cannery_web/components/core_components/topbar.html.heex:116
|
||||||
#: lib/cannery_web/templates/user_confirmation/new.html.heex:28
|
#: lib/cannery_web/templates/user_confirmation/new.html.heex:29
|
||||||
#: lib/cannery_web/templates/user_registration/new.html.heex:3
|
#: lib/cannery_web/templates/user_registration/new.html.heex:3
|
||||||
#: lib/cannery_web/templates/user_registration/new.html.heex:37
|
#: lib/cannery_web/templates/user_registration/new.html.heex:37
|
||||||
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:42
|
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:42
|
||||||
#: lib/cannery_web/templates/user_reset_password/new.html.heex:28
|
#: lib/cannery_web/templates/user_reset_password/new.html.heex:29
|
||||||
#: lib/cannery_web/templates/user_session/new.html.heex:39
|
#: lib/cannery_web/templates/user_session/new.html.heex:39
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Register"
|
msgid "Register"
|
||||||
msgstr "Registrieren"
|
msgstr "Registrieren"
|
||||||
|
|
||||||
#: 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:15
|
#: lib/cannery_web/templates/user_confirmation/new.html.heex:16
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Resend confirmation instructions"
|
msgid "Resend confirmation instructions"
|
||||||
msgstr "Bestätigungsmail erneut senden"
|
msgstr "Bestätigungsmail erneut senden"
|
||||||
@ -133,28 +133,28 @@ msgstr "Bestätigungsmail erneut senden"
|
|||||||
msgid "Reset password"
|
msgid "Reset password"
|
||||||
msgstr "Passwort zurücksetzen"
|
msgstr "Passwort zurücksetzen"
|
||||||
|
|
||||||
#: lib/cannery_web/components/add_shot_group_component.html.heex:54
|
#: lib/cannery_web/components/add_shot_group_component.html.heex:56
|
||||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:82
|
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:84
|
||||||
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:157
|
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:159
|
||||||
#: lib/cannery_web/live/container_live/form_component.html.heex:51
|
#: lib/cannery_web/live/container_live/form_component.html.heex:55
|
||||||
#: lib/cannery_web/live/invite_live/form_component.html.heex:32
|
#: lib/cannery_web/live/invite_live/form_component.html.heex:32
|
||||||
#: lib/cannery_web/live/range_live/form_component.html.heex:41
|
#: lib/cannery_web/live/range_live/form_component.html.heex:44
|
||||||
#: lib/cannery_web/live/tag_live/form_component.html.heex:37
|
#: lib/cannery_web/live/tag_live/form_component.html.heex:37
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr "Speichern"
|
msgstr "Speichern"
|
||||||
|
|
||||||
#: lib/cannery_web/templates/user_reset_password/new.html.heex:15
|
#: lib/cannery_web/templates/user_reset_password/new.html.heex:16
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Send instructions to reset password"
|
msgid "Send instructions to reset password"
|
||||||
msgstr "Anleitung zum Passwort zurücksetzen zusenden"
|
msgstr "Anleitung zum Passwort zurücksetzen zusenden"
|
||||||
|
|
||||||
#: lib/cannery_web/live/container_live/show.html.heex:76
|
#: lib/cannery_web/live/container_live/show.html.heex:75
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Why not add one?"
|
msgid "Why not add one?"
|
||||||
msgstr "Warum fügen Sie keine hinzu?"
|
msgstr "Warum fügen Sie keine hinzu?"
|
||||||
|
|
||||||
#: lib/cannery_web/live/container_live/edit_tags_component.html.heex:50
|
#: lib/cannery_web/live/container_live/edit_tags_component.html.heex:51
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Add"
|
msgid "Add"
|
||||||
msgstr "Hinzufügen"
|
msgstr "Hinzufügen"
|
||||||
@ -169,9 +169,9 @@ msgstr "Munition markieren"
|
|||||||
msgid "Why not get some ready to shoot?"
|
msgid "Why not get some ready to shoot?"
|
||||||
msgstr "Warum nicht einige für den Schießstand auswählen?"
|
msgstr "Warum nicht einige für den Schießstand auswählen?"
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:101
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:104
|
||||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:101
|
#: lib/cannery_web/live/ammo_group_live/show.html.heex:103
|
||||||
#: lib/cannery_web/live/range_live/index.html.heex:38
|
#: lib/cannery_web/live/range_live/index.html.heex:45
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Record shots"
|
msgid "Record shots"
|
||||||
msgstr "Schüsse dokumentieren"
|
msgstr "Schüsse dokumentieren"
|
||||||
@ -181,17 +181,12 @@ msgstr "Schüsse dokumentieren"
|
|||||||
msgid "Add another container!"
|
msgid "Add another container!"
|
||||||
msgstr "Einen weiteren Behälter hinzufügen!"
|
msgstr "Einen weiteren Behälter hinzufügen!"
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:94
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Move containers"
|
|
||||||
msgstr "Behälter verschieben"
|
|
||||||
|
|
||||||
#: lib/cannery_web/components/move_ammo_group_component.ex:126
|
#: lib/cannery_web/components/move_ammo_group_component.ex:126
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Select"
|
msgid "Select"
|
||||||
msgstr "Markieren"
|
msgstr "Markieren"
|
||||||
|
|
||||||
#: lib/cannery_web/live/invite_live/index.html.heex:30
|
#: lib/cannery_web/live/invite_live/index.html.heex:38
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Copy to clipboard"
|
msgid "Copy to clipboard"
|
||||||
msgstr "In die Zwischenablage kopieren"
|
msgstr "In die Zwischenablage kopieren"
|
||||||
@ -201,12 +196,12 @@ msgstr "In die Zwischenablage kopieren"
|
|||||||
msgid "add a container first"
|
msgid "add a container first"
|
||||||
msgstr "Zuerst einen Behälter hinzufügen"
|
msgstr "Zuerst einen Behälter hinzufügen"
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:75
|
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:77
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Create"
|
msgid "Create"
|
||||||
msgstr "Erstellen"
|
msgstr "Erstellen"
|
||||||
|
|
||||||
#: lib/cannery_web/templates/user_settings/edit.html.heex:111
|
#: lib/cannery_web/templates/user_settings/edit.html.heex:110
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Change Language"
|
msgid "Change Language"
|
||||||
msgstr "Sprache wechseln"
|
msgstr "Sprache wechseln"
|
||||||
@ -216,7 +211,7 @@ msgstr "Sprache wechseln"
|
|||||||
msgid "Change language"
|
msgid "Change language"
|
||||||
msgstr "Sprache wechseln"
|
msgstr "Sprache wechseln"
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:60
|
#: lib/cannery_web/live/ammo_group_live/show.html.heex:55
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "View in Catalog"
|
msgid "View in Catalog"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -227,23 +222,25 @@ msgid "add an ammo type first"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/components/move_ammo_group_component.ex:80
|
#: lib/cannery_web/components/move_ammo_group_component.ex:80
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:121
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/show.html.heex:96
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Move ammo"
|
msgid "Move ammo"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/invite_live/index.html.heex:78
|
#: lib/cannery_web/live/invite_live/index.html.heex:90
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Set Unlimited"
|
msgid "Set Unlimited"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:86
|
#: lib/cannery_web/live/ammo_group_live/show.html.heex:89
|
||||||
#: lib/cannery_web/live/range_live/index.html.heex:31
|
#: lib/cannery_web/live/range_live/index.html.heex:38
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Stage for range"
|
msgid "Stage for range"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:85
|
#: lib/cannery_web/live/ammo_group_live/show.html.heex:88
|
||||||
#: lib/cannery_web/live/range_live/index.html.heex:30
|
#: lib/cannery_web/live/range_live/index.html.heex:37
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Unstage from range"
|
msgid "Unstage from range"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -252,3 +249,124 @@ msgstr ""
|
|||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Export Data as JSON"
|
msgid "Export Data as JSON"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_type_live/index.html.heex:85
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Clone %{ammo_type_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:88
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:144
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Clone %{container_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/invite_live/index.html.heex:35
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Copy invite link for %{invite_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_type_live/index.html.heex:104
|
||||||
|
#: lib/cannery_web/live/ammo_type_live/show.html.heex:36
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Delete %{ammo_type_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:103
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:159
|
||||||
|
#: lib/cannery_web/live/container_live/show.html.heex:55
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Delete %{container_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/tag_live/index.html.heex:66
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Delete %{tag_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/invite_live/index.html.heex:63
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Delete invite for %{invite_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/show.ex:161
|
||||||
|
#: lib/cannery_web/live/range_live/index.html.heex:131
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Delete shot record of %{shot_group_count} shots"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_type_live/index.html.heex:75
|
||||||
|
#: lib/cannery_web/live/ammo_type_live/show.html.heex:19
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit %{ammo_type_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:78
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:134
|
||||||
|
#: lib/cannery_web/live/container_live/show.html.heex:42
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit %{container_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/tag_live/index.html.heex:53
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit %{tag_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:143
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/show.html.heex:62
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit ammo group of %{ammo_group_count} bullets"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/invite_live/index.html.heex:46
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit invite for %{invite_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/show.ex:146
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit shot group of %{shot_group_count} shots"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/range_live/index.html.heex:114
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit shot record of %{shot_group_count} shots"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:97
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "Stage"
|
||||||
|
msgstr "Munition markieren"
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:66
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:123
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Tag %{container_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:96
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Unstage"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_type_live/index.html.heex:65
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "View %{ammo_type_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:155
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "Clone ammo group of %{ammo_group_count} bullets"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:170
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/show.html.heex:76
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "Delete ammo group of %{ammo_group_count} bullets"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:131
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "View ammo group of %{ammo_group_count} bullets"
|
||||||
|
msgstr ""
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -23,18 +23,18 @@ msgstr ""
|
|||||||
## Run "mix gettext.extract" to bring this file up to
|
## Run "mix gettext.extract" to bring this file up to
|
||||||
## date. Leave "msgstr"s empty as changing them here has no
|
## date. Leave "msgstr"s empty as changing them here has no
|
||||||
## effect: edit them in PO (.po) files instead.
|
## effect: edit them in PO (.po) files instead.
|
||||||
#: lib/cannery/containers.ex:179
|
#: lib/cannery/containers.ex:200
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Container must be empty before deleting"
|
msgid "Container must be empty before deleting"
|
||||||
msgstr "Behälter muss vor dem Löschen leer sein"
|
msgstr "Behälter muss vor dem Löschen leer sein"
|
||||||
|
|
||||||
#: lib/cannery_web/live/container_live/index.ex:92
|
#: lib/cannery_web/live/container_live/index.ex:86
|
||||||
#: lib/cannery_web/live/container_live/show.ex:73
|
#: lib/cannery_web/live/container_live/show.ex:71
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Could not delete %{name}: %{error}"
|
msgid "Could not delete %{name}: %{error}"
|
||||||
msgstr "Konnte %{name} nicht löschen: %{error}"
|
msgstr "Konnte %{name} nicht löschen: %{error}"
|
||||||
|
|
||||||
#: lib/cannery_web/live/container_live/index.ex:80
|
#: lib/cannery_web/live/container_live/index.ex:74
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Could not find that container"
|
msgid "Could not find that container"
|
||||||
msgstr "Konnte Behälter nicht finden"
|
msgstr "Konnte Behälter nicht finden"
|
||||||
@ -49,12 +49,12 @@ msgstr "Mailadressenänderungs-Link ist ungültig oder abgelaufen."
|
|||||||
msgid "Error"
|
msgid "Error"
|
||||||
msgstr "Fehler"
|
msgstr "Fehler"
|
||||||
|
|
||||||
#: lib/cannery_web/templates/error/error.html.heex:28
|
#: lib/cannery_web/templates/error/error.html.heex:31
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Go back home"
|
msgid "Go back home"
|
||||||
msgstr "Zur Hauptseite zurückkehren"
|
msgstr "Zur Hauptseite zurückkehren"
|
||||||
|
|
||||||
#: lib/cannery_web/views/error_view.ex:11
|
#: lib/cannery_web/views/error_view.ex:10
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Internal Server Error"
|
msgid "Internal Server Error"
|
||||||
msgstr "Interner Serverfehler"
|
msgstr "Interner Serverfehler"
|
||||||
@ -64,7 +64,7 @@ msgstr "Interner Serverfehler"
|
|||||||
msgid "Invalid email or password"
|
msgid "Invalid email or password"
|
||||||
msgstr "Ungültige Mailadresse oder Passwort"
|
msgstr "Ungültige Mailadresse oder Passwort"
|
||||||
|
|
||||||
#: lib/cannery_web/views/error_view.ex:9
|
#: lib/cannery_web/views/error_view.ex:8
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Not found"
|
msgid "Not found"
|
||||||
msgstr "Nicht gefunden"
|
msgstr "Nicht gefunden"
|
||||||
@ -83,15 +83,15 @@ msgstr "Oops, etwas ist schiefgegangen. Bitte beachten Sie den Fehler unten."
|
|||||||
msgid "Reset password link is invalid or it has expired."
|
msgid "Reset password link is invalid or it has expired."
|
||||||
msgstr "Link zum Passwort zurücksetzen ist ungültig oder abgelaufen."
|
msgstr "Link zum Passwort zurücksetzen ist ungültig oder abgelaufen."
|
||||||
|
|
||||||
#: lib/cannery_web/controllers/user_registration_controller.ex:22
|
#: lib/cannery_web/controllers/user_registration_controller.ex:23
|
||||||
#: lib/cannery_web/controllers/user_registration_controller.ex:51
|
#: lib/cannery_web/controllers/user_registration_controller.ex:52
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Sorry, public registration is disabled"
|
msgid "Sorry, public registration is disabled"
|
||||||
msgstr "Entschuldigung, aber öffentliche Registrierung ist deaktiviert"
|
msgstr "Entschuldigung, aber öffentliche Registrierung ist deaktiviert"
|
||||||
|
|
||||||
#: lib/cannery_web/controllers/user_registration_controller.ex:12
|
#: lib/cannery_web/controllers/user_registration_controller.ex:13
|
||||||
#: lib/cannery_web/controllers/user_registration_controller.ex:41
|
#: lib/cannery_web/controllers/user_registration_controller.ex:42
|
||||||
#: lib/cannery_web/controllers/user_registration_controller.ex:70
|
#: lib/cannery_web/controllers/user_registration_controller.ex:71
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Sorry, this invite was not found or expired"
|
msgid "Sorry, this invite was not found or expired"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -102,7 +102,7 @@ msgstr ""
|
|||||||
msgid "Unable to delete user"
|
msgid "Unable to delete user"
|
||||||
msgstr "Dieser Nutzer konnte nicht gelöscht werden"
|
msgstr "Dieser Nutzer konnte nicht gelöscht werden"
|
||||||
|
|
||||||
#: lib/cannery_web/views/error_view.ex:10
|
#: lib/cannery_web/views/error_view.ex:9
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Unauthorized"
|
msgid "Unauthorized"
|
||||||
msgstr "Unbefugt"
|
msgstr "Unbefugt"
|
||||||
@ -112,11 +112,6 @@ msgstr "Unbefugt"
|
|||||||
msgid "User confirmation link is invalid or it has expired."
|
msgid "User confirmation link is invalid or it has expired."
|
||||||
msgstr "Nutzerkonto Bestätigungslink ist ungültig oder abgelaufen."
|
msgstr "Nutzerkonto Bestätigungslink ist ungültig oder abgelaufen."
|
||||||
|
|
||||||
#: lib/cannery_web/live/invite_live/index.ex:19
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "You are not authorized to view this page"
|
|
||||||
msgstr "Sie sind nicht berechtigt, diese Seite aufzurufen"
|
|
||||||
|
|
||||||
#: lib/cannery_web/controllers/user_auth.ex:177
|
#: lib/cannery_web/controllers/user_auth.ex:177
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "You are not authorized to view this page."
|
msgid "You are not authorized to view this page."
|
||||||
@ -142,27 +137,16 @@ msgstr "ist nicht gültig"
|
|||||||
msgid "must have the @ sign and no spaces"
|
msgid "must have the @ sign and no spaces"
|
||||||
msgstr "Muss ein @ Zeichen und keine Leerzeichen haben"
|
msgstr "Muss ein @ Zeichen und keine Leerzeichen haben"
|
||||||
|
|
||||||
#: lib/cannery/tags.ex:66
|
#: lib/cannery_web/live/container_live/show.ex:46
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Tag not found"
|
msgid "Tag not found"
|
||||||
msgstr "Tag nicht gefunden"
|
msgstr "Tag nicht gefunden"
|
||||||
|
|
||||||
#: lib/cannery_web/live/container_live/edit_tags_component.ex:30
|
#: lib/cannery_web/live/container_live/edit_tags_component.ex:46
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Tag could not be added"
|
msgid "Tag could not be added"
|
||||||
msgstr "Tag konnte nicht hinzugefügt werden"
|
msgstr "Tag konnte nicht hinzugefügt werden"
|
||||||
|
|
||||||
#: lib/cannery/activity_log/shot_group.ex:122
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Count must be at least 1"
|
|
||||||
msgstr "Anzahl muss mindestens 1 sein"
|
|
||||||
|
|
||||||
#: lib/cannery/activity_log/shot_group.ex:82
|
|
||||||
#: lib/cannery/activity_log/shot_group.ex:118
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Count must be less than %{count}"
|
|
||||||
msgstr "Anzahl muss weniger als %{count} betragen"
|
|
||||||
|
|
||||||
#: lib/cannery_web/controllers/user_auth.ex:39
|
#: lib/cannery_web/controllers/user_auth.ex:39
|
||||||
#: lib/cannery_web/controllers/user_auth.ex:161
|
#: lib/cannery_web/controllers/user_auth.ex:161
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
@ -171,39 +155,59 @@ msgstr ""
|
|||||||
"Sie müssen ihr Nutzerkonto bestätigen und einloggen, um diese Seite "
|
"Sie müssen ihr Nutzerkonto bestätigen und einloggen, um diese Seite "
|
||||||
"anzuzeigen."
|
"anzuzeigen."
|
||||||
|
|
||||||
#: lib/cannery_web/live/container_live/edit_tags_component.ex:52
|
#: lib/cannery_web/live/container_live/edit_tags_component.ex:73
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Tag could not be removed"
|
msgid "Tag could not be removed"
|
||||||
msgstr "Tag konnte nicht gelöscht werden"
|
msgstr "Tag konnte nicht gelöscht werden"
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:157
|
#: lib/cannery_web/live/ammo_group_live/form_component.ex:160
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Could not parse number of copies"
|
msgid "Could not parse number of copies"
|
||||||
msgstr "Konnte die Anzahl der Kopien nicht verstehen"
|
msgstr "Konnte die Anzahl der Kopien nicht verstehen"
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:142
|
#: lib/cannery_web/live/ammo_group_live/form_component.ex:150
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}"
|
msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Ungültige Nummer an Kopien. Muss zwischen 1 and %{max} liegen. War "
|
"Ungültige Nummer an Kopien. Muss zwischen 1 and %{max} liegen. War "
|
||||||
"%{multiplier}"
|
"%{multiplier}"
|
||||||
|
|
||||||
#: lib/cannery/ammo.ex:686
|
#: lib/cannery/ammo.ex:1015
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Invalid multiplier"
|
msgid "Invalid multiplier"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery/ammo/ammo_group.ex:97
|
#: lib/cannery/ammo/ammo_group.ex:92
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Please select an ammo type and container"
|
msgid "Please select an ammo type and container"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/range_live/index.html.heex:67
|
#: lib/cannery_web/live/range_live/index.html.heex:74
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Your browser does not support the canvas element."
|
msgid "Your browser does not support the canvas element."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery/activity_log/shot_group.ex:77
|
#: lib/cannery/activity_log/shot_group.ex:72
|
||||||
#, elixir-autogen, elixir-format, fuzzy
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
msgid "Please select a valid user and ammo pack"
|
msgid "Please select a valid user and ammo pack"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery/activity_log/shot_group.ex:86
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Ammo left can be at most %{count} rounds"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery/activity_log/shot_group.ex:82
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Ammo left must be at least 0"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery/activity_log/shot_group.ex:119
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "Count can be at most %{count} shots"
|
||||||
|
msgstr "Anzahl muss weniger als %{count} betragen"
|
||||||
|
|
||||||
|
#: lib/cannery/activity_log/shot_group.ex:78
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "can't be blank"
|
||||||
|
msgstr ""
|
||||||
|
@ -23,31 +23,31 @@ msgstr ""
|
|||||||
## Run "mix gettext.extract" to bring this file up to
|
## Run "mix gettext.extract" to bring this file up to
|
||||||
## date. Leave "msgstr"s empty as changing them here has no
|
## date. Leave "msgstr"s empty as changing them here has no
|
||||||
## effect: edit them in PO (.po) files instead.
|
## effect: edit them in PO (.po) files instead.
|
||||||
#: lib/cannery_web/live/ammo_type_live/form_component.ex:86
|
#: lib/cannery_web/live/ammo_type_live/form_component.ex:89
|
||||||
#: lib/cannery_web/live/container_live/form_component.ex:89
|
#: lib/cannery_web/live/container_live/form_component.ex:89
|
||||||
#: lib/cannery_web/live/invite_live/form_component.ex:80
|
#: lib/cannery_web/live/invite_live/form_component.ex:80
|
||||||
#: lib/cannery_web/live/tag_live/form_component.ex:79
|
#: lib/cannery_web/live/tag_live/form_component.ex:78
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "%{name} created successfully"
|
msgid "%{name} created successfully"
|
||||||
msgstr "%{name} erfolgreich erstellt"
|
msgstr "%{name} erfolgreich erstellt"
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_type_live/index.ex:73
|
#: lib/cannery_web/live/ammo_type_live/index.ex:72
|
||||||
#: lib/cannery_web/live/ammo_type_live/show.ex:55
|
#: lib/cannery_web/live/ammo_type_live/show.ex:54
|
||||||
#: lib/cannery_web/live/tag_live/index.ex:64
|
#: lib/cannery_web/live/tag_live/index.ex:65
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "%{name} deleted succesfully"
|
msgid "%{name} deleted succesfully"
|
||||||
msgstr "%{name} erfolgreich gelöscht"
|
msgstr "%{name} erfolgreich gelöscht"
|
||||||
|
|
||||||
#: lib/cannery_web/live/container_live/index.ex:85
|
#: lib/cannery_web/live/container_live/index.ex:79
|
||||||
#: lib/cannery_web/live/container_live/show.ex:63
|
#: lib/cannery_web/live/container_live/show.ex:61
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "%{name} has been deleted"
|
msgid "%{name} has been deleted"
|
||||||
msgstr "%{name} wurde gelöscht"
|
msgstr "%{name} wurde gelöscht"
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_type_live/form_component.ex:67
|
#: lib/cannery_web/live/ammo_type_live/form_component.ex:70
|
||||||
#: lib/cannery_web/live/container_live/form_component.ex:70
|
#: lib/cannery_web/live/container_live/form_component.ex:70
|
||||||
#: lib/cannery_web/live/invite_live/form_component.ex:62
|
#: lib/cannery_web/live/invite_live/form_component.ex:62
|
||||||
#: lib/cannery_web/live/tag_live/form_component.ex:61
|
#: lib/cannery_web/live/tag_live/form_component.ex:60
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "%{name} updated successfully"
|
msgid "%{name} updated successfully"
|
||||||
msgstr "%{name} erfolgreich aktualisiert"
|
msgstr "%{name} erfolgreich aktualisiert"
|
||||||
@ -57,24 +57,24 @@ msgstr "%{name} erfolgreich aktualisiert"
|
|||||||
msgid "A link to confirm your email change has been sent to the new address."
|
msgid "A link to confirm your email change has been sent to the new address."
|
||||||
msgstr "Eine Mail zum Bestätigen ihre Mailadresse wurde Ihnen zugesandt."
|
msgstr "Eine Mail zum Bestätigen ihre Mailadresse wurde Ihnen zugesandt."
|
||||||
|
|
||||||
#: lib/cannery_web/live/invite_live/index.html.heex:98
|
#: lib/cannery_web/live/invite_live/index.html.heex:110
|
||||||
#: lib/cannery_web/live/invite_live/index.html.heex:126
|
#: lib/cannery_web/live/invite_live/index.html.heex:138
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Are you sure you want to delete %{email}? This action is permanent!"
|
msgid "Are you sure you want to delete %{email}? This action is permanent!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Sind Sie sicher, dass sie %{email} löschen möchten? Dies kann nicht "
|
"Sind Sie sicher, dass sie %{email} löschen möchten? Dies kann nicht "
|
||||||
"zurückgenommen werden!"
|
"zurückgenommen werden!"
|
||||||
|
|
||||||
#: lib/cannery_web/live/container_live/index.html.heex:92
|
#: lib/cannery_web/live/container_live/index.html.heex:100
|
||||||
#: lib/cannery_web/live/container_live/index.html.heex:135
|
#: lib/cannery_web/live/container_live/index.html.heex:156
|
||||||
#: lib/cannery_web/live/container_live/show.html.heex:55
|
#: lib/cannery_web/live/container_live/show.html.heex:52
|
||||||
#: lib/cannery_web/live/tag_live/index.html.heex:63
|
#: lib/cannery_web/live/tag_live/index.html.heex:64
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Are you sure you want to delete %{name}?"
|
msgid "Are you sure you want to delete %{name}?"
|
||||||
msgstr "Sind Sie sicher, dass sie %{name} löschen möchten?"
|
msgstr "Sind Sie sicher, dass sie %{name} löschen möchten?"
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:153
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:168
|
||||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:75
|
#: lib/cannery_web/live/ammo_group_live/show.html.heex:74
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Are you sure you want to delete this ammo?"
|
msgid "Are you sure you want to delete this ammo?"
|
||||||
msgstr "Sind Sie sicher, dass sie diese Munition löschen möchten?"
|
msgstr "Sind Sie sicher, dass sie diese Munition löschen möchten?"
|
||||||
@ -84,7 +84,7 @@ msgstr "Sind Sie sicher, dass sie diese Munition löschen möchten?"
|
|||||||
msgid "Are you sure you want to delete your account?"
|
msgid "Are you sure you want to delete your account?"
|
||||||
msgstr "Sind Sie sicher, dass sie Ihren Account löschen möchten?"
|
msgstr "Sind Sie sicher, dass sie Ihren Account löschen möchten?"
|
||||||
|
|
||||||
#: lib/cannery_web/components/topbar.ex:104
|
#: lib/cannery_web/components/core_components/topbar.html.heex:89
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Are you sure you want to log out?"
|
msgid "Are you sure you want to log out?"
|
||||||
msgstr "Wirklich ausloggen?"
|
msgstr "Wirklich ausloggen?"
|
||||||
@ -123,17 +123,17 @@ msgstr "Passwort erfolgreich zurückgesetzt."
|
|||||||
msgid "Password updated successfully."
|
msgid "Password updated successfully."
|
||||||
msgstr "Passwort erfolgreich geändert."
|
msgstr "Passwort erfolgreich geändert."
|
||||||
|
|
||||||
#: lib/cannery_web/controllers/user_registration_controller.ex:65
|
#: lib/cannery_web/controllers/user_registration_controller.ex:66
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Please check your email to verify your account"
|
msgid "Please check your email to verify your account"
|
||||||
msgstr "Bitte überprüfen Sie ihre Mailbox und bestätigen Sie das Nutzerkonto"
|
msgstr "Bitte überprüfen Sie ihre Mailbox und bestätigen Sie das Nutzerkonto"
|
||||||
|
|
||||||
#: lib/cannery_web/components/add_shot_group_component.html.heex:56
|
#: lib/cannery_web/components/add_shot_group_component.html.heex:58
|
||||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:83
|
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:85
|
||||||
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:158
|
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:160
|
||||||
#: lib/cannery_web/live/container_live/form_component.html.heex:53
|
#: lib/cannery_web/live/container_live/form_component.html.heex:57
|
||||||
#: lib/cannery_web/live/invite_live/form_component.html.heex:34
|
#: lib/cannery_web/live/invite_live/form_component.html.heex:34
|
||||||
#: lib/cannery_web/live/range_live/form_component.html.heex:43
|
#: lib/cannery_web/live/range_live/form_component.html.heex:46
|
||||||
#: lib/cannery_web/live/tag_live/form_component.html.heex:39
|
#: lib/cannery_web/live/tag_live/form_component.html.heex:39
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Saving..."
|
msgid "Saving..."
|
||||||
@ -151,44 +151,44 @@ msgstr ""
|
|||||||
"Sind Sie sicher, dass sie %{tag_name} Tag von %{container_name} entfernen "
|
"Sind Sie sicher, dass sie %{tag_name} Tag von %{container_name} entfernen "
|
||||||
"wollen?"
|
"wollen?"
|
||||||
|
|
||||||
#: lib/cannery_web/live/container_live/edit_tags_component.ex:36
|
#: lib/cannery_web/live/container_live/edit_tags_component.ex:51
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "%{name} added successfully"
|
msgid "%{name} added successfully"
|
||||||
msgstr "%{name} erfolgreich hinzugefügt"
|
msgstr "%{name} erfolgreich hinzugefügt"
|
||||||
|
|
||||||
#: lib/cannery_web/live/container_live/show.ex:39
|
#: lib/cannery_web/live/container_live/show.ex:38
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "%{tag_name} has been removed from %{container_name}"
|
msgid "%{tag_name} has been removed from %{container_name}"
|
||||||
msgstr "%{tag_name} wurde von %{container_name} entfernt"
|
msgstr "%{tag_name} wurde von %{container_name} entfernt"
|
||||||
|
|
||||||
#: lib/cannery_web/live/container_live/edit_tags_component.html.heex:52
|
#: lib/cannery_web/live/container_live/edit_tags_component.html.heex:53
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Adding..."
|
msgid "Adding..."
|
||||||
msgstr "Füge hinzu..."
|
msgstr "Füge hinzu..."
|
||||||
|
|
||||||
#: lib/cannery_web/components/add_shot_group_component.ex:56
|
#: lib/cannery_web/components/add_shot_group_component.ex:60
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Shots recorded successfully"
|
msgid "Shots recorded successfully"
|
||||||
msgstr "Schüsse erfolgreich dokumentiert"
|
msgstr "Schüsse erfolgreich dokumentiert"
|
||||||
|
|
||||||
#: lib/cannery_web/live/range_live/index.html.heex:27
|
#: lib/cannery_web/live/range_live/index.html.heex:34
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Are you sure you want to unstage this ammo?"
|
msgid "Are you sure you want to unstage this ammo?"
|
||||||
msgstr "Sind sie sicher, dass Sie diese Munition demarkieren möchten?"
|
msgstr "Sind sie sicher, dass Sie diese Munition demarkieren möchten?"
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/show.ex:142
|
#: lib/cannery_web/live/ammo_group_live/show.ex:159
|
||||||
#: lib/cannery_web/live/range_live/index.html.heex:116
|
#: lib/cannery_web/live/range_live/index.html.heex:128
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Are you sure you want to delete this shot record?"
|
msgid "Are you sure you want to delete this shot record?"
|
||||||
msgstr "Sind sie sicher, dass sie die Schießkladde löschen möchten?"
|
msgstr "Sind sie sicher, dass sie die Schießkladde löschen möchten?"
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/show.ex:83
|
#: lib/cannery_web/live/ammo_group_live/show.ex:81
|
||||||
#: lib/cannery_web/live/range_live/index.ex:80
|
#: lib/cannery_web/live/range_live/index.ex:79
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Shot records deleted succesfully"
|
msgid "Shot records deleted succesfully"
|
||||||
msgstr "Schießkladde erfolgreich gelöscht"
|
msgstr "Schießkladde erfolgreich gelöscht"
|
||||||
|
|
||||||
#: lib/cannery_web/live/range_live/form_component.ex:55
|
#: lib/cannery_web/live/range_live/form_component.ex:54
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Shot records updated successfully"
|
msgid "Shot records updated successfully"
|
||||||
msgstr "Schießkladde erfolgreich aktualisiert"
|
msgstr "Schießkladde erfolgreich aktualisiert"
|
||||||
@ -198,17 +198,17 @@ msgstr "Schießkladde erfolgreich aktualisiert"
|
|||||||
msgid "%{email} confirmed successfully."
|
msgid "%{email} confirmed successfully."
|
||||||
msgstr "%{email} erfolgreich bestätigt."
|
msgstr "%{email} erfolgreich bestätigt."
|
||||||
|
|
||||||
#: lib/cannery_web/components/move_ammo_group_component.ex:53
|
#: lib/cannery_web/components/move_ammo_group_component.ex:54
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Ammo moved to %{name} successfully"
|
msgid "Ammo moved to %{name} successfully"
|
||||||
msgstr "Munition erfolgreich zu %{name} verschoben"
|
msgstr "Munition erfolgreich zu %{name} verschoben"
|
||||||
|
|
||||||
#: lib/cannery_web/live/invite_live/index.ex:128
|
#: lib/cannery_web/live/invite_live/index.ex:116
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Copied to clipboard"
|
msgid "Copied to clipboard"
|
||||||
msgstr "Der Zwischenablage hinzugefügt"
|
msgstr "Der Zwischenablage hinzugefügt"
|
||||||
|
|
||||||
#: lib/cannery_web/live/container_live/edit_tags_component.ex:58
|
#: lib/cannery_web/live/container_live/edit_tags_component.ex:78
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "%{name} removed successfully"
|
msgid "%{name} removed successfully"
|
||||||
msgstr "%{name} erfolgreich entfernt"
|
msgstr "%{name} erfolgreich entfernt"
|
||||||
@ -219,7 +219,7 @@ msgstr "%{name} erfolgreich entfernt"
|
|||||||
msgid "You'll need to"
|
msgid "You'll need to"
|
||||||
msgstr "Sie müssen"
|
msgstr "Sie müssen"
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:76
|
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:78
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Creating..."
|
msgid "Creating..."
|
||||||
msgstr "Erstellen..."
|
msgstr "Erstellen..."
|
||||||
@ -240,65 +240,65 @@ msgstr "Spracheinstellung gespeichert."
|
|||||||
msgid "Ammo deleted succesfully"
|
msgid "Ammo deleted succesfully"
|
||||||
msgstr "Munitionsgruppe erfolgreich gelöscht"
|
msgstr "Munitionsgruppe erfolgreich gelöscht"
|
||||||
|
|
||||||
#: lib/cannery_web/live/range_live/index.ex:95
|
#: lib/cannery_web/live/range_live/index.ex:93
|
||||||
#, elixir-autogen, elixir-format, fuzzy
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
msgid "Ammo unstaged succesfully"
|
msgid "Ammo unstaged succesfully"
|
||||||
msgstr "Munition erfolgreich demarkiert"
|
msgstr "Munition erfolgreich demarkiert"
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:118
|
#: lib/cannery_web/live/ammo_group_live/form_component.ex:126
|
||||||
#, elixir-autogen, elixir-format, fuzzy
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
msgid "Ammo updated successfully"
|
msgid "Ammo updated successfully"
|
||||||
msgstr "Munitionsgruppe erfolgreich aktualisiert"
|
msgstr "Munitionsgruppe erfolgreich aktualisiert"
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/form_component.ex:178
|
#: lib/cannery_web/live/ammo_group_live/form_component.ex:185
|
||||||
#, elixir-autogen, elixir-format, fuzzy
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
msgid "Ammo added successfully"
|
msgid "Ammo added successfully"
|
||||||
msgid_plural "Ammo added successfully"
|
msgid_plural "Ammo added successfully"
|
||||||
msgstr[0] "Munitionsgruppe erfolgreich aktualisiert"
|
msgstr[0] "Munitionsgruppe erfolgreich aktualisiert"
|
||||||
msgstr[1] "Munitionsgruppe erfolgreich aktualisiert"
|
msgstr[1] "Munitionsgruppe erfolgreich aktualisiert"
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_type_live/index.html.heex:90
|
#: lib/cannery_web/live/ammo_type_live/index.html.heex:97
|
||||||
#: lib/cannery_web/live/ammo_type_live/show.html.heex:29
|
#: lib/cannery_web/live/ammo_type_live/show.html.heex:29
|
||||||
#, elixir-autogen, elixir-format, fuzzy
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
msgid "Are you sure you want to delete %{name}? This will delete all %{name} type ammo as well!"
|
msgid "Are you sure you want to delete %{name}? This will delete all %{name} type ammo as well!"
|
||||||
msgstr "Sind Sie sicher, dass sie %{name} löschen möchten?"
|
msgstr "Sind Sie sicher, dass sie %{name} löschen möchten?"
|
||||||
|
|
||||||
#: lib/cannery_web/live/home_live.html.heex:65
|
#: lib/cannery_web/live/home_live.html.heex:63
|
||||||
#, elixir-autogen, elixir-format, fuzzy
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
msgid "Register to setup Cannery"
|
msgid "Register to setup Cannery"
|
||||||
msgstr "Registrieren Sie sich, um %{name} zu bearbeiten"
|
msgstr "Registrieren Sie sich, um %{name} zu bearbeiten"
|
||||||
|
|
||||||
#: lib/cannery_web/live/invite_live/index.ex:54
|
#: lib/cannery_web/live/invite_live/index.ex:43
|
||||||
#, elixir-autogen, elixir-format, fuzzy
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
msgid "%{invite_name} deleted succesfully"
|
msgid "%{invite_name} deleted succesfully"
|
||||||
msgstr "%{name} erfolgreich gelöscht"
|
msgstr "%{name} erfolgreich gelöscht"
|
||||||
|
|
||||||
#: lib/cannery_web/live/invite_live/index.ex:115
|
#: lib/cannery_web/live/invite_live/index.ex:104
|
||||||
#, elixir-autogen, elixir-format, fuzzy
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
msgid "%{invite_name} disabled succesfully"
|
msgid "%{invite_name} disabled succesfully"
|
||||||
msgstr "%{name} erfolgreich deaktiviert"
|
msgstr "%{name} erfolgreich deaktiviert"
|
||||||
|
|
||||||
#: lib/cannery_web/live/invite_live/index.ex:91
|
#: lib/cannery_web/live/invite_live/index.ex:80
|
||||||
#, elixir-autogen, elixir-format, fuzzy
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
msgid "%{invite_name} enabled succesfully"
|
msgid "%{invite_name} enabled succesfully"
|
||||||
msgstr "%{name} erfolgreich aktiviert"
|
msgstr "%{name} erfolgreich aktiviert"
|
||||||
|
|
||||||
#: lib/cannery_web/live/invite_live/index.ex:69
|
#: lib/cannery_web/live/invite_live/index.ex:58
|
||||||
#, elixir-autogen, elixir-format, fuzzy
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
msgid "%{invite_name} updated succesfully"
|
msgid "%{invite_name} updated succesfully"
|
||||||
msgstr "%{name} erfolgreich aktualisiert"
|
msgstr "%{name} erfolgreich aktualisiert"
|
||||||
|
|
||||||
#: lib/cannery_web/live/invite_live/index.ex:140
|
#: lib/cannery_web/live/invite_live/index.ex:125
|
||||||
#, elixir-autogen, elixir-format, fuzzy
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
msgid "%{user_email} deleted succesfully"
|
msgid "%{user_email} deleted succesfully"
|
||||||
msgstr "%{name} erfolgreich gelöscht"
|
msgstr "%{name} erfolgreich gelöscht"
|
||||||
|
|
||||||
#: lib/cannery_web/live/invite_live/index.html.heex:48
|
#: lib/cannery_web/live/invite_live/index.html.heex:58
|
||||||
#, elixir-autogen, elixir-format, fuzzy
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
msgid "Are you sure you want to delete the invite for %{invite_name}?"
|
msgid "Are you sure you want to delete the invite for %{invite_name}?"
|
||||||
msgstr "Sind Sie sicher, dass sie die Einladung für %{name} löschen möchten?"
|
msgstr "Sind Sie sicher, dass sie die Einladung für %{name} löschen möchten?"
|
||||||
|
|
||||||
#: lib/cannery_web/live/invite_live/index.html.heex:73
|
#: lib/cannery_web/live/invite_live/index.html.heex:85
|
||||||
#, elixir-autogen, elixir-format, fuzzy
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
msgid "Are you sure you want to make %{invite_name} unlimited?"
|
msgid "Are you sure you want to make %{invite_name} unlimited?"
|
||||||
msgstr "Sind Sie sicher, dass sie %{name} auf unbegrenzt setzen möchten?"
|
msgstr "Sind Sie sicher, dass sie %{name} auf unbegrenzt setzen möchten?"
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -66,11 +66,11 @@ msgstr ""
|
|||||||
msgid "Invite someone new!"
|
msgid "Invite someone new!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/components/topbar.ex:137
|
#: lib/cannery_web/components/core_components/topbar.html.heex:124
|
||||||
#: lib/cannery_web/templates/user_confirmation/new.html.heex:31
|
#: lib/cannery_web/templates/user_confirmation/new.html.heex:32
|
||||||
#: lib/cannery_web/templates/user_registration/new.html.heex:44
|
#: lib/cannery_web/templates/user_registration/new.html.heex:44
|
||||||
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:45
|
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:45
|
||||||
#: lib/cannery_web/templates/user_reset_password/new.html.heex:31
|
#: lib/cannery_web/templates/user_reset_password/new.html.heex:32
|
||||||
#: 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:28
|
#: lib/cannery_web/templates/user_session/new.html.heex:28
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
@ -97,19 +97,19 @@ msgstr ""
|
|||||||
msgid "New Tag"
|
msgid "New Tag"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/components/topbar.ex:129
|
#: lib/cannery_web/components/core_components/topbar.html.heex:116
|
||||||
#: lib/cannery_web/templates/user_confirmation/new.html.heex:28
|
#: lib/cannery_web/templates/user_confirmation/new.html.heex:29
|
||||||
#: lib/cannery_web/templates/user_registration/new.html.heex:3
|
#: lib/cannery_web/templates/user_registration/new.html.heex:3
|
||||||
#: lib/cannery_web/templates/user_registration/new.html.heex:37
|
#: lib/cannery_web/templates/user_registration/new.html.heex:37
|
||||||
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:42
|
#: lib/cannery_web/templates/user_reset_password/edit.html.heex:42
|
||||||
#: lib/cannery_web/templates/user_reset_password/new.html.heex:28
|
#: lib/cannery_web/templates/user_reset_password/new.html.heex:29
|
||||||
#: lib/cannery_web/templates/user_session/new.html.heex:39
|
#: lib/cannery_web/templates/user_session/new.html.heex:39
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Register"
|
msgid "Register"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: 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:15
|
#: lib/cannery_web/templates/user_confirmation/new.html.heex:16
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Resend confirmation instructions"
|
msgid "Resend confirmation instructions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -120,28 +120,28 @@ msgstr ""
|
|||||||
msgid "Reset password"
|
msgid "Reset password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/components/add_shot_group_component.html.heex:54
|
#: lib/cannery_web/components/add_shot_group_component.html.heex:56
|
||||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:82
|
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:84
|
||||||
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:157
|
#: lib/cannery_web/live/ammo_type_live/form_component.html.heex:159
|
||||||
#: lib/cannery_web/live/container_live/form_component.html.heex:51
|
#: lib/cannery_web/live/container_live/form_component.html.heex:55
|
||||||
#: lib/cannery_web/live/invite_live/form_component.html.heex:32
|
#: lib/cannery_web/live/invite_live/form_component.html.heex:32
|
||||||
#: lib/cannery_web/live/range_live/form_component.html.heex:41
|
#: lib/cannery_web/live/range_live/form_component.html.heex:44
|
||||||
#: lib/cannery_web/live/tag_live/form_component.html.heex:37
|
#: lib/cannery_web/live/tag_live/form_component.html.heex:37
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/templates/user_reset_password/new.html.heex:15
|
#: lib/cannery_web/templates/user_reset_password/new.html.heex:16
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Send instructions to reset password"
|
msgid "Send instructions to reset password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/container_live/show.html.heex:76
|
#: lib/cannery_web/live/container_live/show.html.heex:75
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Why not add one?"
|
msgid "Why not add one?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/container_live/edit_tags_component.html.heex:50
|
#: lib/cannery_web/live/container_live/edit_tags_component.html.heex:51
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Add"
|
msgid "Add"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -156,9 +156,9 @@ msgstr ""
|
|||||||
msgid "Why not get some ready to shoot?"
|
msgid "Why not get some ready to shoot?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/index.html.heex:101
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:104
|
||||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:101
|
#: lib/cannery_web/live/ammo_group_live/show.html.heex:103
|
||||||
#: lib/cannery_web/live/range_live/index.html.heex:38
|
#: lib/cannery_web/live/range_live/index.html.heex:45
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Record shots"
|
msgid "Record shots"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -168,17 +168,12 @@ msgstr ""
|
|||||||
msgid "Add another container!"
|
msgid "Add another container!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:94
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Move containers"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/cannery_web/components/move_ammo_group_component.ex:126
|
#: lib/cannery_web/components/move_ammo_group_component.ex:126
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Select"
|
msgid "Select"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/invite_live/index.html.heex:30
|
#: lib/cannery_web/live/invite_live/index.html.heex:38
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Copy to clipboard"
|
msgid "Copy to clipboard"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -188,12 +183,12 @@ msgstr ""
|
|||||||
msgid "add a container first"
|
msgid "add a container first"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:75
|
#: lib/cannery_web/live/ammo_group_live/form_component.html.heex:77
|
||||||
#, elixir-autogen, elixir-format, fuzzy
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
msgid "Create"
|
msgid "Create"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/templates/user_settings/edit.html.heex:111
|
#: lib/cannery_web/templates/user_settings/edit.html.heex:110
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Change Language"
|
msgid "Change Language"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -203,7 +198,7 @@ msgstr ""
|
|||||||
msgid "Change language"
|
msgid "Change language"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:60
|
#: lib/cannery_web/live/ammo_group_live/show.html.heex:55
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "View in Catalog"
|
msgid "View in Catalog"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -214,23 +209,25 @@ msgid "add an ammo type first"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/components/move_ammo_group_component.ex:80
|
#: lib/cannery_web/components/move_ammo_group_component.ex:80
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:121
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/show.html.heex:96
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Move ammo"
|
msgid "Move ammo"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/invite_live/index.html.heex:78
|
#: lib/cannery_web/live/invite_live/index.html.heex:90
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Set Unlimited"
|
msgid "Set Unlimited"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:86
|
#: lib/cannery_web/live/ammo_group_live/show.html.heex:89
|
||||||
#: lib/cannery_web/live/range_live/index.html.heex:31
|
#: lib/cannery_web/live/range_live/index.html.heex:38
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Stage for range"
|
msgid "Stage for range"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/cannery_web/live/ammo_group_live/show.html.heex:85
|
#: lib/cannery_web/live/ammo_group_live/show.html.heex:88
|
||||||
#: lib/cannery_web/live/range_live/index.html.heex:30
|
#: lib/cannery_web/live/range_live/index.html.heex:37
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Unstage from range"
|
msgid "Unstage from range"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -239,3 +236,124 @@ msgstr ""
|
|||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Export Data as JSON"
|
msgid "Export Data as JSON"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_type_live/index.html.heex:85
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Clone %{ammo_type_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:88
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:144
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Clone %{container_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/invite_live/index.html.heex:35
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Copy invite link for %{invite_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_type_live/index.html.heex:104
|
||||||
|
#: lib/cannery_web/live/ammo_type_live/show.html.heex:36
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Delete %{ammo_type_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:103
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:159
|
||||||
|
#: lib/cannery_web/live/container_live/show.html.heex:55
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Delete %{container_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/tag_live/index.html.heex:66
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Delete %{tag_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/invite_live/index.html.heex:63
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Delete invite for %{invite_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/show.ex:161
|
||||||
|
#: lib/cannery_web/live/range_live/index.html.heex:131
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Delete shot record of %{shot_group_count} shots"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_type_live/index.html.heex:75
|
||||||
|
#: lib/cannery_web/live/ammo_type_live/show.html.heex:19
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit %{ammo_type_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:78
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:134
|
||||||
|
#: lib/cannery_web/live/container_live/show.html.heex:42
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit %{container_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/tag_live/index.html.heex:53
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit %{tag_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:143
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/show.html.heex:62
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit ammo group of %{ammo_group_count} bullets"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/invite_live/index.html.heex:46
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit invite for %{invite_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/show.ex:146
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit shot group of %{shot_group_count} shots"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/range_live/index.html.heex:114
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Edit shot record of %{shot_group_count} shots"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:97
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "Stage"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:66
|
||||||
|
#: lib/cannery_web/live/container_live/index.html.heex:123
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Tag %{container_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:96
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Unstage"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_type_live/index.html.heex:65
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "View %{ammo_type_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:155
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "Clone ammo group of %{ammo_group_count} bullets"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:170
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/show.html.heex:76
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "Delete ammo group of %{ammo_group_count} bullets"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/cannery_web/live/ammo_group_live/index.html.heex:131
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "View ammo group of %{ammo_group_count} bullets"
|
||||||
|
msgstr ""
|
||||||
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user