chore: movs display name helper to won helper module
This commit is contained in:
parent
29a953c038
commit
9f97515d74
10 changed files with 234 additions and 84 deletions
64
lib/mv_web/helpers/member_helpers.ex
Normal file
64
lib/mv_web/helpers/member_helpers.ex
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
defmodule MvWeb.Helpers.MemberHelpers do
|
||||||
|
@moduledoc """
|
||||||
|
Helper functions for member-related operations in the web layer.
|
||||||
|
|
||||||
|
Provides utilities for formatting and displaying member information.
|
||||||
|
"""
|
||||||
|
|
||||||
|
alias Mv.Membership.Member
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns a display name for a member.
|
||||||
|
|
||||||
|
Combines first_name and last_name if available, otherwise falls back to email.
|
||||||
|
This ensures that members without names still have a meaningful display name.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> member = %Member{first_name: "John", last_name: "Doe", email: "john@example.com"}
|
||||||
|
iex> MvWeb.Helpers.MemberHelpers.display_name(member)
|
||||||
|
"John Doe"
|
||||||
|
|
||||||
|
iex> member = %Member{first_name: nil, last_name: nil, email: "john@example.com"}
|
||||||
|
iex> MvWeb.Helpers.MemberHelpers.display_name(member)
|
||||||
|
"john@example.com"
|
||||||
|
|
||||||
|
iex> member = %Member{first_name: "John", last_name: nil, email: "john@example.com"}
|
||||||
|
iex> MvWeb.Helpers.MemberHelpers.display_name(member)
|
||||||
|
"John"
|
||||||
|
"""
|
||||||
|
def display_name(%Member{} = member) do
|
||||||
|
name_parts =
|
||||||
|
[member.first_name, member.last_name]
|
||||||
|
|> Enum.reject(&blank?/1)
|
||||||
|
|> Enum.map_join(" ", &String.trim/1)
|
||||||
|
|
||||||
|
if name_parts == "" do
|
||||||
|
member.email
|
||||||
|
else
|
||||||
|
name_parts
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Checks if a value is blank (nil, empty string, or only whitespace).
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> MvWeb.Helpers.MemberHelpers.blank?(nil)
|
||||||
|
true
|
||||||
|
|
||||||
|
iex> MvWeb.Helpers.MemberHelpers.blank?("")
|
||||||
|
true
|
||||||
|
|
||||||
|
iex> MvWeb.Helpers.MemberHelpers.blank?(" ")
|
||||||
|
true
|
||||||
|
|
||||||
|
iex> MvWeb.Helpers.MemberHelpers.blank?("John")
|
||||||
|
false
|
||||||
|
"""
|
||||||
|
def blank?(nil), do: true
|
||||||
|
def blank?(""), do: true
|
||||||
|
def blank?(value) when is_binary(value), do: String.trim(value) == ""
|
||||||
|
def blank?(_), do: false
|
||||||
|
end
|
||||||
|
|
@ -36,7 +36,7 @@ defmodule MvWeb.ContributionPeriodLive.Show do
|
||||||
<.mockup_warning />
|
<.mockup_warning />
|
||||||
|
|
||||||
<.header>
|
<.header>
|
||||||
{gettext("Contributions for %{name}", name: MvWeb.MemberLive.Index.display_name(@member))}
|
{gettext("Contributions for %{name}", name: MvWeb.Helpers.MemberHelpers.display_name(@member))}
|
||||||
<:subtitle>
|
<:subtitle>
|
||||||
{gettext("Contribution type")}:
|
{gettext("Contribution type")}:
|
||||||
<span class="font-semibold">{@member.contribution_type}</span>
|
<span class="font-semibold">{@member.contribution_type}</span>
|
||||||
|
|
|
||||||
|
|
@ -289,6 +289,6 @@ defmodule MvWeb.CustomFieldValueLive.Form do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp member_options(members) do
|
defp member_options(members) do
|
||||||
Enum.map(members, &{MvWeb.MemberLive.Index.display_name(&1), &1.id})
|
Enum.map(members, &{MvWeb.Helpers.MemberHelpers.display_name(&1), &1.id})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ defmodule MvWeb.MemberLive.Form do
|
||||||
|
|
||||||
<h1 class="text-2xl font-bold text-center flex-1">
|
<h1 class="text-2xl font-bold text-center flex-1">
|
||||||
<%= if @member do %>
|
<%= if @member do %>
|
||||||
{MvWeb.MemberLive.Index.display_name(@member)}
|
{MvWeb.Helpers.MemberHelpers.display_name(@member)}
|
||||||
<% else %>
|
<% else %>
|
||||||
{gettext("New Member")}
|
{gettext("New Member")}
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
||||||
|
|
@ -1165,61 +1165,6 @@ defmodule MvWeb.MemberLive.Index do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
|
||||||
Returns a display name for a member.
|
|
||||||
|
|
||||||
Combines first_name and last_name if available, otherwise falls back to email.
|
|
||||||
This ensures that members without names still have a meaningful display name.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> member = %Member{first_name: "John", last_name: "Doe", email: "john@example.com"}
|
|
||||||
iex> display_name(member)
|
|
||||||
"John Doe"
|
|
||||||
|
|
||||||
iex> member = %Member{first_name: nil, last_name: nil, email: "john@example.com"}
|
|
||||||
iex> display_name(member)
|
|
||||||
"john@example.com"
|
|
||||||
|
|
||||||
iex> member = %Member{first_name: "John", last_name: nil, email: "john@example.com"}
|
|
||||||
iex> display_name(member)
|
|
||||||
"John"
|
|
||||||
"""
|
|
||||||
def display_name(member) do
|
|
||||||
name_parts =
|
|
||||||
[member.first_name, member.last_name]
|
|
||||||
|> Enum.reject(&blank?/1)
|
|
||||||
|> Enum.map_join(" ", &String.trim/1)
|
|
||||||
|
|
||||||
if name_parts == "" do
|
|
||||||
member.email
|
|
||||||
else
|
|
||||||
name_parts
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Checks if a value is blank (nil, empty string, or only whitespace).
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> blank?(nil)
|
|
||||||
true
|
|
||||||
|
|
||||||
iex> blank?("")
|
|
||||||
true
|
|
||||||
|
|
||||||
iex> blank?(" ")
|
|
||||||
true
|
|
||||||
|
|
||||||
iex> blank?("John")
|
|
||||||
false
|
|
||||||
"""
|
|
||||||
def blank?(nil), do: true
|
|
||||||
def blank?(""), do: true
|
|
||||||
def blank?(value) when is_binary(value), do: String.trim(value) == ""
|
|
||||||
def blank?(_), do: false
|
|
||||||
|
|
||||||
# Public helper function to format dates for use in templates
|
# Public helper function to format dates for use in templates
|
||||||
def format_date(date), do: DateFormatter.format_date(date)
|
def format_date(date), do: DateFormatter.format_date(date)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ defmodule MvWeb.MemberLive.Show do
|
||||||
</.button>
|
</.button>
|
||||||
|
|
||||||
<h1 class="text-2xl font-bold text-center flex-1">
|
<h1 class="text-2xl font-bold text-center flex-1">
|
||||||
{MvWeb.MemberLive.Index.display_name(@member)}
|
{MvWeb.Helpers.MemberHelpers.display_name(@member)}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<.button variant="primary" navigate={~p"/members/#{@member}/edit?return_to=show"}>
|
<.button variant="primary" navigate={~p"/members/#{@member}/edit?return_to=show"}>
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ defmodule MvWeb.UserLive.Form do
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p class="font-medium text-green-900">
|
<p class="font-medium text-green-900">
|
||||||
{MvWeb.MemberLive.Index.display_name(@user.member)}
|
{MvWeb.Helpers.MemberHelpers.display_name(@user.member)}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm text-green-700">{@user.member.email}</p>
|
<p class="text-sm text-green-700">{@user.member.email}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -210,7 +210,7 @@ defmodule MvWeb.UserLive.Form do
|
||||||
)
|
)
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<p class="font-medium">{MvWeb.MemberLive.Index.display_name(member)}</p>
|
<p class="font-medium">{MvWeb.Helpers.MemberHelpers.display_name(member)}</p>
|
||||||
<p class="text-sm text-base-content/70">{member.email}</p>
|
<p class="text-sm text-base-content/70">{member.email}</p>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
@ -438,7 +438,7 @@ defmodule MvWeb.UserLive.Form do
|
||||||
|
|
||||||
member_name =
|
member_name =
|
||||||
if selected_member,
|
if selected_member,
|
||||||
do: MvWeb.MemberLive.Index.display_name(selected_member),
|
do: MvWeb.Helpers.MemberHelpers.display_name(selected_member),
|
||||||
else: ""
|
else: ""
|
||||||
|
|
||||||
# Store the selected member ID and name in socket state and clear unlink flag
|
# Store the selected member ID and name in socket state and clear unlink flag
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ defmodule MvWeb.UserLive.Show do
|
||||||
class="text-blue-600 underline hover:text-blue-800"
|
class="text-blue-600 underline hover:text-blue-800"
|
||||||
>
|
>
|
||||||
<.icon name="hero-users" class="inline w-4 h-4 mr-1" />
|
<.icon name="hero-users" class="inline w-4 h-4 mr-1" />
|
||||||
{MvWeb.MemberLive.Index.display_name(@user.member)}
|
{MvWeb.Helpers.MemberHelpers.display_name(@user.member)}
|
||||||
</.link>
|
</.link>
|
||||||
<% else %>
|
<% else %>
|
||||||
<span class="italic text-gray-500">{gettext("No member linked")}</span>
|
<span class="italic text-gray-500">{gettext("No member linked")}</span>
|
||||||
|
|
|
||||||
141
test/mv_web/helpers/member_helpers_test.exs
Normal file
141
test/mv_web/helpers/member_helpers_test.exs
Normal file
|
|
@ -0,0 +1,141 @@
|
||||||
|
defmodule MvWeb.Helpers.MemberHelpersTest do
|
||||||
|
@moduledoc """
|
||||||
|
Tests for the display_name/1 helper function in MemberHelpers.
|
||||||
|
"""
|
||||||
|
use Mv.DataCase, async: true
|
||||||
|
|
||||||
|
alias Mv.Membership.Member
|
||||||
|
alias MvWeb.Helpers.MemberHelpers
|
||||||
|
|
||||||
|
describe "display_name/1" do
|
||||||
|
test "returns full name when both first_name and last_name are present" do
|
||||||
|
member = %Member{
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
email: "john@example.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert MemberHelpers.display_name(member) == "John Doe"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns email when both first_name and last_name are nil" do
|
||||||
|
member = %Member{
|
||||||
|
first_name: nil,
|
||||||
|
last_name: nil,
|
||||||
|
email: "john@example.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert MemberHelpers.display_name(member) == "john@example.com"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns first_name only when last_name is nil" do
|
||||||
|
member = %Member{
|
||||||
|
first_name: "John",
|
||||||
|
last_name: nil,
|
||||||
|
email: "john@example.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert MemberHelpers.display_name(member) == "John"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns last_name only when first_name is nil" do
|
||||||
|
member = %Member{
|
||||||
|
first_name: nil,
|
||||||
|
last_name: "Doe",
|
||||||
|
email: "john@example.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert MemberHelpers.display_name(member) == "Doe"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns email when first_name and last_name are empty strings" do
|
||||||
|
member = %Member{
|
||||||
|
first_name: "",
|
||||||
|
last_name: "",
|
||||||
|
email: "john@example.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert MemberHelpers.display_name(member) == "john@example.com"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns email when first_name and last_name are whitespace only" do
|
||||||
|
member = %Member{
|
||||||
|
first_name: " ",
|
||||||
|
last_name: " \t ",
|
||||||
|
email: "john@example.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert MemberHelpers.display_name(member) == "john@example.com"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "trims whitespace from name parts" do
|
||||||
|
member = %Member{
|
||||||
|
first_name: " John ",
|
||||||
|
last_name: " Doe ",
|
||||||
|
email: "john@example.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert MemberHelpers.display_name(member) == "John Doe"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "handles one empty string and one nil" do
|
||||||
|
member = %Member{
|
||||||
|
first_name: "",
|
||||||
|
last_name: nil,
|
||||||
|
email: "john@example.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert MemberHelpers.display_name(member) == "john@example.com"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "handles one nil and one empty string" do
|
||||||
|
member = %Member{
|
||||||
|
first_name: nil,
|
||||||
|
last_name: "",
|
||||||
|
email: "john@example.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert MemberHelpers.display_name(member) == "john@example.com"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "handles one whitespace and one nil" do
|
||||||
|
member = %Member{
|
||||||
|
first_name: " ",
|
||||||
|
last_name: nil,
|
||||||
|
email: "john@example.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert MemberHelpers.display_name(member) == "john@example.com"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "handles one valid name and one whitespace" do
|
||||||
|
member = %Member{
|
||||||
|
first_name: "John",
|
||||||
|
last_name: " ",
|
||||||
|
email: "john@example.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert MemberHelpers.display_name(member) == "John"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "handles member with only first_name containing whitespace" do
|
||||||
|
member = %Member{
|
||||||
|
first_name: " John ",
|
||||||
|
last_name: nil,
|
||||||
|
email: "john@example.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert MemberHelpers.display_name(member) == "John"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "handles member with only last_name containing whitespace" do
|
||||||
|
member = %Member{
|
||||||
|
first_name: nil,
|
||||||
|
last_name: " Doe ",
|
||||||
|
email: "john@example.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert MemberHelpers.display_name(member) == "Doe"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
defmodule MvWeb.MemberLive.Index.DisplayNameTest do
|
defmodule MvWeb.Helpers.MemberHelpersTest do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
Tests for the display_name/1 helper function in MemberLive.Index.
|
Tests for the display_name/1 helper function in MemberHelpers.
|
||||||
"""
|
"""
|
||||||
use Mv.DataCase, async: true
|
use Mv.DataCase, async: true
|
||||||
|
|
||||||
alias Mv.Membership.Member
|
alias Mv.Membership.Member
|
||||||
alias MvWeb.MemberLive.Index
|
alias MvWeb.Helpers.MemberHelpers
|
||||||
|
|
||||||
describe "display_name/1" do
|
describe "display_name/1" do
|
||||||
test "returns full name when both first_name and last_name are present" do
|
test "returns full name when both first_name and last_name are present" do
|
||||||
|
|
@ -15,7 +15,7 @@ defmodule MvWeb.MemberLive.Index.DisplayNameTest do
|
||||||
email: "john@example.com"
|
email: "john@example.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert Index.display_name(member) == "John Doe"
|
assert MemberHelpers.display_name(member) == "John Doe"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns email when both first_name and last_name are nil" do
|
test "returns email when both first_name and last_name are nil" do
|
||||||
|
|
@ -25,7 +25,7 @@ defmodule MvWeb.MemberLive.Index.DisplayNameTest do
|
||||||
email: "john@example.com"
|
email: "john@example.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert Index.display_name(member) == "john@example.com"
|
assert MemberHelpers.display_name(member) == "john@example.com"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns first_name only when last_name is nil" do
|
test "returns first_name only when last_name is nil" do
|
||||||
|
|
@ -35,7 +35,7 @@ defmodule MvWeb.MemberLive.Index.DisplayNameTest do
|
||||||
email: "john@example.com"
|
email: "john@example.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert Index.display_name(member) == "John"
|
assert MemberHelpers.display_name(member) == "John"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns last_name only when first_name is nil" do
|
test "returns last_name only when first_name is nil" do
|
||||||
|
|
@ -45,7 +45,7 @@ defmodule MvWeb.MemberLive.Index.DisplayNameTest do
|
||||||
email: "john@example.com"
|
email: "john@example.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert Index.display_name(member) == "Doe"
|
assert MemberHelpers.display_name(member) == "Doe"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns email when first_name and last_name are empty strings" do
|
test "returns email when first_name and last_name are empty strings" do
|
||||||
|
|
@ -55,7 +55,7 @@ defmodule MvWeb.MemberLive.Index.DisplayNameTest do
|
||||||
email: "john@example.com"
|
email: "john@example.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert Index.display_name(member) == "john@example.com"
|
assert MemberHelpers.display_name(member) == "john@example.com"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns email when first_name and last_name are whitespace only" do
|
test "returns email when first_name and last_name are whitespace only" do
|
||||||
|
|
@ -65,7 +65,7 @@ defmodule MvWeb.MemberLive.Index.DisplayNameTest do
|
||||||
email: "john@example.com"
|
email: "john@example.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert Index.display_name(member) == "john@example.com"
|
assert MemberHelpers.display_name(member) == "john@example.com"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "trims whitespace from name parts" do
|
test "trims whitespace from name parts" do
|
||||||
|
|
@ -75,7 +75,7 @@ defmodule MvWeb.MemberLive.Index.DisplayNameTest do
|
||||||
email: "john@example.com"
|
email: "john@example.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert Index.display_name(member) == "John Doe"
|
assert MemberHelpers.display_name(member) == "John Doe"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "handles one empty string and one nil" do
|
test "handles one empty string and one nil" do
|
||||||
|
|
@ -85,7 +85,7 @@ defmodule MvWeb.MemberLive.Index.DisplayNameTest do
|
||||||
email: "john@example.com"
|
email: "john@example.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert Index.display_name(member) == "john@example.com"
|
assert MemberHelpers.display_name(member) == "john@example.com"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "handles one nil and one empty string" do
|
test "handles one nil and one empty string" do
|
||||||
|
|
@ -95,7 +95,7 @@ defmodule MvWeb.MemberLive.Index.DisplayNameTest do
|
||||||
email: "john@example.com"
|
email: "john@example.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert Index.display_name(member) == "john@example.com"
|
assert MemberHelpers.display_name(member) == "john@example.com"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "handles one whitespace and one nil" do
|
test "handles one whitespace and one nil" do
|
||||||
|
|
@ -105,7 +105,7 @@ defmodule MvWeb.MemberLive.Index.DisplayNameTest do
|
||||||
email: "john@example.com"
|
email: "john@example.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert Index.display_name(member) == "john@example.com"
|
assert MemberHelpers.display_name(member) == "john@example.com"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "handles one valid name and one whitespace" do
|
test "handles one valid name and one whitespace" do
|
||||||
|
|
@ -115,7 +115,7 @@ defmodule MvWeb.MemberLive.Index.DisplayNameTest do
|
||||||
email: "john@example.com"
|
email: "john@example.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert Index.display_name(member) == "John"
|
assert MemberHelpers.display_name(member) == "John"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "handles member with only first_name containing whitespace" do
|
test "handles member with only first_name containing whitespace" do
|
||||||
|
|
@ -125,7 +125,7 @@ defmodule MvWeb.MemberLive.Index.DisplayNameTest do
|
||||||
email: "john@example.com"
|
email: "john@example.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert Index.display_name(member) == "John"
|
assert MemberHelpers.display_name(member) == "John"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "handles member with only last_name containing whitespace" do
|
test "handles member with only last_name containing whitespace" do
|
||||||
|
|
@ -135,7 +135,7 @@ defmodule MvWeb.MemberLive.Index.DisplayNameTest do
|
||||||
email: "john@example.com"
|
email: "john@example.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert Index.display_name(member) == "Doe"
|
assert MemberHelpers.display_name(member) == "Doe"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue